Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 16 Oct 2013 17:02:56 -0400
changeset 165845 1496fdb1d4b5f7141e37045e0b85f6dee11bbce5
parent 165844 af90be9858824e59ea1dc874dfe5a51e1b420213 (current diff)
parent 165796 66af8b3096a61f6eb95059817eb0de629d0c187b (diff)
child 165846 07606a1ebf5dd5f2468c50067966c224ab0af96b
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound.
toolkit/mozapps/update/test/chrome/test_0027_check_staging_billboard.xul
toolkit/mozapps/update/test/chrome/test_0037_available_staging_basic.xul
toolkit/mozapps/update/test/chrome/test_0047_available_staging_billboard.xul
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -797,20 +797,17 @@ pref("dom.promise.enabled", false);
 // Enable dataStore
 #ifdef RELEASE_BUILD
 pref("dom.datastore.enabled", false);
 #else
 pref("dom.datastore.enabled", true);
 #endif
 
 // DOM Inter-App Communication API.
-#ifdef MOZ_WIDGET_GONK
-// Enable this only for gonk-specific build but not for desktop build.
 pref("dom.inter-app-communication-api.enabled", true);
-#endif
 
 // Allow ADB to run for this many hours before disabling
 // (only applies when marionette is disabled)
 // 0 disables the timer.
 pref("b2g.adb.timeout-hours", 12);
 
 // InputMethod so we can do soft keyboards
 pref("dom.mozInputMethod.enabled", true);
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "36aa3e5d226a844b07b3e4b2219f54b549456ec1", 
+    "revision": "86e06b1db110e34eb66826d3b1bdee3a5d57b3a7", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -302,16 +302,19 @@ let SessionStoreInternal = {
 
   // time in milliseconds when the session was started (saved across sessions),
   // defaults to now if no session was restored or timestamp doesn't exist
   _sessionStartTime: Date.now(),
 
   // states for all currently opened windows
   _windows: {},
 
+  // counter for creating unique window IDs
+  _nextWindowID: 0,
+
   // states for all recently closed windows
   _closedWindows: [],
 
   // collection of session states yet to be restored
   _statesToRestore: {},
 
   // counts the number of crashes since the last clean start
   _recentCrashes: 0,
@@ -678,16 +681,25 @@ let SessionStoreInternal = {
         TabStateCache.updateField(aEvent.originalTarget, "pinned", false);
         this.saveStateDelayed(win);
         break;
     }
     this._clearRestoringWindows();
   },
 
   /**
+   * Generate a unique window identifier
+   * @return string
+   *         A unique string to identify a window
+   */
+  _generateWindowID: function ssi_generateWindowID() {
+    return "window" + (this._nextWindowID++);
+  },
+
+  /**
    * If it's the first window load since app start...
    * - determine if we're reloading after a crash or a forced-restart
    * - restore window state
    * - restart downloads
    * Set up event listeners for this window's tabs
    * @param aWindow
    *        Window reference
    * @param aInitialState
@@ -698,18 +710,19 @@ let SessionStoreInternal = {
     if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi])
       return;
 
     // ignore non-browser windows and windows opened while shutting down
     if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" ||
         this._loadState == STATE_QUITTING)
       return;
 
-    // assign it a unique identifier (timestamp)
-    aWindow.__SSi = "window" + Date.now();
+    // Assign the window a unique identifier we can use to reference
+    // internal data about the window.
+    aWindow.__SSi = this._generateWindowID();
 
     // and create its data object
     this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };
 
     let isPrivateWindow = false;
     if (PrivateBrowsingUtils.isWindowPrivate(aWindow))
       this._windows[aWindow.__SSi].isPrivate = isPrivateWindow = true;
     if (!this._isWindowLoaded(aWindow))
@@ -875,18 +888,20 @@ let SessionStoreInternal = {
    * - save all window data
    * @param aWindow
    *        Window reference
    */
   onClose: function ssi_onClose(aWindow) {
     // this window was about to be restored - conserve its original data, if any
     let isFullyLoaded = this._isWindowLoaded(aWindow);
     if (!isFullyLoaded) {
-      if (!aWindow.__SSi)
-        aWindow.__SSi = "window" + Date.now();
+      if (!aWindow.__SSi) {
+        aWindow.__SSi = this._generateWindowID();
+      }
+
       this._windows[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID];
       delete this._statesToRestore[aWindow.__SS_restoreID];
       delete aWindow.__SS_restoreID;
     }
 
     // ignore windows not tracked by SessionStore
     if (!aWindow.__SSi || !this._windows[aWindow.__SSi]) {
       return;
--- a/browser/metro/base/content/input.js
+++ b/browser/metro/base/content/input.js
@@ -1032,71 +1032,47 @@ var GestureModule = {
 };
 
 /**
  * Helper to track when the user is using a precise pointing device (pen/mouse)
  * versus an imprecise one (touch).
  */
 var InputSourceHelper = {
   isPrecise: false,
-  touchIsActive: false,
 
   init: function ish_init() {
-    window.addEventListener("mousemove", this, true);
-    window.addEventListener("mousedown", this, true);
-    window.addEventListener("touchstart", this, true);
-    window.addEventListener("touchend", this, true);
-    window.addEventListener("touchcancel", this, true);
+    Services.obs.addObserver(this, "metro_precise_input", false);
+    Services.obs.addObserver(this, "metro_imprecise_input", false);
   },
 
   _precise: function () {
     if (!this.isPrecise) {
       this.isPrecise = true;
       this._fire("MozPrecisePointer");
     }
   },
 
   _imprecise: function () {
     if (this.isPrecise) {
       this.isPrecise = false;
       this._fire("MozImprecisePointer");
     }
   },
 
-  handleEvent: function ish_handleEvent(aEvent) {
-    switch(aEvent.type) {
-      case "touchstart":
-        this._imprecise();
-        this.touchIsActive = true;
-        break;
-      case "touchend":
-      case "touchcancel":
-        this.touchIsActive = false;
+  observe: function BrowserUI_observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "metro_precise_input":
+        this._precise();
         break;
-      default:
-        // Ignore mouse movement when touch is active. Prevents both mouse scrollbars
-        // and touch scrollbars from displaying at the same time. Also works around
-        // odd win8 bug involving an erant mousemove event after a touch sequence
-        // starts (bug 896017).
-        if (this.touchIsActive) {
-          return;
-        }
-
-        switch (aEvent.mozInputSource) {
-          case Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE:
-          case Ci.nsIDOMMouseEvent.MOZ_SOURCE_PEN:
-          case Ci.nsIDOMMouseEvent.MOZ_SOURCE_ERASER:
-          case Ci.nsIDOMMouseEvent.MOZ_SOURCE_CURSOR:
-            this._precise();
-            break;
-        }
+      case "metro_imprecise_input":
+        this._imprecise();
         break;
     }
   },
-  
+
   fireUpdate: function fireUpdate() {
     if (this.isPrecise) {
       this._fire("MozPrecisePointer");
     } else {
       this._fire("MozImprecisePointer");
     }
   },
 
--- a/browser/metro/base/tests/mochitest/browser_inputsource.js
+++ b/browser/metro/base/tests/mochitest/browser_inputsource.js
@@ -27,49 +27,33 @@ function testState(aState) {
     ok(!StyleSheetSvc.sheetRegistered(uri, Ci.nsIStyleSheetService.AGENT_SHEET), "cursor stylesheet registered");
   } else {
     ok(!InputSourceHelper.isPrecise, "InputSourceHelper");
     let uri = Util.makeURI("chrome://browser/content/cursor.css");
     ok(StyleSheetSvc.sheetRegistered(uri, Ci.nsIStyleSheetService.AGENT_SHEET), "cursor stylesheet registered");
   }
 }
 
-function sendMouseMoves() {
-  let utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                      .getInterface(Components.interfaces.nsIDOMWindowUtils);
-  for (let deg = 0; deg < 180; deg++) {
-    let coord = Math.sin((deg * Math.PI)/180) * 750;
-    utils.sendMouseEventToWindow("mousemove", coord, coord, 2, 1, 0, true,
-                                  1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE);
-  }
+function notifyPrecise()
+{
+  Services.obs.notifyObservers(null, "metro_precise_input", null);
 }
 
-function sendTouchStart() {
-  EventUtils.synthesizeTouchAtPoint(100, 100, { type: "touchstart" }, window);
-}
-
-function sendTouchMove() {
-  EventUtils.synthesizeTouchAtPoint(100, 100, { type: "touchmove" }, window);
-}
-
-function sendTouchEnd() {
-  EventUtils.synthesizeTouchAtPoint(100, 100, { type: "touchend" }, window);
+function notifyImprecise()
+{
+  Services.obs.notifyObservers(null, "metro_imprecise_input", null);
 }
 
 gTests.push({
   desc: "precise/imprecise input switcher",
   setUp: setUp,
   run: function () {
-    sendMouseMoves();
+    notifyPrecise();
     testState("precise");
-    sendTouchStart();
-    testState("imprecise");
-    sendMouseMoves();
+    notifyImprecise();
     testState("imprecise");
-    sendTouchMove();
+    notifyPrecise();
+    testState("precise");
+    notifyImprecise();
     testState("imprecise");
-    sendTouchEnd();
-    testState("imprecise");
-    sendMouseMoves();
-    testState("precise");
   }
 });
 
--- a/browser/themes/linux/devtools/debugger.css
+++ b/browser/themes/linux/devtools/debugger.css
@@ -61,18 +61,24 @@
 #sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents {
   color: #888;
 }
 
 #sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents > .dbg-breakpoint {
   display: none;
 }
 
-#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow {
+#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
   background-image: none;
+  box-shadow: inset -1px 0 0 #222426;
+}
+
+#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
+  background-image: none;
+  box-shadow: inset 1px 0 0 #222426;
 }
 
 /* Black box message and source progress meter */
 
 #black-boxed-message,
 #source-progress-container {
   background: url(background-noise-toolbar.png) rgb(61,69,76);
   /* Prevent the container deck from aquiring the height from this message. */
--- a/browser/themes/osx/devtools/debugger.css
+++ b/browser/themes/osx/devtools/debugger.css
@@ -59,18 +59,24 @@
 #sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents {
   color: #888;
 }
 
 #sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents > .dbg-breakpoint {
   display: none;
 }
 
-#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow {
+#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
   background-image: none;
+  box-shadow: inset -1px 0 0 #222426;
+}
+
+#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
+  background-image: none;
+  box-shadow: inset 1px 0 0 #222426;
 }
 
 /* Black box message and source progress meter */
 
 #black-boxed-message,
 #source-progress-container {
   background: url(background-noise-toolbar.png) rgb(61,69,76);
   /* Prevent the container deck from aquiring the height from this message. */
--- a/browser/themes/windows/devtools/debugger.css
+++ b/browser/themes/windows/devtools/debugger.css
@@ -59,18 +59,24 @@
 #sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents {
   color: #888;
 }
 
 #sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents > .dbg-breakpoint {
   display: none;
 }
 
-#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow {
+#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
   background-image: none;
+  box-shadow: inset -1px 0 0 #222426;
+}
+
+#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
+  background-image: none;
+  box-shadow: inset 1px 0 0 #222426;
 }
 
 /* Black box message and source progress meter */
 
 #black-boxed-message,
 #source-progress-container {
   background: url(background-noise-toolbar.png) rgb(61,69,76);
   /* Prevent the container deck from aquiring the height from this message. */
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -61,17 +61,19 @@ nsDOMCameraManager::nsDOMCameraManager(n
   SetIsDOMBinding();
 }
 
 nsDOMCameraManager::~nsDOMCameraManager()
 {
   /* destructor code */
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  obs->RemoveObserver(this, "xpcom-shutdown");
+  if (obs) {
+    obs->RemoveObserver(this, "xpcom-shutdown");
+  }
 }
 
 bool
 nsDOMCameraManager::CheckPermission(nsPIDOMWindow* aWindow)
 {
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(permMgr, false);
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -1057,16 +1057,33 @@ ContactDB.prototype = {
           let substringRequest = index.mozGetAll(substring, limit);
 
           substringRequest.onsuccess = function (event) {
             if (DEBUG) debug("Request successful. Record count: " + event.target.result.length);
             for (let i in event.target.result) {
               substringResult[event.target.result[i].id] = event.target.result[i];
             }
           }.bind(this);
+        } else if (normalized[0] !== "+") {
+          // We might have an international prefix like '00'
+          let parsed = PhoneNumberUtils.parse(normalized);
+          if (parsed && parsed.internationalNumber &&
+              parsed.nationalNumber  &&
+              parsed.nationalNumber !== normalized &&
+              parsed.internationalNumber !== normalized) {
+            if (DEBUG) debug("Search with " + parsed.internationalNumber);
+            let prefixRequest = index.mozGetAll(parsed.internationalNumber, limit);
+
+            prefixRequest.onsuccess = function (event) {
+              if (DEBUG) debug("Request successful. Record count: " + event.target.result.length);
+              for (let i in event.target.result) {
+                substringResult[event.target.result[i].id] = event.target.result[i];
+              }
+            }.bind(this);
+          }
         }
 
         request = index.mozGetAll(normalized, limit);
       } else {
         // XXX: "contains" should be handled separately, this is "startsWith"
         if (options.filterOp === 'contains' && key !== 'tel') {
           dump("ContactDB: 'contains' only works for 'tel'. Falling back " +
                "to 'startsWith'.\n");
--- a/dom/contacts/tests/test_contacts_international.html
+++ b/dom/contacts/tests/test_contacts_international.html
@@ -56,16 +56,26 @@ var properties1 = {
 };
 
 var shortNumber = "888";
 var properties2 = {
   name: "Testname2",
   tel: [{type: ["work"], value: shortNumber, carrier: "testCarrier"}]
 };
 
+var number3 = {
+  international1: "0041557932012345",
+  international2: "+557932012345"
+};
+
+var properties3 = {
+  name: "Testname2",
+  tel: [{value: number3.international2}]
+};
+
 var req;
 var index = 0;
 var createResult1;
 var findResult1;
 var sample_id1;
 
 var mozContacts = window.navigator.mozContacts;
 
@@ -233,16 +243,51 @@ var steps = [
     req = mozContacts.clear();
     req.onsuccess = function () {
       ok(true, "Deleted the database");
       next();
     }
     req.onerror = onFailure;
   },
   function () {
+    ok(true, "Adding a new contact with country code");
+    createResult1 = new mozContact();
+    createResult1.init(properties3);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching for international number with prefix");
+    var options = {filterBy: ["tel"],
+                   filterOp: "match",
+                   filterValue: number3.international1};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      ok(req.result.length == 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear();
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
     ok(true, "all done!\n");
     SimpleTest.finish();
   }
 ];
 
 function next() {
   ok(true, "Begin!");
   if (index >= steps.length) {
--- a/dom/datastore/tests/file_arrays.html
+++ b/dom/datastore/tests/file_arrays.html
@@ -40,42 +40,44 @@
       ok("clear" in store, "store.clear exists");
 
       gStore = stores[0];
 
       runTest();
     }, cbError);
   }
 
+  var itemNumber = 60;
+
   function testStoreAdd() {
     var objects = [];
-    for (var i = 0; i < 300; ++i) {
+    for (var i = 0; i < itemNumber; ++i) {
       objects.push(i);
     }
 
     function testStoreAddInternal() {
       if (!objects.length) {
-        ok(true, "We inserted 300 items");
+        ok(true, "We inserted " + itemNumber + " items");
         runTest();
         return;
       }
 
       var obj = objects.shift();
       gStore.add(obj).then(function() {
         ok(true, "We inserted a new item!");
         testStoreAddInternal();
       }, cbError);
     }
 
     testStoreAddInternal();
   }
 
   function testStoreGet() {
     var objects = [];
-    for (var i = 1; i <= 300; ++i) {
+    for (var i = 1; i <= itemNumber; ++i) {
       objects.push(i);
     }
 
     gStore.get(objects).then(function(data) {
        is(data.length, objects.length, "Get - Data matches");
        for (var i = 0; i < data.length; ++i) {
          is(data[i], objects[i] - 1, "Get - Data matches: " + i + " " + data[i] + " == " + objects[i]);
        }
--- a/dom/messages/SystemMessageManager.js
+++ b/dom/messages/SystemMessageManager.js
@@ -131,16 +131,17 @@ SystemMessageManager.prototype = {
       delete dispatchers[aType];
       return;
     }
 
     // Last registered handler wins.
     dispatchers[aType] = { handler: aHandler, messages: [], isHandling: false };
 
     // Ask for the list of currently pending messages.
+    this.addMessageListeners("SystemMessageManager:GetPendingMessages:Return");
     cpmm.sendAsyncMessage("SystemMessageManager:GetPendingMessages",
                           { type: aType,
                             uri: this._uri,
                             manifest: this._manifest });
   },
 
   mozHasPendingMessage: function sysMessMgr_hasPendingMessage(aType) {
     debug("asking pending message for [" + aType + "]");
@@ -209,16 +210,18 @@ SystemMessageManager.prototype = {
     if (aMessage.name == "SystemMessageManager:Message") {
       // Send an acknowledgement to parent to clean up the pending message,
       // so a re-launched app won't handle it again, which is redundant.
       cpmm.sendAsyncMessage("SystemMessageManager:Message:Return:OK",
                             { type: msg.type,
                               manifest: this._manifest,
                               uri: this._uri,
                               msgID: msg.msgID });
+    } else if (aMessage.name == "SystemMessageManager:GetPendingMessages:Return") {
+      this.removeMessageListeners(aMessage.name);
     }
 
     let messages = (aMessage.name == "SystemMessageManager:Message")
                    ? [msg.msg]
                    : msg.msgQueue;
 
     // We only dispatch messages when a handler is registered.
     let dispatcher = this._dispatchers[msg.type];
@@ -243,18 +246,17 @@ SystemMessageManager.prototype = {
                                    "handle-system-messages-done",
                                    /* aData */ null);
     }
   },
 
   // nsIDOMGlobalPropertyInitializer implementation.
   init: function sysMessMgr_init(aWindow) {
     debug("init");
-    this.initDOMRequestHelper(aWindow, ["SystemMessageManager:Message",
-                              "SystemMessageManager:GetPendingMessages:Return"]);
+    this.initDOMRequestHelper(aWindow, ["SystemMessageManager:Message"]);
 
     let principal = aWindow.document.nodePrincipal;
     this._isInBrowserElement = principal.isInBrowserElement;
     this._uri = principal.URI.spec;
 
     let appsService = Cc["@mozilla.org/AppsService;1"]
                         .getService(Ci.nsIAppsService);
     this._manifest = appsService.getManifestURLByLocalId(principal.appId);
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -1147,38 +1147,42 @@ this.STK_SUPPORTED_TERMINAL_PROFILE = [
  *
  * @see 3GPP TS 51.011 10.3.7 (SIM) and 3GPP TS 31.102 4.2.8 (USIM).
  */
 this.GECKO_ICC_SERVICES = {
   sim: {
     ADN: 2,
     FDN: 3,
     PLMNSEL: 7,
+    MSISDN: 9,
     CBMI: 14,
     SPN: 17,
     SDN: 18,
     DATA_DOWNLOAD_SMS_CB: 25,
     DATA_DOWNLOAD_SMS_PP: 26,
     CBMIR: 30,
     BDN: 31,
     PNN: 51,
     OPL: 52,
+    MDN: 53,
     SPDI: 56
   },
   usim: {
     FDN: 2,
     SDN: 4,
     BDN: 6,
     CBMI: 15,
     CBMIR: 16,
     SPN: 19,
+    MSISDN: 21,
     DATA_DOWNLOAD_SMS_PP: 28,
     DATA_DOWNLOAD_SMS_CB: 29,
     PNN: 45,
     OPL: 46,
+    MDN: 47,
     SPDI: 51
   },
   ruim: {
     ENHANCED_PHONEBOOK: 6,
     SPN: 17
   }
 };
 
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -10990,20 +10990,18 @@ ICCIOHelper[ICC_COMMAND_UPDATE_RECORD] =
  */
 let ICCRecordHelper = {
   /**
    * Fetch ICC records.
    */
   fetchICCRecords: function fetchICCRecords() {
     this.readICCID();
     RIL.getIMSI();
-    this.readMSISDN();
     this.readAD();
     this.readSST();
-    this.readMBDN();
   },
 
   /**
    * Read EF_phase.
    * This EF is only available in SIM.
    */
   readICCPhase: function readICCPhase() {
     function callback() {
@@ -11138,24 +11136,38 @@ let ICCRecordHelper = {
       if (DEBUG) {
         let str = "";
         for (let i = 0; i < sst.length; i++) {
           str += sst[i] + ", ";
         }
         debug("SST: " + str);
       }
 
+      if (ICCUtilsHelper.isICCServiceAvailable("MSISDN")) {
+        if (DEBUG) debug("MSISDN: MSISDN is available");
+        this.readMSISDN();
+      } else {
+        if (DEBUG) debug("MSISDN: MSISDN service is not available");
+      }
+
       // Fetch SPN and PLMN list, if some of them are available.
       if (ICCUtilsHelper.isICCServiceAvailable("SPN")) {
         if (DEBUG) debug("SPN: SPN is available");
         this.readSPN();
       } else {
         if (DEBUG) debug("SPN: SPN service is not available");
       }
 
+      if (ICCUtilsHelper.isICCServiceAvailable("MDN")) {
+        if (DEBUG) debug("MDN: MDN available.");
+        this.readMBDN();
+      } else {
+        if (DEBUG) debug("MDN: MDN service is not available");
+      }
+
       if (ICCUtilsHelper.isICCServiceAvailable("SPDI")) {
         if (DEBUG) debug("SPDI: SPDI available.");
         this.readSPDI();
       } else {
         if (DEBUG) debug("SPDI: SPDI service is not available");
       }
 
       if (ICCUtilsHelper.isICCServiceAvailable("PNN")) {
--- a/dom/system/gonk/tests/test_ril_worker_icc.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc.js
@@ -1890,8 +1890,91 @@ add_test(function test_reading_ad_and_pa
 
   do_test(undefined, "466923202422409", "466", "92" );
   do_test(0x03,      "466923202422409", "466", "923");
   do_test(undefined, "310260542718417", "310", "260");
   do_test(0x02,      "310260542718417", "310", "26" );
 
   run_next_test();
 });
+
+add_test(function test_reading_optional_efs() {
+  let worker = newUint8Worker();
+  let record = worker.ICCRecordHelper;
+  let gsmPdu = worker.GsmPDUHelper;
+  let ril    = worker.RIL;
+  let buf    = worker.Buf;
+  let io     = worker.ICCIOHelper;
+
+  function buildSST(supportedEf) {
+    let sst = [];
+    let len = supportedEf.length;
+    for (let i = 0; i < len; i++) {
+      let index, bitmask, iccService;
+      if (ril.appType === CARD_APPTYPE_SIM) {
+        iccService = GECKO_ICC_SERVICES.sim[supportedEf[i]];
+        iccService -= 1;
+        index = Math.floor(iccService / 4);
+        bitmask = 2 << ((iccService % 4) << 1);
+      } else if (ril.appType === CARD_APPTYPE_USIM){
+        iccService = GECKO_ICC_SERVICES.usim[supportedEf[i]];
+        iccService -= 1;
+        index = Math.floor(iccService / 8);
+        bitmask = 1 << ((iccService % 8) << 0);
+      }
+
+      if (sst) {
+        sst[index] |= bitmask;
+      }
+    }
+    return sst;
+  }
+
+  ril.updateCellBroadcastConfig = function fakeUpdateCellBroadcastConfig() {
+    // Ignore updateCellBroadcastConfig after reading SST
+  };
+
+  function do_test(sst, supportedEf) {
+    // Clone supportedEf to local array for testing
+    let testEf = supportedEf.slice(0);
+
+    record.readMSISDN = function fakeReadMSISDN() {
+      testEf.splice(testEf.indexOf("MSISDN"), 1);
+    };
+
+    record.readMBDN = function fakeReadMBDN() {
+      testEf.splice(testEf.indexOf("MDN"), 1);
+    };
+
+    io.loadTransparentEF = function fakeLoadTransparentEF(options) {
+      // Write data size
+      buf.writeInt32(sst.length * 2);
+
+      // Write data
+      for (let i = 0; i < sst.length; i++) {
+         gsmPdu.writeHexOctet(sst[i] || 0);
+      }
+
+      // Write string delimiter
+      buf.writeStringDelimiter(sst.length * 2);
+
+      if (options.callback) {
+        options.callback(options);
+      }
+
+      if (testEf.length !== 0) {
+        do_print("Un-handled EF: " + JSON.stringify(testEf));
+        do_check_true(false);
+      }
+    };
+
+    record.readSST();
+  }
+
+  // TODO: Add all necessary optional EFs eventually
+  let supportedEf = ["MSISDN", "MDN"];
+  ril.appType = CARD_APPTYPE_SIM;
+  do_test(buildSST(supportedEf), supportedEf);
+  ril.appType = CARD_APPTYPE_USIM;
+  do_test(buildSST(supportedEf), supportedEf);
+
+  run_next_test();
+});
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -193,17 +193,23 @@ ContainerRender(ContainerT* aContainer,
     }
 
     nsIntRect clipRect = layerToRender->GetLayer()->
         CalculateScissorRect(aClipRect, &aManager->GetWorldTransform());
     if (clipRect.IsEmpty()) {
       continue;
     }
 
-    layerToRender->RenderLayer(childOffset, clipRect);
+    if (layerToRender->HasLayerBeenComposited()) {
+      // Composer2D will compose this layer so skip GPU composition
+      // this time & reset composition flag for next composition phase
+      layerToRender->SetLayerComposited(false);
+    } else {
+      layerToRender->RenderLayer(childOffset, clipRect);
+    }
     // invariant: our GL context should be current here, I don't think we can
     // assert it though
   }
 
   if (needsSurface) {
     // Unbind the current surface and rebind the previous one.
 #ifdef MOZ_DUMP_PAINTING
     if (gfxUtils::sDumpPainting) {
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -721,16 +721,17 @@ LayerManagerComposite::CreateDrawTarget(
 
 LayerComposite::LayerComposite(LayerManagerComposite *aManager)
   : mCompositeManager(aManager)
   , mCompositor(aManager->GetCompositor())
   , mShadowOpacity(1.0)
   , mUseShadowClipRect(false)
   , mShadowTransformSetByAnimation(false)
   , mDestroyed(false)
+  , mLayerComposited(false)
 { }
 
 LayerComposite::~LayerComposite()
 {
 }
 
 void
 LayerComposite::Destroy()
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -376,32 +376,39 @@ public:
   {
     mShadowTransform = aMatrix;
   }
   void SetShadowTransformSetByAnimation(bool aSetByAnimation)
   {
     mShadowTransformSetByAnimation = aSetByAnimation;
   }
 
+  void SetLayerComposited(bool value)
+  {
+    mLayerComposited = value;
+  }
+
   // These getters can be used anytime.
   float GetShadowOpacity() { return mShadowOpacity; }
   const nsIntRect* GetShadowClipRect() { return mUseShadowClipRect ? &mShadowClipRect : nullptr; }
   const nsIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
   const gfx3DMatrix& GetShadowTransform() { return mShadowTransform; }
   bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
+  bool HasLayerBeenComposited() { return mLayerComposited; }
 
 protected:
   gfx3DMatrix mShadowTransform;
   nsIntRegion mShadowVisibleRegion;
   nsIntRect mShadowClipRect;
   LayerManagerComposite* mCompositeManager;
   RefPtr<Compositor> mCompositor;
   float mShadowOpacity;
   bool mUseShadowClipRect;
   bool mShadowTransformSetByAnimation;
   bool mDestroyed;
+  bool mLayerComposited;
 };
 
 
 } /* layers */
 } /* mozilla */
 
 #endif /* GFX_LayerManagerComposite_H */
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -630,16 +630,17 @@ RES_DRAWABLE_MDPI = \
   res/drawable-mdpi/ic_menu_new_tab.png \
   res/drawable-mdpi/ic_menu_reload.png \
   res/drawable-mdpi/ic_status_logo.png \
   res/drawable-mdpi/ic_url_bar_go.png \
   res/drawable-mdpi/ic_url_bar_reader.png \
   res/drawable-mdpi/ic_url_bar_search.png \
   res/drawable-mdpi/ic_url_bar_star.png \
   res/drawable-mdpi/ic_url_bar_tab.png \
+  res/drawable-mdpi/icon_bookmarks_empty.png \
   res/drawable-mdpi/icon_last_tabs.png \
   res/drawable-mdpi/icon_last_tabs_empty.png \
   res/drawable-mdpi/icon_most_recent.png \
   res/drawable-mdpi/icon_most_recent_empty.png \
   res/drawable-mdpi/icon_most_visited.png \
   res/drawable-mdpi/icon_openinapp.png \
   res/drawable-mdpi/icon_pageaction.png \
   res/drawable-mdpi/icon_reading_list_empty.png \
@@ -747,16 +748,17 @@ RES_DRAWABLE_HDPI = \
   res/drawable-hdpi/ic_menu_new_tab.png \
   res/drawable-hdpi/ic_menu_reload.png \
   res/drawable-hdpi/ic_status_logo.png \
   res/drawable-hdpi/ic_url_bar_go.png \
   res/drawable-hdpi/ic_url_bar_reader.png \
   res/drawable-hdpi/ic_url_bar_search.png \
   res/drawable-hdpi/ic_url_bar_star.png \
   res/drawable-hdpi/ic_url_bar_tab.png \
+  res/drawable-hdpi/icon_bookmarks_empty.png \
   res/drawable-hdpi/icon_last_tabs.png \
   res/drawable-hdpi/icon_last_tabs_empty.png \
   res/drawable-hdpi/icon_most_recent.png \
   res/drawable-hdpi/icon_most_recent_empty.png \
   res/drawable-hdpi/icon_most_visited.png \
   res/drawable-hdpi/icon_openinapp.png \
   res/drawable-hdpi/icon_pageaction.png \
   res/drawable-hdpi/icon_reading_list_empty.png \
@@ -850,16 +852,17 @@ RES_DRAWABLE_XHDPI = \
   res/drawable-xhdpi/ic_menu_new_tab.png \
   res/drawable-xhdpi/ic_menu_reload.png \
   res/drawable-xhdpi/ic_status_logo.png \
   res/drawable-xhdpi/ic_url_bar_go.png \
   res/drawable-xhdpi/ic_url_bar_reader.png \
   res/drawable-xhdpi/ic_url_bar_search.png \
   res/drawable-xhdpi/ic_url_bar_star.png \
   res/drawable-xhdpi/ic_url_bar_tab.png \
+  res/drawable-xhdpi/icon_bookmarks_empty.png \
   res/drawable-xhdpi/icon_last_tabs.png \
   res/drawable-xhdpi/icon_last_tabs_empty.png \
   res/drawable-xhdpi/icon_most_recent.png \
   res/drawable-xhdpi/icon_most_recent_empty.png \
   res/drawable-xhdpi/icon_most_visited.png \
   res/drawable-xhdpi/icon_openinapp.png \
   res/drawable-xhdpi/icon_pageaction.png \
   res/drawable-xhdpi/icon_reading_list_empty.png \
--- a/mobile/android/base/home/BookmarksPage.java
+++ b/mobile/android/base/home/BookmarksPage.java
@@ -15,17 +15,20 @@ import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewStub;
 import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 /**
  * A page in about:home that displays a ListView of bookmarks.
  */
 public class BookmarksPage extends HomeFragment {
     public static final String LOGTAG = "GeckoBookmarksPage";
 
     // Cursor loader ID for list of bookmarks.
@@ -35,16 +38,19 @@ public class BookmarksPage extends HomeF
     private static final String BOOKMARKS_FOLDER_KEY = "folder_id";
 
     // List of bookmarks.
     private BookmarksListView mList;
 
     // Adapter for list of bookmarks.
     private BookmarksListAdapter mListAdapter;
 
+    // Reference to the View to display when there are no results.
+    private View mEmptyView;
+
     // Callback for cursor loaders.
     private CursorLoaderCallbacks mLoaderCallbacks;
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         final View view = inflater.inflate(R.layout.home_bookmarks_page, container, false);
 
         mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list);
@@ -97,16 +103,17 @@ public class BookmarksPage extends HomeF
         mLoaderCallbacks = new CursorLoaderCallbacks();
         loadIfVisible();
     }
 
     @Override
     public void onDestroyView() {
         mList = null;
         mListAdapter = null;
+        mEmptyView = null;
         super.onDestroyView();
     }
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
         // Reattach the fragment, forcing a reinflation of its view.
@@ -125,16 +132,32 @@ public class BookmarksPage extends HomeF
         }
     }
 
     @Override
     protected void load() {
         getLoaderManager().initLoader(LOADER_ID_BOOKMARKS_LIST, null, mLoaderCallbacks);
     }
 
+    private void updateUiFromCursor(Cursor c) {
+        if ((c == null || c.getCount() == 0) && mEmptyView == null) {
+            // Set empty page view. We delay this so that the empty view won't flash.
+            final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
+            mEmptyView = emptyViewStub.inflate();
+
+            final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image);
+            emptyIcon.setImageResource(R.drawable.icon_bookmarks_empty);
+
+            final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text);
+            emptyText.setText(R.string.home_bookmarks_empty);
+
+            mList.setEmptyView(mEmptyView);
+        }
+    }
+
     /**
      * Loader for the list for bookmarks.
      */
     private static class BookmarksLoader extends SimpleCursorLoader {
         private final int mFolderId;
 
         public BookmarksLoader(Context context) {
             this(context, Bookmarks.FIXED_ROOT_ID);
@@ -162,16 +185,17 @@ public class BookmarksPage extends HomeF
             } else {
                 return new BookmarksLoader(getActivity(), args.getInt(BOOKMARKS_FOLDER_KEY));
             }
         }
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
             mListAdapter.swapCursor(c);
+            updateUiFromCursor(c);
         }
 
         @Override
         public void onLoaderReset(Loader<Cursor> loader) {
             if (mList != null) {
                 mListAdapter.swapCursor(null);
             }
         }
--- a/mobile/android/base/home/BrowserSearch.java
+++ b/mobile/android/base/home/BrowserSearch.java
@@ -135,17 +135,23 @@ public class BrowserSearch extends HomeF
         public void onSearch(String engineId, String text);
     }
 
     public interface OnEditSuggestionListener {
         public void onEditSuggestion(String suggestion);
     }
 
     public static BrowserSearch newInstance() {
-        return new BrowserSearch();
+        BrowserSearch browserSearch = new BrowserSearch();
+
+        final Bundle args = new Bundle();
+        args.putBoolean(HomePager.CAN_LOAD_ARG, true);
+        browserSearch.setArguments(args);
+
+        return browserSearch;
     }
 
     public BrowserSearch() {
         mSearchTerm = "";
     }
 
     @Override
     public void onAttach(Activity activity) {
@@ -277,17 +283,17 @@ public class BrowserSearch extends HomeF
                 }
                 return false;
             }
         });
 
         registerForContextMenu(mList);
         registerEventListener("SearchEngines:Data");
 
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:Get", null));
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:GetVisible", null));
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
         // Intialize the search adapter
         mAdapter = new SearchAdapter(getActivity());
@@ -310,17 +316,17 @@ public class BrowserSearch extends HomeF
                     setSearchEngines(message);
                 }
             });
         }
     }
 
     @Override
     protected void load() {
-        getLoaderManager().initLoader(LOADER_ID_SEARCH, null, mCursorLoaderCallbacks);
+        SearchLoader.init(getLoaderManager(), LOADER_ID_SEARCH, mCursorLoaderCallbacks, mSearchTerm);
     }
 
     private void handleAutocomplete(String searchTerm, Cursor c) {
         if (TextUtils.isEmpty(mSearchTerm) || c == null || mAutocompleteHandler == null) {
             return;
         }
 
         // Avoid searching the path if we don't have to. Currently just
@@ -601,17 +607,17 @@ public class BrowserSearch extends HomeF
         mAutocompleteHandler = handler;
 
         if (isVisible()) {
             // The adapter depends on the search term to determine its number
             // of items. Make it we notify the view about it.
             mAdapter.notifyDataSetChanged();
 
             // Restart loaders with the new search term
-            SearchLoader.restart(getLoaderManager(), LOADER_ID_SEARCH, mCursorLoaderCallbacks, mSearchTerm, false);
+            SearchLoader.restart(getLoaderManager(), LOADER_ID_SEARCH, mCursorLoaderCallbacks, mSearchTerm);
             filterSuggestions();
         }
     }
 
     private static class SuggestionAsyncLoader extends AsyncTaskLoader<ArrayList<String>> {
         private final SuggestClient mSuggestClient;
         private final String mSearchTerm;
         private ArrayList<String> mSuggestions;
--- a/mobile/android/base/home/LastTabsPage.java
+++ b/mobile/android/base/home/LastTabsPage.java
@@ -241,17 +241,17 @@ public class LastTabsPage extends HomeFr
             }.parse(jsonString);
 
             return c;
         }
     }
 
     private static class LastTabsAdapter extends CursorAdapter {
         public LastTabsAdapter(Context context) {
-            super(context, null);
+            super(context, null, 0);
         }
 
         @Override
         public void bindView(View view, Context context, Cursor cursor) {
             ((TwoLinePageRow) view).updateFromCursor(cursor);
         }
 
         @Override
--- a/mobile/android/base/home/MultiTypeCursorAdapter.java
+++ b/mobile/android/base/home/MultiTypeCursorAdapter.java
@@ -20,17 +20,17 @@ import android.view.ViewGroup;
 abstract class MultiTypeCursorAdapter extends CursorAdapter {
     private final int[] mViewTypes;
     private final int[] mLayouts;
 
     // Bind the view for the given position.
     abstract public void bindView(View view, Context context, int position);
 
     public MultiTypeCursorAdapter(Context context, Cursor cursor, int[] viewTypes, int[] layouts) {
-        super(context, cursor);
+        super(context, cursor, 0);
 
         if (viewTypes.length != layouts.length) {
             throw new IllegalStateException("The view types and the layouts should be of same size");
         }
 
         mViewTypes = viewTypes;
         mLayouts = layouts;
     }
--- a/mobile/android/base/home/PinSiteDialog.java
+++ b/mobile/android/base/home/PinSiteDialog.java
@@ -173,17 +173,17 @@ class PinSiteDialog extends DialogFragme
     public void setOnSiteSelectedListener(OnSiteSelectedListener listener) {
         mOnSiteSelectedListener = listener;
     }
 
     private static class SearchAdapter extends CursorAdapter {
         private LayoutInflater mInflater;
 
         public SearchAdapter(Context context) {
-            super(context, null);
+            super(context, null, 0);
             mInflater = LayoutInflater.from(context);
         }
 
         @Override
         public void bindView(View view, Context context, Cursor cursor) {
             TwoLinePageRow row = (TwoLinePageRow) view;
             row.setShowIcons(false);
             row.updateFromCursor(cursor);
--- a/mobile/android/base/home/ReadingListPage.java
+++ b/mobile/android/base/home/ReadingListPage.java
@@ -193,17 +193,17 @@ public class ReadingListPage extends Hom
         }
     }
 
     /**
      * Cursor adapter for the list of reading list items.
      */
     private class ReadingListAdapter extends CursorAdapter {
         public ReadingListAdapter(Context context, Cursor cursor) {
-            super(context, cursor);
+            super(context, cursor, 0);
         }
 
         @Override
         public void bindView(View view, Context context, Cursor cursor) {
             final TwoLinePageRow row = (TwoLinePageRow) view;
             row.updateFromCursor(cursor);
         }
 
--- a/mobile/android/base/home/SearchLoader.java
+++ b/mobile/android/base/home/SearchLoader.java
@@ -17,67 +17,61 @@ import android.text.TextUtils;
 
 /**
  * Encapsulates the implementation of the search cursor loader.
  */
 class SearchLoader {
     // Key for search terms
     private static final String KEY_SEARCH_TERM = "search_term";
 
-    // Key for performing empty search
-    private static final String KEY_PERFORM_EMPTY_SEARCH = "perform_empty_search";
-
     private SearchLoader() {
     }
 
     public static Loader<Cursor> createInstance(Context context, Bundle args) {
         if (args != null) {
             final String searchTerm = args.getString(KEY_SEARCH_TERM);
-            final boolean performEmptySearch = args.getBoolean(KEY_PERFORM_EMPTY_SEARCH, false);
-            return new SearchCursorLoader(context, searchTerm, performEmptySearch);
+            return new SearchCursorLoader(context, searchTerm);
         } else {
-            return new SearchCursorLoader(context, "", false);
+            return new SearchCursorLoader(context, "");
         }
     }
 
+    private static Bundle createArgs(String searchTerm) {
+        Bundle args = new Bundle();
+        args.putString(SearchLoader.KEY_SEARCH_TERM, searchTerm);
+
+        return args;
+    }
+
+    public static void init(LoaderManager manager, int loaderId,
+                               LoaderCallbacks<Cursor> callbacks, String searchTerm) {
+        final Bundle args = createArgs(searchTerm);
+        manager.initLoader(loaderId, args, callbacks);
+    }
+
     public static void restart(LoaderManager manager, int loaderId,
                                LoaderCallbacks<Cursor> callbacks, String searchTerm) {
-        restart(manager, loaderId, callbacks, searchTerm, true);
-    }
-
-    public static void restart(LoaderManager manager, int loaderId,
-                               LoaderCallbacks<Cursor> callbacks, String searchTerm, boolean performEmptySearch) {
-        Bundle bundle = new Bundle();
-        bundle.putString(SearchLoader.KEY_SEARCH_TERM, searchTerm);
-        bundle.putBoolean(SearchLoader.KEY_PERFORM_EMPTY_SEARCH, performEmptySearch);
-        manager.restartLoader(loaderId, bundle, callbacks);
+        final Bundle args = createArgs(searchTerm);
+        manager.restartLoader(loaderId, args, callbacks);
     }
 
     public static class SearchCursorLoader extends SimpleCursorLoader {
         // Max number of search results
         private static final int SEARCH_LIMIT = 100;
 
         // The target search term associated with the loader
         private final String mSearchTerm;
 
-        // An empty search on the DB
-        private final boolean mPerformEmptySearch;
-
-        public SearchCursorLoader(Context context, String searchTerm, boolean performEmptySearch) {
+        public SearchCursorLoader(Context context, String searchTerm) {
             super(context);
             mSearchTerm = searchTerm;
-            mPerformEmptySearch = performEmptySearch;
         }
 
         @Override
         public Cursor loadCursor() {
-            if (!mPerformEmptySearch && TextUtils.isEmpty(mSearchTerm)) {
-                return null;
-            }
-
             return BrowserDB.filter(getContext().getContentResolver(), mSearchTerm, SEARCH_LIMIT);
         }
 
         public String getSearchTerm() {
             return mSearchTerm;
         }
     }
 
--- a/mobile/android/base/home/TopSitesGridView.java
+++ b/mobile/android/base/home/TopSitesGridView.java
@@ -10,16 +10,17 @@ import org.mozilla.gecko.ThumbnailHelper
 import org.mozilla.gecko.db.BrowserDB.TopSitesCursorWrapper;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.util.StringUtils;
 
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.database.Cursor;
+import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.View;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.GridView;
 
@@ -27,19 +28,19 @@ import java.util.EnumSet;
 
 /**
  * A grid view of top and pinned sites.
  * Each cell in the grid is a TopSitesGridItemView.
  */
 public class TopSitesGridView extends GridView {
     private static final String LOGTAG = "GeckoTopSitesGridView";
 
-    // Listener for pinning sites.
-    public static interface OnPinSiteListener {
-        public void onPinSite(int position);
+    // Listener for editing pinned sites.
+    public static interface OnEditPinnedSiteListener {
+        public void onEditPinnedSite(int position);
     }
 
     // Max number of top sites that needs to be shown.
     private final int mMaxSites;
 
     // Number of columns to show.
     private final int mNumColumns;
 
@@ -53,22 +54,27 @@ public class TopSitesGridView extends Gr
     private int mMeasuredWidth;
 
     // Measured height of this view.
     private int mMeasuredHeight;
 
     // On URL open listener.
     private OnUrlOpenListener mUrlOpenListener;
 
-    // Pin site listener.
-    private OnPinSiteListener mPinSiteListener;
+    // Edit pinned site listener.
+    private OnEditPinnedSiteListener mEditPinnedSiteListener;
 
     // Context menu info.
     private TopSitesGridContextMenuInfo mContextMenuInfo;
 
+    // Whether we're handling focus changes or not. This is used
+    // to avoid infinite re-layouts when using this GridView as
+    // a ListView header view (see bug 918044).
+    private boolean mIsHandlingFocusChange;
+
     public TopSitesGridView(Context context) {
         this(context, null);
     }
 
     public TopSitesGridView(Context context, AttributeSet attrs) {
         this(context, attrs, R.attr.topSitesGridViewStyle);
     }
 
@@ -77,16 +83,18 @@ public class TopSitesGridView extends Gr
         mMaxSites = getResources().getInteger(R.integer.number_of_top_sites);
         mNumColumns = getResources().getInteger(R.integer.number_of_top_sites_cols);
         setNumColumns(mNumColumns);
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TopSitesGridView, defStyle, 0);
         mHorizontalSpacing = a.getDimensionPixelOffset(R.styleable.TopSitesGridView_android_horizontalSpacing, 0x00);
         mVerticalSpacing = a.getDimensionPixelOffset(R.styleable.TopSitesGridView_android_verticalSpacing, 0x00);
         a.recycle();
+
+        mIsHandlingFocusChange = false;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -101,18 +109,18 @@ public class TopSitesGridView extends Gr
 
                 // If the url is empty, the user can pin a site.
                 // If not, navigate to the page given by the url.
                 if (!TextUtils.isEmpty(url)) {
                     if (mUrlOpenListener != null) {
                         mUrlOpenListener.onUrlOpen(url, EnumSet.noneOf(OnUrlOpenListener.Flags.class));
                     }
                 } else {
-                    if (mPinSiteListener != null) {
-                        mPinSiteListener.onPinSite(position);
+                    if (mEditPinnedSiteListener != null) {
+                        mEditPinnedSiteListener.onEditPinnedSite(position);
                     }
                 }
             }
         });
 
         setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
             @Override
             public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
@@ -123,17 +131,31 @@ public class TopSitesGridView extends Gr
         });
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
         mUrlOpenListener = null;
-        mPinSiteListener = null;
+        mEditPinnedSiteListener = null;
+    }
+
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        mIsHandlingFocusChange = true;
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+        mIsHandlingFocusChange = false;
+    }
+
+    @Override
+    public void requestLayout() {
+        if (!mIsHandlingFocusChange) {
+            super.requestLayout();
+        }
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
     public int getColumnWidth() {
         // This method will be called from onMeasure() too.
@@ -202,22 +224,22 @@ public class TopSitesGridView extends Gr
      *
      * @param listener An url open listener for this view.
      */
     public void setOnUrlOpenListener(OnUrlOpenListener listener) {
         mUrlOpenListener = listener;
     }
 
     /**
-     * Set a pin site listener to be used by this view.
+     * Set an edit pinned site listener to be used by this view.
      *
-     * @param listener A pin site listener for this view.
+     * @param listener An edit pinned site listener for this view.
      */
-    public void setOnPinSiteListener(OnPinSiteListener listener) {
-        mPinSiteListener = listener;
+    public void setOnEditPinnedSiteListener(final OnEditPinnedSiteListener listener) {
+        mEditPinnedSiteListener = listener;
     }
 
     /**
      * A ContextMenuInfo for TopBoomarksView that adds details from the cursor.
      */
     public static class TopSitesGridContextMenuInfo extends AdapterContextMenuInfo {
 
         public String url;
--- a/mobile/android/base/home/TopSitesPage.java
+++ b/mobile/android/base/home/TopSitesPage.java
@@ -15,17 +15,17 @@ import org.mozilla.gecko.db.BrowserContr
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
 import org.mozilla.gecko.db.BrowserDB.TopSitesCursorWrapper;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.home.HomeListView.HomeContextMenuInfo;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.PinSiteDialog.OnSiteSelectedListener;
-import org.mozilla.gecko.home.TopSitesGridView.OnPinSiteListener;
+import org.mozilla.gecko.home.TopSitesGridView.OnEditPinnedSiteListener;
 import org.mozilla.gecko.home.TopSitesGridView.TopSitesGridContextMenuInfo;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.Cursor;
@@ -95,18 +95,18 @@ public class TopSitesPage extends HomeFr
     private boolean mSnapBannerToTop;
 
     // Callbacks used for the search and favicon cursor loaders
     private CursorLoaderCallbacks mCursorLoaderCallbacks;
 
     // Callback for thumbnail loader
     private ThumbnailsLoaderCallbacks mThumbnailsLoaderCallbacks;
 
-    // Listener for pinning sites
-    private PinSiteListener mPinSiteListener;
+    // Listener for editing pinned sites.
+    private EditPinnedSiteListener mEditPinnedSiteListener;
 
     // On URL open listener
     private OnUrlOpenListener mUrlOpenListener;
 
     // Max number of entries shown in the grid from the cursor.
     private int mMaxGridEntries;
 
     // Time in ms until the Gecko thread is reset to normal priority.
@@ -150,17 +150,17 @@ public class TopSitesPage extends HomeFr
         mGrid = new TopSitesGridView(getActivity());
         mList.addHeaderView(mGrid);
 
         return view;
     }
 
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
-        mPinSiteListener = new PinSiteListener();
+        mEditPinnedSiteListener = new EditPinnedSiteListener();
 
         mList.setTag(HomePager.LIST_TAG_TOP_SITES);
         mList.setHeaderDividersEnabled(false);
 
         mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 final ListView list = (ListView) parent;
@@ -181,17 +181,17 @@ public class TopSitesPage extends HomeFr
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
 
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         mGrid.setOnUrlOpenListener(mUrlOpenListener);
-        mGrid.setOnPinSiteListener(mPinSiteListener);
+        mGrid.setOnEditPinnedSiteListener(mEditPinnedSiteListener);
 
         registerForContextMenu(mList);
         registerForContextMenu(mGrid);
 
         mBanner = (HomeBanner) view.findViewById(R.id.home_banner);
         mList.setOnTouchListener(new OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
@@ -333,17 +333,17 @@ public class TopSitesPage extends HomeFr
                     BrowserDB.unpinSite(context.getContentResolver(), position);
                 }
             });
 
             return true;
         }
 
         if (itemId == R.id.top_sites_edit) {
-            mPinSiteListener.onPinSite(info.position);
+            mEditPinnedSiteListener.onEditPinnedSite(info.position);
             return true;
         }
 
         return false;
     }
 
     @Override
     protected void load() {
@@ -366,28 +366,28 @@ public class TopSitesPage extends HomeFr
         Uri uri = Uri.parse(url);
         if ("user-entered".equals(uri.getScheme())) {
             return uri.getSchemeSpecificPart();
         }
         return url;
     }
 
     /**
-     * Listener for pinning sites.
+     * Listener for editing pinned sites.
      */
-    private class PinSiteListener implements OnPinSiteListener,
-                                             OnSiteSelectedListener {
+    private class EditPinnedSiteListener implements OnEditPinnedSiteListener,
+                                                    OnSiteSelectedListener {
         // Tag for the PinSiteDialog fragment.
         private static final String TAG_PIN_SITE = "pin_site";
 
         // Position of the pin.
         private int mPosition;
 
         @Override
-        public void onPinSite(int position) {
+        public void onEditPinnedSite(int position) {
             mPosition = position;
 
             final FragmentManager manager = getActivity().getSupportFragmentManager();
             PinSiteDialog dialog = (PinSiteDialog) manager.findFragmentByTag(TAG_PIN_SITE);
             if (dialog == null) {
                 dialog = PinSiteDialog.newInstance();
             }
 
@@ -480,17 +480,17 @@ public class TopSitesPage extends HomeFr
         public Cursor loadCursor() {
             Log.d(LOGTAG, "TopSitesLoader.loadCursor()");
             return BrowserDB.getTopSites(getContext().getContentResolver(), mMaxGridEntries, SEARCH_LIMIT);
         }
     }
 
     private class VisitedAdapter extends CursorAdapter {
         public VisitedAdapter(Context context, Cursor cursor) {
-            super(context, cursor);
+            super(context, cursor, 0);
         }
 
         @Override
         public int getCount() {
             return Math.max(0, super.getCount() - mMaxGridEntries);
         }
 
         @Override
@@ -513,17 +513,17 @@ public class TopSitesPage extends HomeFr
         }
     }
 
     public class TopSitesGridAdapter extends CursorAdapter {
         // Cache to store the thumbnails.
         private Map<String, Bitmap> mThumbnails;
 
         public TopSitesGridAdapter(Context context, Cursor cursor) {
-            super(context, cursor);
+            super(context, cursor, 0);
         }
 
         @Override
         public int getCount() {
             return Math.min(mMaxGridEntries, super.getCount());
         }
 
         @Override
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -270,16 +270,17 @@ size. -->
 <!ENTITY button_clear "Clear">
 
 <!ENTITY home_top_sites_title "Top Sites">
 <!-- Localization note (home_top_sites_add): This string is used as placeholder
      text underneath empty thumbnails in the Top Sites page on about:home. -->
 <!ENTITY home_top_sites_add "Add a site">
 
 <!ENTITY home_history_title "History">
+<!ENTITY home_bookmarks_empty "Bookmarks you save show up here.">
 <!ENTITY home_last_tabs_title "Tabs from last time">
 <!ENTITY home_last_tabs_open "Open all tabs from last time">
 <!ENTITY home_last_tabs_empty "Your recent tabs show up here.">
 <!ENTITY home_most_recent_title "Most recent">
 <!ENTITY home_most_recent_empty "Websites you visited most recently show up here.">
 <!ENTITY home_reading_list_empty "Articles you save for later show up here.">
 <!-- Localization note (home_reading_list_hint): The "TIP" string is synonymous to "hint", "clue", etc. This string is displayed
      as an advisory message on how to add content to the reading list when the reading list empty.
--- a/mobile/android/base/preferences/SearchEnginePreference.java
+++ b/mobile/android/base/preferences/SearchEnginePreference.java
@@ -136,17 +136,18 @@ public class SearchEnginePreference exte
         } catch (IllegalArgumentException e) {
             Log.e(LOGTAG, "IllegalArgumentException creating Bitmap. Most likely a zero-length bitmap.", e);
         } catch (NullPointerException e) {
             Log.e(LOGTAG, "NullPointerException creating Bitmap. Most likely a zero-length bitmap.", e);
         }
     }
 
     /**
-     * Set if this object's UI should show that this is the default engine.
+     * Set if this object's UI should show that this is the default engine. To ensure proper ordering,
+     * this method should only be called after this Preference is added to the PreferenceCategory.
      * @param isDefault Flag indicating if this represents the default engine.
      */
     public void setIsDefaultEngine(boolean isDefault) {
         mIsDefaultEngine = isDefault;
         if (isDefault) {
             setOrder(0);
             setSummary(LABEL_IS_DEFAULT);
         } else {
@@ -201,17 +202,19 @@ public class SearchEnginePreference exte
             }
         });
 
         // Copy the icon from this object to the prompt we produce. We lazily create the drawable,
         // as the user may not ever actually tap this object.
         if (mPromptIcon == null && mIconBitmap != null) {
             mPromptIcon = new BitmapDrawable(mFaviconView.getBitmap());
         }
-        builder.setIcon(mPromptIcon);
+
+        // Icons are hidden until Bug 926711 is fixed.
+        //builder.setIcon(mPromptIcon);
 
         // We have to construct the dialog itself on the UI thread.
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 mDialog = builder.create();
                 mDialog.setOnShowListener(new DialogInterface.OnShowListener() {
                     // Called when the dialog is shown (so we're finally able to manipulate button enabledness).
--- a/mobile/android/base/preferences/SearchPreferenceCategory.java
+++ b/mobile/android/base/preferences/SearchPreferenceCategory.java
@@ -36,78 +36,72 @@ public class SearchPreferenceCategory ex
         super(context);
     }
 
     @Override
     protected void onAttachedToActivity() {
         super.onAttachedToActivity();
 
         // Ensures default engine remains at top of list.
-        setOrderingAsAdded(false);
+        setOrderingAsAdded(true);
 
         // Request list of search engines from Gecko.
         GeckoAppShell.registerEventListener("SearchEngines:Data", this);
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:Get", null));
     }
 
     @Override
     public void handleMessage(String event, final JSONObject data) {
         if (event.equals("SearchEngines:Data")) {
-            // Parse engines array from JSON. The first element in the array is the default engine.
+            // We are no longer interested in this event from Gecko, as we do not request it again with
+            // this instance.
+            GeckoAppShell.unregisterEventListener("SearchEngines:Data", this);
+
+            // Parse engines array from JSON.
             JSONArray engines;
-            JSONObject defaultEngine;
-            final String defaultEngineName;
             try {
                 engines = data.getJSONArray("searchEngines");
-                if (engines.length() == 0) {
-                    return;
-                }
-                defaultEngine = engines.getJSONObject(0);
-                defaultEngineName = defaultEngine.getString("name");
             } catch (JSONException e) {
                 Log.e(LOGTAG, "Unable to decode search engine data from Gecko.", e);
                 return;
             }
 
             // Create an element in this PreferenceCategory for each engine.
             for (int i = 0; i < engines.length(); i++) {
                 try {
                     JSONObject engineJSON = engines.getJSONObject(i);
                     final String engineName = engineJSON.getString("name");
 
                     SearchEnginePreference enginePreference = new SearchEnginePreference(getContext(), this);
                     enginePreference.setSearchEngineFromJSON(engineJSON);
-                    if (engineName.equals(defaultEngineName)) {
-                        // We set this here, not in setSearchEngineFromJSON, because it allows us to
-                        // keep a reference  to the default engine to use when the AlertDialog
-                        // callbacks are used.
-                        enginePreference.setIsDefaultEngine(true);
-                        mDefaultEngineReference = enginePreference;
-                    }
-
                     enginePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                         @Override
                         public boolean onPreferenceClick(Preference preference) {
                             SearchEnginePreference sPref = (SearchEnginePreference) preference;
                             // Display the configuration dialog associated with the tapped engine.
                             sPref.showDialog();
                             return true;
                         }
                     });
 
                     addPreference(enginePreference);
+
+                    // The first element in the array is the default engine.
+                    if (i == 0) {
+                        // We set this here, not in setSearchEngineFromJSON, because it allows us to
+                        // keep a reference  to the default engine to use when the AlertDialog
+                        // callbacks are used.
+                        enginePreference.setIsDefaultEngine(true);
+                        mDefaultEngineReference = enginePreference;
+                    }
                 } catch (JSONException e) {
                     Log.e(LOGTAG, "JSONException parsing engine at index " + i, e);
                 }
             }
         }
-
-        // We are no longer interested in this event from Gecko, as we do not request it again with
-        // this instance.
-        GeckoAppShell.unregisterEventListener("SearchEngines:Data", this);
     }
 
     /**
      * Set the default engine to any available engine. Used if the current default is removed or
      * disabled.
      */
     private void setFallbackDefaultEngine() {
         if (getPreferenceCount() > 0) {
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4bd9cd7ba9670b4ba37f98b326bbd75bec2d9cd0
GIT binary patch
literal 1547
zc$@(X2K4!fP)<h;3K|Lk000e1NJLTq004&o004&w1^@s6Jm*RZ000HjNkl<Zc-rjT
z-)oyy9LI4V7E5Vx6eeMFYH*{(U!A2Ill3R1i0j1FY6@LJD^mnFXW3x8s5%_oCd@GD
zQd^9;>AD}STg{sDH+=7R-G8S0Ts)A-vS(@f{5a1!pNkj0=uP`QZ|KuJ=bY!Zwzh~G
zvVH)>IEZl&;~>UCjDr{lF%DuJ#5jm?Je@q+G+jGP!=F%bzfMED;lDTIc!m@#gKA(I
zQjEjSIFe8X<)I8o#$jI^XFxUK4CAmXjto?rjH3z}#$it!mqBgAWyWDg96g{~(8D;)
zj>Casi*XdeVI1bhkq5N{ImTgT91bk*FpfoV7>9XrjDYgNFyk;Qjs%o_ilYp>7>7A=
zjDhme5yoLg9BuHo&vD#@HpUVEI3_>^;1k9X?*Vkcoq*!F109SbzNSG1Xo_*f({5M`
zIF2>g%{b!c1gH>v#W><+53Gk2$2#m`9Px1qRESP7j(FGym5}3j2rn@X{hb9BfwPQ5
zZ)vDT6h{@(j6+{@pd$1m<IsH5aD=LmVI2CI2c>~aj6*MnKxyO<<Iu;in#ECM9D2xs
z(ol|ZM9YDarg1ES!#JWd3`%3ej3XMmpsaZuWk@iNU?m{~qc8(=9%1Mh$Q;bTC}beX
zIJU=u6!gM4oP;ZI3zUIw!4)_O<IoE!aBPmF4GzE{9EbC;0PCP=bKRZCd3Qd8?!4Me
zilY;{VGO>8i|`jzg#omp>dx<?JI^ubhE84MNWgx`Ljh)?2=|1G8K!$sgjpy+-d&eO
zm~kW^2dAL~72(3@tO6zY4swtPDvn{eAsl#|-GCv#<2WH4tVzD~D~_yim?p~l5yu7L
zAWd|^k2scugEY~SA92hJ2Wg^9e#CK1I7kx}{D>n7{|JX^k`+k$^{?z<e(Y_GD)jmN
zAy`2;SQ8Y2`uKcGINUh<Jm?p1lfog#(ebd}%1#J}9Ve4vzp_8Zhtyd3G^+g^M;VB=
zsxTh)POuLdh?Ze|q{}wJQP>bJiY{Rs(QOau2m^`S27I9F2IC=kC|snd!Whwg_jy0}
zmPQiBP+X2C<QPb3D$pOdb1PZ+Pq=tNhmnu#iML*!WD7KmY}}7Sz5@@0i|MCzc-NG(
zvTwtE;iCHC0rZ%5korwn6E5yf+=p&cPuaf?tHMQY#lP^D=~pmx@u2m#t->3&T;6gJ
z?g|&Z*)Y0nyV>V8xGh|KFL&Xft=B%i0xQA@8o3PzY`+uiWuA+9!V08$VI07IZW#KX
zS9zOY&5Oby>g{D5H9f*0>SY;6&BwwZ>P;|?ns0?c)cc-s)chh0qTW@;;o^s3PgrFf
z051q*Xrz;I;8ngbghsj<2TsfhLulkE<G>~F!VntyhH*UF{8<=6BlC=d_o+Y4P21yW
z=L<Ja9X7|&B@Dnz#^yLigaLRNwK<M2gaLS&u{n+(gaLT@$>ungg#mc^!`3(wyy)eb
zHsE<1<46k^zZq#8;}{Swd@}}Yh=cd2w`R)5IL-+dz8U9ijN>{ejV!^W`&DV=x@~bd
z8r}BuI}CwS_p=ScqDHs)I5x$R0u`}E81NXp<9@#cDq{O=isL;{5h+1`qbNKg>5Dj$
ztW9xDMD(}2&oc^d4jhi?S?M#I;y4W|Bn$4MIzB!x<zWF-NM>z{<0`0t{RTO30(_``
zAFhE4Skam|0=^UM8f0rxgdz)%+qs(*1feJl!`GQKXaPcCvnHBkn{kvaMocQ2)glBZ
zZm@R+A^xCtzQMDDS1$L;InODFoUFvLo$%{A!_6qgW2zx13vukwj_8gQZb42H9G#(s
zI1VEY8z*oJmW&mNl~t1g+5svga4T{|g?50zLL6St9i%tJa4Qz0?74E%vvOO>(RNh+
zh{8%FxuvV@ro$srfe7x8MJPJF*Z6kg0DNS4qd|=quGqsF65@gvYBcyDgFojldlAJU
xibE8KC=O8^qBulxh~f~%A&NspafsrOfxd>{(hw7%z=Hq)002ovPDHLkV1i=6$b0|*
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3ee46dfe989943dd13525f18f9e7ac3dcd60ae51
GIT binary patch
literal 997
zc$@*{0~-8^P)<h;3K|Lk000e1NJLTq003G5003GD1^@s6IH*Aa000B5Nkl<Zc-rmV
z&r1|x9LMpUb=n;bBJ@x~Bm~P6;c7oOBNRMY(7{8bhzU_kx<yNATewu3`D;x}JS;-#
zAyEiXX8wk`d-k8P&*9mNvCI74=R5N~yl%oi&)XGepJ$%g*vZKW3qa%paA*$AI5dZ5
zkq^+&(B{#xj5Nr>u8;#w!f}!gtwI@$Q_7%nXzz@IQNvRX?Tt3ruM>J7+Bmd(c@eap
zJco9t9S$snK7@7-?c5|77EN+!XF5Q)5n6{14sBlv44X<E+MYD%4npgY=FrxC1H+*?
z4sA^a43{z-x;_O~JP5r43Wu&O3x-Fs9J=<qdBeld8=!FL|Mh|4u|5th2r6v)5PB1u
zI5Y?YVEAN!LjxpW+sDw`kl@fq$Ae(_Y%uDfTVN-E&^yo)<<LW51Y|f0p*6Syy)Xjr
zVI2+v41FkGvna;UE5@Wz4BZMDcmO$=g<sW`I7#VZjI)pvW6wZqqeH8333}i$6yQ7T
zfe|u$Vr~U7*B&u<)$7m-WMBlQp;G;GkGKP<h$bVVk>WIT3Vxa#)e_MvWjFLi_-k@J
zTST)9c0v!E9L=dAJE2ES4(8ODozQ8MgE@88?mxpxlcU*HbaK^^gtE!;tXhGj?a)FC
zESns$F45?$+fRlXESMa#mL+KR>LYj(=1h)y+B|4puXQA0#^ktFUm@w&qEZ4rn;iKI
zB!XDID@8c+CpZ(-dbA23BN%xa)Swsso1hq>$R9v$)DBA=-bEnt6vP|7l@*6Vh$Fv+
zxGZ~y3cLzo<TntPZF5$E@eoDMgCgrLxB|HlL>>o)!hIdj2uFSa3Z)xpUJ{HvLGezd
zM+8F;QVcyoF!XDRp%*AF;8P6!hhXSkYN5}8LDYFlp|24R-AO6*0O8ONDTRJcICPFu
z=o!j;fs{i3CLDT;N@%Tml!CBk3ZYYA5SOA5x<3S=`zeGT3qj~H3ZY9O2wkEOy5e($
z^#BerRb&sX!l93+p6-GoWZ{R8r)*W3Ltpk`(rb{dvAt9czUIU0cE}w1o`(}%h1(YQ
z?-cQ~s~*1oeVIc)@#u)cEf-_B>Cq$IhU5(G{(w<gM2dOjhRb6iQ__ZZII(584l$o}
z!;+I<ytJWhUIi>dSHO?rT(onQBW-AlxA87C_}QTuiyOy9nL~GhUMFxT#3a!P^K~AU
z(k*Rhp%*G*u-lM_G{hv87GvB3U5xz>#2n(UaN_UE;?Rskb7;n)IW&i69GaGYV#o;t
TVF!SZ00000NkvXXu0mjfIMlre
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4ea238ec6f6523920ddecc38fe07b5afd0b5b7ef
GIT binary patch
literal 2164
zc$_tsdpy(oAIFtFvbjYvEW*ZIBO{$nhsH9OOoYNnvCIiQnvqh&SCouSnRUu-_Uj@o
zxlc=U4u%oyh$O6Bcgih<G5l8N_xt1Xem~!@&-?xU_w~*^M!>5m!W8A?<W$_<aK5r!
z^W%Yc$l|YWcA(_szz*&>tY6~b^pN*CqQ5Txo2tg+L7{T3n%)^N%Ubze+e6+UeW{v&
zb9TN@i7=uM9Ec;<`~w&A+<6cNxc0v4S)@iU3N>{=x?FzM_8T_GfZW@)R20`!NYg5A
zNSV3<S)3|D$6dLiOsBu6$*I{L7Rq7eK`OuwFwB6Q!rBJxz!tY`u4<$`X=g`w-sbKr
zc5fYsR09=OM}5#-+3qBuS88Cj4u|kYL)jMb;no0Q;A55bEMs|sUAYHqQfjo)sv=Hh
z&^N#so5j%#yYtg{Aw(zFWHe(5$EWK-FzQ*Op>Nrh2&~B`P7MmoYn|!^6#~W=bOKS!
zMq=G;Pm^Yr1EqT)j}^C&T~EInGwmTnmkQ2r0~{HU@KZoe^bw*#C%+M-_x8x8z5B&e
zSBwclo2wR975?FoKn@Gpga`9X=3q%0)G$$IR>bf?K1=>Slh(poUudj!z$#F@i4hmG
zhas30-csw4{;9J{*z5C$i3I9}6cz;I5I0J(#|uG)dDkJJjo)R8lFEflvYH*X>E@k7
z9;RZvFPseQ?T^2ZDY?diT-GLCDhLzaBGF}hOsRxD?z|7%7I6D<3GkB=T!^Ewvf*Mr
zIx5SP7(5~yrrmo~50H1s#PJ4QsHxZm?JWdtz_~4&UHbr}SgdvG#W7D|<uLTbF)rjj
zYwH>E)pmv9To_hk$*JF^m7g925Ee(zt*xQFgrHZ3vAAfpn$3dkTBokRmVM>S&`Rs;
zWeX5--}01dL3%;{XGvHl>G&W*ml|wO3WP=klz~DxtWK1%io~}+A{a9|&y{cLxGyUG
ze9%IJO5;zsNc>_2dLQzN)Tb*hSAFF+{KI`^J_ahxzH?M?Ktc?dP>9m`*4n@D@%dVH
zJu=r9F5W6@S3p^NNkS9G+reh>_^PoUj+y>?HD|Y>*iyuJ*8WIpbQMAO>0OC@X=Y*4
zVCLm0zVq&Zp=T8`m=dOo!eJUS4~X2F4<7e079X784ds<z2-7B<m|bOdPpVS`nX7Kf
z+o<D8W^IZ*^I-lw_?#!wSs~=MUU+m%CF1-w<u!}&h?YKOsNJb(1BIj#pd2;^*Y8uq
zdc&IG`niUHRi|w@vE}<wK}o2~d2M+_d6J=McQ;H1FT$`%U-|7fRz+y4P@^i6o?Wp#
z?mW+WrBgI^_;AR*%#E6mLX&B=*SEVSzpsk@*{&I?6aQ(@pKtoBb7ut0;=5lghsdnE
zV5;4nN_3x5?5~;Lb3N@(>NutBZiMwW+}<4(*0xVXYejb+^G|<{Xc9bROX*lInvJT;
z)^p=I&6$hnk=w=D&1q9J9ezH^<Vox0*}2*;n4Gay^v5WxslWTprjdZ@J#m2m8(0^V
z3$UFVz|NK}H+`b~GH@y`R43|}x9IoguGH{$_{zqfk*&MWvkJ4EDWb8dwv}~>qxIgc
z*N#0;XI{Qv50vyJ;%-Q-96B2d-Z4mHHcw{{Ub4zFt1x^Ke&{#<XKuE&m3%$Q$Hbws
zKxU+Gi2ktE!EAi_P2)?L@91Idi_?b^zCcMy{>vrYjss(FkM}Vjm)R#e8l0fFdB0K7
z;d}wM0`A$u-C6-<zYV@eQ<t_4W4Pfbw?SW!aXtan3r6^~&1c>;`%%+;&%b*#IoFe%
zA7#4<M%mGyIUl#q?MDYFYCX8+ke+{QtwG(($ICOVIbX6QKqHFS5-hC1b(XL*_;NZW
z6gIR*la}_tY+4ftmwySJa}8;<W8a*}Cv<^k?7x^H%7xw3Gf1+NPx5a#s6fm2E)1L0
zQrwaD$6U)%;bzDIU~z`|4ve8U(LY@Y&fN<vKaXmG3Kl^#qEhuB(|y45+^>k5V>uc%
zky0y6X|+rz$?Ec5HOa}al61;l)&|TDt`u{FwecYa1RZn)mZxG66~Yl?ax!60YP$Uq
z;U;nacBPqJ*rdPSFikfN$WCjdVX$S!HXvcn7{NK2pk9-t;d-)C*O<IQfSqn#fM6Z?
zhfezSf@ZWMKeK}j$z0gNE%gA)m4~-j!<YWNVP2;N87#Cxln(G^d1p53%=!(M-su3Y
zTB8XW|I6d9`J*1#_oO#q^ST3*)Zu}s7C*`7dki2}$Izdah<wPp^?&Hm{vS+ftg4Qd
z9u+7y+k&ogTMWHD=8_40UIJ_79zR>LVTZ@rz!Am>ekiQqcTz@VndXvPhjUT8<mF%y
zVL;Y`5MMO1sCQU!H5s?+n}`oQlZc#QIpuO#%!r|Hrwe)X&Rxy6w6w{=yy*Hui}BfJ
znio{3$lXHR2V5<PGn#)=&1*2{mN_2EbruF2TcqTpppvtvLR3A2tF{lg3MDglEZKFs
z-RhLmik%(t9dH;@EJYe;ed6f~)G3^bV2W22cmyt%65OCdpax@j*Ub}Zq$Capy62vz
zh)i@DK^=O^iKw-6G9kF*5D%{Kfcb~~Hb?PV4r_6ph}t<xYI?VeD+(Vt4$|9d-bCx<
z1+~%z1re%x2(dJj*_9gj!KC))p4)*Px{2wmm%U7mr!DHtHEBgfq|sO3{HEOr=TFXF
zR9^^14B<am+<mDXql*-`+16!-<itzdm$geKYw<M3cdu!R?q{cfA+o?Mkdd^&<;3Eo
zsdm}>L!@+(=th!%|9Fw5z$}VMLPJb3G7FYD^1s%c_QR3?wPyVq&+8!Bp&$orHrcPI
mzLr;mUE5KDC9A=HhHDv+r6HrY`Z1`nA5HE@2)J_RFyOx^L=N5n
--- a/mobile/android/base/resources/layout/home_bookmarks_page.xml
+++ b/mobile/android/base/resources/layout/home_bookmarks_page.xml
@@ -2,14 +2,19 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">
 
+    <ViewStub android:id="@+id/home_empty_view_stub"
+              android:layout="@layout/home_empty_page"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"/>
+
     <org.mozilla.gecko.home.BookmarksListView
             android:id="@+id/bookmarks_list"
             android:layout_width="fill_parent"
             android:layout_height="fill_parent"/>
 
 </FrameLayout>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -252,16 +252,17 @@
   <string name="button_set">&button_set;</string>
   <string name="button_clear">&button_clear;</string>
   <string name="button_yes">&button_yes;</string>
   <string name="button_no">&button_no;</string>
 
   <string name="home_top_sites_title">&home_top_sites_title;</string>
   <string name="home_top_sites_add">&home_top_sites_add;</string>
   <string name="home_history_title">&home_history_title;</string>
+  <string name="home_bookmarks_empty">&home_bookmarks_empty;</string>
   <string name="home_last_tabs_title">&home_last_tabs_title;</string>
   <string name="home_last_tabs_open">&home_last_tabs_open;</string>
   <string name="home_last_tabs_empty">&home_last_tabs_empty;</string>
   <string name="home_most_recent_title">&home_most_recent_title;</string>
   <string name="home_most_recent_empty">&home_most_recent_empty;</string>
   <string name="home_reading_list_empty">&home_reading_list_empty;</string>
   <string name="home_reading_list_hint">&home_reading_list_hint;</string>
   <string name="home_reading_list_hint_accessible">&home_reading_list_hint_accessible;</string>
--- a/toolkit/components/osfile/modules/_PromiseWorker.jsm
+++ b/toolkit/components/osfile/modules/_PromiseWorker.jsm
@@ -93,17 +93,17 @@ PromiseWorker.prototype = {
      * of |OS.File.Error|. These are treated by |worker.onmessage|.
      * However, for other errors, we rely on DOM's mechanism for
      * serializing errors, which transmits these errors through
      * |worker.onerror|.
      *
      * @param {Error} error Some JS error.
      */
     worker.onerror = function onerror(error) {
-      self._log("Received uncaught error from worker", error.message);
+      self._log("Received uncaught error from worker", error.message, error.filename, error.lineno);
       error.preventDefault();
       let {deferred} = self._queue.pop();
       deferred.reject(error);
     };
 
     /**
      * Receive messages from the worker, propagate them to the listeners.
      *
--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -24,77 +24,72 @@ this.EXPORTED_SYMBOLS = ["OS"];
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 
 let SharedAll = {};
 Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", SharedAll);
 Cu.import("resource://gre/modules/Deprecated.jsm", this);
 
 // Boilerplate, to simplify the transition to require()
-let OS = SharedAll.OS;
-
-let LOG = OS.Shared.LOG.bind(OS.Shared, "Controller");
-
-let isTypedArray = OS.Shared.isTypedArray;
+let LOG = SharedAll.LOG.bind(SharedAll, "Controller");
+let isTypedArray = SharedAll.isTypedArray;
 
 // The constructor for file errors.
-let OSError;
-if (OS.Constants.Win) {
-  Cu.import("resource://gre/modules/osfile/osfile_win_allthreads.jsm", this);
-  OSError = OS.Shared.Win.Error;
-} else if (OS.Constants.libc) {
-  Cu.import("resource://gre/modules/osfile/osfile_unix_allthreads.jsm", this);
-  OSError = OS.Shared.Unix.Error;
+let SysAll = {};
+if (SharedAll.Constants.Win) {
+  Cu.import("resource://gre/modules/osfile/osfile_win_allthreads.jsm", SysAll);
+} else if (SharedAll.Constants.libc) {
+  Cu.import("resource://gre/modules/osfile/osfile_unix_allthreads.jsm", SysAll);
 } else {
   throw new Error("I am neither under Windows nor under a Posix system");
 }
-let Type = OS.Shared.Type;
+let OSError = SysAll.Error;
+let Type = SysAll.Type;
+
 let Path = {};
 Cu.import("resource://gre/modules/osfile/ospath.jsm", Path);
 
 // The library of promises.
 Cu.import("resource://gre/modules/Promise.jsm", this);
 
 // The implementation of communications
 Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
 
 Cu.import("resource://gre/modules/Services.jsm", this);
 
 Cu.import("resource://gre/modules/AsyncShutdown.jsm", this);
 
-LOG("Checking profileDir", OS.Constants.Path);
-
 // If profileDir is not available, osfile.jsm has been imported before the
 // profile is setup. In this case, make this a lazy getter.
-if (!("profileDir" in OS.Constants.Path)) {
-  Object.defineProperty(OS.Constants.Path, "profileDir", {
+if (!("profileDir" in SharedAll.Constants.Path)) {
+  Object.defineProperty(SharedAll.Constants.Path, "profileDir", {
     get: function() {
       let path = undefined;
       try {
         path = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
-        delete OS.Constants.Path.profileDir;
-        OS.Constants.Path.profileDir = path;
+        delete SharedAll.Constants.Path.profileDir;
+        SharedAll.Constants.Path.profileDir = path;
       } catch (ex) {
         // Ignore errors: profileDir is still not available
       }
       return path;
     }
   });
 }
 
 LOG("Checking localProfileDir");
 
-if (!("localProfileDir" in OS.Constants.Path)) {
-  Object.defineProperty(OS.Constants.Path, "localProfileDir", {
+if (!("localProfileDir" in SharedAll.Constants.Path)) {
+  Object.defineProperty(SharedAll.Constants.Path, "localProfileDir", {
     get: function() {
       let path = undefined;
       try {
         path = Services.dirsvc.get("ProfLD", Ci.nsIFile).path;
-        delete OS.Constants.Path.localProfileDir;
-        OS.Constants.Path.localProfileDir = path;
+        delete SharedAll.Constants.Path.localProfileDir;
+        SharedAll.Constants.Path.localProfileDir = path;
       } catch (ex) {
         // Ignore errors: localProfileDir is still not available
       }
       return path;
     }
   });
 }
 
@@ -118,17 +113,17 @@ let Scheduler = {
   shutdown: false,
 
   /**
    * The latest promise returned.
    */
   latestPromise: Promise.resolve("OS.File scheduler hasn't been launched yet"),
 
   post: function post(...args) {
-    if (!this.launched && OS.Shared.DEBUG) {
+    if (!this.launched && SharedAll.Config.DEBUG) {
       // If we have delayed sending SET_DEBUG, do it now.
       worker.post("SET_DEBUG", [true]);
     }
     this.launched = true;
     if (this.shutdown) {
       LOG("OS.File is not available anymore. The following request has been rejected.", args);
       return Promise.reject(new Error("OS.File has been shut down."));
     }
@@ -207,33 +202,33 @@ let readDebugPref = function readDebugPr
 };
 
 /**
  * Listen to PREF_OSFILE_LOG changes and update gShouldLog flag
  * appropriately.
  */
 Services.prefs.addObserver(PREF_OSFILE_LOG,
   function prefObserver(aSubject, aTopic, aData) {
-    OS.Shared.DEBUG = readDebugPref(PREF_OSFILE_LOG, OS.Shared.DEBUG);
+    SharedAll.Config.DEBUG = readDebugPref(PREF_OSFILE_LOG, SharedAll.Config.DEBUG);
     if (Scheduler.launched) {
       // Don't start the worker just to set this preference.
-      Scheduler.post("SET_DEBUG", [OS.Shared.DEBUG]);
+      Scheduler.post("SET_DEBUG", [SharedAll.Config.DEBUG]);
     }
   }, false);
-OS.Shared.DEBUG = readDebugPref(PREF_OSFILE_LOG, false);
+SharedAll.Config.DEBUG = readDebugPref(PREF_OSFILE_LOG, false);
 
 Services.prefs.addObserver(PREF_OSFILE_LOG_REDIRECT,
   function prefObserver(aSubject, aTopic, aData) {
-    OS.Shared.TEST = readDebugPref(PREF_OSFILE_LOG_REDIRECT, OS.Shared.TEST);
+    SharedAll.Config.TEST = readDebugPref(PREF_OSFILE_LOG_REDIRECT, OS.Shared.TEST);
   }, false);
-OS.Shared.TEST = readDebugPref(PREF_OSFILE_LOG_REDIRECT, false);
+SharedAll.Config.TEST = readDebugPref(PREF_OSFILE_LOG_REDIRECT, false);
 
 // Update worker's DEBUG flag if it's true.
 // Don't start the worker just for this, though.
-if (OS.Shared.DEBUG && Scheduler.launched) {
+if (SharedAll.Config.DEBUG && Scheduler.launched) {
   Scheduler.post("SET_DEBUG", [true]);
 }
 
 // Observer topics used for monitoring shutdown
 const WEB_WORKERS_SHUTDOWN_TOPIC = "web-workers-shutdown";
 
 // Preference used to configure test shutdown observer.
 const PREF_OSFILE_TEST_SHUTDOWN_OBSERVER =
@@ -503,16 +498,45 @@ File.open = function open(path, mode, op
   ).then(
     function onSuccess(msg) {
       return new File(msg);
     }
   );
 };
 
 /**
+ * Creates and opens a file with a unique name. By default, generate a random HEX number and use it to create a unique new file name.
+ *
+ * @param {string} path The path to the file.
+ * @param {*=} options Additional options for file opening. This
+ * implementation interprets the following fields:
+ *
+ * - {number} humanReadable If |true|, create a new filename appending a decimal number. ie: filename-1.ext, filename-2.ext.
+ *  If |false| use HEX numbers ie: filename-A65BC0.ext
+ * - {number} maxReadableNumber Used to limit the amount of tries after a failed
+ *  file creation. Default is 20.
+ *
+ * @return {Object} contains A file object{file} and the path{path}.
+ * @throws {OS.File.Error} If the file could not be opened.
+ */
+File.openUnique = function openUnique(path, options) {
+  return Scheduler.post(
+      "openUnique", [Type.path.toMsg(path), options],
+      path
+    ).then(
+    function onSuccess(msg) {
+      return {
+        path: msg.path,
+        file: new File(msg.file)
+      };
+    }
+  );
+};
+
+/**
  * Get the information on the file.
  *
  * @return {promise}
  * @resolves {OS.File.Info}
  * @rejects {OS.Error}
  */
 File.stat = function stat(path) {
   return Scheduler.post(
@@ -764,23 +788,17 @@ File.Info = function Info(value) {
   // prototype defines getters for all of these fields.
   for (let k in value) {
     if (k != "creationDate") {
       Object.defineProperty(this, k, {value: value[k]});
     }
   }
   Object.defineProperty(this, "_deprecatedCreationDate", {value: value["creationDate"]});
 };
-if (OS.Constants.Win) {
-  File.Info.prototype = Object.create(OS.Shared.Win.AbstractInfo.prototype);
-} else if (OS.Constants.libc) {
-  File.Info.prototype = Object.create(OS.Shared.Unix.AbstractInfo.prototype);
-} else {
-  throw new Error("I am neither under Windows nor under a Posix system");
-}
+File.Info.prototype = SysAll.AbstractInfo.prototype;
 
 // Deprecated
 Object.defineProperty(File.Info.prototype, "creationDate", {
   get: function creationDate() {
     Deprecated.warning("Field 'creationDate' is deprecated.", "https://developer.mozilla.org/en-US/docs/JavaScript_OS.File/OS.File.Info#Cross-platform_Attributes");
     return this._deprecatedCreationDate;
   }
 });
@@ -966,36 +984,45 @@ DirectoryIterator.prototype = {
       }
     );
   }
 };
 
 DirectoryIterator.Entry = function Entry(value) {
   return value;
 };
-if (OS.Constants.Win) {
-  DirectoryIterator.Entry.prototype = Object.create(OS.Shared.Win.AbstractEntry.prototype);
-} else if (OS.Constants.libc) {
-  DirectoryIterator.Entry.prototype = Object.create(OS.Shared.Unix.AbstractEntry.prototype);
-} else {
-  throw new Error("I am neither under Windows nor under a Posix system");
-}
+DirectoryIterator.Entry.prototype = Object.create(SysAll.AbstractEntry.prototype);
 
 DirectoryIterator.Entry.fromMsg = function fromMsg(value) {
   return new DirectoryIterator.Entry(value);
 };
 
 // Constants
-Object.defineProperty(File, "POS_START", {value: OS.Shared.POS_START});
-Object.defineProperty(File, "POS_CURRENT", {value: OS.Shared.POS_CURRENT});
-Object.defineProperty(File, "POS_END", {value: OS.Shared.POS_END});
+File.POS_START = SysAll.POS_START;
+File.POS_CURRENT = SysAll.POS_CURRENT;
+File.POS_END = SysAll.POS_END;
+
+// Exports
+File.Error = OSError;
+File.DirectoryIterator = DirectoryIterator;
 
+this.OS = {};
 OS.File = File;
-OS.File.Error = OSError;
-OS.File.DirectoryIterator = DirectoryIterator;
+OS.Constants = SharedAll.Constants;
+OS.Shared = {
+  LOG: SharedAll.LOG,
+  Type: SysAll.Type,
+  get DEBUG() {
+    return SharedAll.Config.DEBUG;
+  },
+  set DEBUG(x) {
+    return SharedAll.Config.DEBUG = x;
+  }
+};
+Object.freeze(OS.Shared);
 OS.Path = Path;
 
 
 // Auto-flush OS.File during profile-before-change. This ensures that any I/O
 // that has been queued *before* profile-before-change is properly completed.
 // To ensure that I/O queued *during* profile-before-change is completed,
 // clients should register using AsyncShutdown.addBlocker.
 AsyncShutdown.profileBeforeChange.addBlocker(
--- a/toolkit/components/osfile/modules/osfile_async_worker.js
+++ b/toolkit/components/osfile/modules/osfile_async_worker.js
@@ -9,17 +9,18 @@ if (this.Components) {
 
 // Worker thread for osfile asynchronous front-end
 
 (function(exports) {
   "use strict";
 
   importScripts("resource://gre/modules/osfile.jsm");
 
-  let LOG = exports.OS.Shared.LOG.bind(exports.OS.Shared.LOG, "Agent");
+  let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+  let LOG = SharedAll.LOG.bind(SharedAll, "Agent");
 
  /**
   * Communications with the controller.
   *
   * Accepts messages:
   * {fun:function_name, args:array_of_arguments_or_null, id:id}
   *
   * Sends messages:
@@ -211,22 +212,22 @@ if (this.Components) {
   *
   * It is in charge of performing method-specific deserialization
   * of messages, calling the function/method of OS.File and serializing
   * back the results.
   */
   let Agent = {
    // Update worker's OS.Shared.DEBUG flag message from controller.
    SET_DEBUG: function SET_DEBUG (aDEBUG) {
-     exports.OS.Shared.DEBUG = aDEBUG;
+     SharedAll.Config.DEBUG = aDEBUG;
    },
    // Return worker's current OS.Shared.DEBUG value to controller.
    // Note: This is used for testing purposes.
    GET_DEBUG: function GET_DEBUG () {
-     return exports.OS.Shared.DEBUG;
+     return SharedAll.Config.DEBUG;
    },
    // Report file descriptors leaks.
    System_shutdown: function System_shutdown () {
      // Return information about both opened files and opened
      // directory iterators.
      return {
        openedFiles: OpenedFiles.listOpenedResources(),
        openedDirectoryIterators:
@@ -265,16 +266,30 @@ if (this.Components) {
      let filePath = Type.path.fromMsg(path);
      let file = File.open(filePath, mode, options);
      return OpenedFiles.add(file, {
        // Adding path information to keep track of opened files
        // to report leaks when debugging.
        path: filePath
      });
    },
+   openUnique: function openUnique(path, options) {
+     let filePath = Type.path.fromMsg(path);
+     let openedFile = OS.Shared.AbstractFile.openUnique(filePath, options);
+     let resourceId = OpenedFiles.add(openedFile.file, {
+       // Adding path information to keep track of opened files
+       // to report leaks when debugging.
+       path: openedFile.path
+     });
+
+     return {
+       path: openedFile.path,
+       file: resourceId
+     };
+   },
    read: function read(path, bytes, options) {
      let data = File.read(Type.path.fromMsg(path), bytes, options);
      return new Transfer({buffer: data.buffer, byteOffset: data.byteOffset, byteLength: data.byteLength}, [data.buffer]);
    },
    exists: function exists(path) {
      return File.exists(Type.path.fromMsg(path));
    },
    writeAtomic: function writeAtomic(path, buffer, options) {
--- a/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm
+++ b/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm
@@ -1021,21 +1021,16 @@ exports.OS = {
     Error: OSError,
     declareFFI: declareFFI,
     projectValue: projectValue,
     isTypedArray: isTypedArray,
     defineLazyGetter: defineLazyGetter,
     offsetBy: offsetBy
   }
 };
-if (exports.Constants.Win) {
-  exports.OS.Win = {};
-} else {
-  exports.OS.Unix = {};
-}
 
 Object.defineProperty(exports.OS.Shared, "DEBUG", {
   get: function() {
     return Config.DEBUG;
   },
   set: function(x) {
     return Config.DEBUG = x;
   }
--- a/toolkit/components/osfile/modules/osfile_shared_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_shared_front.jsm
@@ -9,20 +9,21 @@
  * be executed only on a worker thread.
  */
 
 if (typeof Components != "undefined") {
   throw new Error("osfile_shared_front.jsm cannot be used from the main thread");
 }
 (function(exports) {
 
-exports.OS = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm").OS;
+let SharedAll =
+  require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
 
-let LOG = exports.OS.Shared.LOG.bind(OS.Shared, "Shared front-end");
-let clone = exports.OS.Shared.clone;
+let LOG = SharedAll.LOG.bind(SharedAll, "Shared front-end");
+let clone = SharedAll.clone;
 
 /**
  * Code shared by implementations of File.
  *
  * @param {*} fd An OS-specific file handle.
  * @constructor
  */
 let AbstractFile = function AbstractFile(fd) {
@@ -84,17 +85,17 @@ AbstractFile.prototype = {
     let {ptr, bytes} = AbstractFile.normalizeToPointer(buffer, options.bytes);
     let pos = 0;
     while (pos < bytes) {
       let chunkSize = this._read(ptr, bytes - pos, options);
       if (chunkSize == 0) {
         break;
       }
       pos += chunkSize;
-      ptr = exports.OS.Shared.offsetBy(ptr, chunkSize);
+      ptr = SharedAll.offsetBy(ptr, chunkSize);
     }
 
     return pos;
   },
 
   /**
    * Write bytes from a buffer to this file.
    *
@@ -116,23 +117,81 @@ AbstractFile.prototype = {
 
     let {ptr, bytes} =
       AbstractFile.normalizeToPointer(buffer, options.bytes || undefined);
 
     let pos = 0;
     while (pos < bytes) {
       let chunkSize = this._write(ptr, bytes - pos, options);
       pos += chunkSize;
-      ptr = exports.OS.Shared.offsetBy(ptr, chunkSize);
+      ptr = SharedAll.offsetBy(ptr, chunkSize);
     }
     return pos;
   }
 };
 
 /**
+ * Creates and opens a file with a unique name. By default, generate a random HEX number and use it to create a unique new file name.
+ *
+ * @param {string} path The path to the file.
+ * @param {*=} options Additional options for file opening. This
+ * implementation interprets the following fields:
+ *
+ * - {number} humanReadable If |true|, create a new filename appending a decimal number. ie: filename-1.ext, filename-2.ext.
+ *  If |false| use HEX numbers ie: filename-A65BC0.ext
+ * - {number} maxReadableNumber Used to limit the amount of tries after a failed
+ *  file creation. Default is 20.
+ *
+ * @return {Object} contains A file object{file} and the path{path}.
+ * @throws {OS.File.Error} If the file could not be opened.
+ */
+AbstractFile.openUnique = function openUnique(path, options = {}) {
+  let mode = {
+    create : true
+  };
+
+  let dirName = OS.Path.dirname(path);
+  let leafName = OS.Path.basename(path);
+  let lastDotCharacter = leafName.lastIndexOf('.');
+  let fileName = leafName.substring(0, lastDotCharacter != -1 ? lastDotCharacter : leafName.length);
+  let suffix = (lastDotCharacter != -1 ? leafName.substring(lastDotCharacter) : "");
+  let uniquePath = "";
+  let maxAttempts = options.maxAttempts || 99;
+  let humanReadable = !!options.humanReadable;
+  const HEX_RADIX = 16;
+  // We produce HEX numbers between 0 and 2^24 - 1.
+  const MAX_HEX_NUMBER = 16777215;
+
+  try {
+    return {
+      path: path,
+      file: OS.File.open(path, mode)
+    };
+  } catch (ex if ex instanceof OS.File.Error && ex.becauseExists) {
+    for (let i = 0; i < maxAttempts; ++i) {
+      try {
+        if (humanReadable) {
+          uniquePath = OS.Path.join(dirName, fileName + "-" + (i + 1) + suffix);
+        } else {
+          let hexNumber = Math.floor(Math.random() * MAX_HEX_NUMBER).toString(HEX_RADIX);
+          uniquePath = OS.Path.join(dirName, fileName + "-" + hexNumber + suffix);
+        }
+        return {
+          path: uniquePath,
+          file: OS.File.open(uniquePath, mode)
+        };
+      } catch (ex if ex instanceof OS.File.Error && ex.becauseExists) {
+        // keep trying ...
+      }
+    }
+    throw OS.File.Error.exists("could not find an unused file name.");
+  }
+};
+
+/**
  * Utility function used to normalize a Typed Array or C
  * pointer into a uint8_t C pointer.
  *
  * Future versions might extend this to other data structures.
  *
  * @param {Typed array | C pointer} candidate The buffer. If
  * a C pointer, it must be non-null.
  * @param {number} bytes The number of bytes that |candidate| should contain.
@@ -145,23 +204,23 @@ AbstractFile.normalizeToPointer = functi
   if (!candidate) {
     throw new TypeError("Expecting  a Typed Array or a C pointer");
   }
   let ptr;
   if ("isNull" in candidate) {
     if (candidate.isNull()) {
       throw new TypeError("Expecting a non-null pointer");
     }
-    ptr = exports.OS.Shared.Type.uint8_t.out_ptr.cast(candidate);
+    ptr = SharedAll.Type.uint8_t.out_ptr.cast(candidate);
     if (bytes == null) {
       throw new TypeError("C pointer missing bytes indication.");
     }
-  } else if (exports.OS.Shared.isTypedArray(candidate)) {
+  } else if (SharedAll.isTypedArray(candidate)) {
     // Typed Array
-    ptr = exports.OS.Shared.Type.uint8_t.out_ptr.implementation(candidate.buffer);
+    ptr = SharedAll.Type.uint8_t.out_ptr.implementation(candidate.buffer);
     if (bytes == null) {
       bytes = candidate.byteLength;
     } else if (candidate.byteLength < bytes) {
       throw new TypeError("Buffer is too short. I need at least " +
                          bytes +
                          " bytes but I have only " +
                          candidate.byteLength +
                           "bytes");
@@ -425,10 +484,13 @@ AbstractFile.removeDir = function(path, 
     }
   } finally {
     iterator.close();
   }
 
   OS.File.removeEmptyDir(path);
 };
 
-   exports.OS.Shared.AbstractFile = AbstractFile;
+if (!exports.OS.Shared) {
+  exports.OS.Shared = {};
+}
+exports.OS.Shared.AbstractFile = AbstractFile;
 })(this);
--- a/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm
+++ b/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm
@@ -9,328 +9,344 @@
  *
  * It serves the following purposes:
  * - open libc;
  * - define OS.Unix.Error;
  * - define a few constants and types that need to be defined on all platforms.
  *
  * This module can be:
  * - opened from the main thread as a jsm module;
- * - opened from a chrome worker through importScripts.
+ * - opened from a chrome worker through require().
  */
 
+"use strict";
+
 let SharedAll;
 if (typeof Components != "undefined") {
+  let Cu = Components.utils;
   // Module is opened as a jsm module
-  this.EXPORTED_SYMBOLS = ["OS"];
-  Components.utils.import("resource://gre/modules/ctypes.jsm");
+  Cu.import("resource://gre/modules/ctypes.jsm", this);
 
   SharedAll = {};
-  Components.utils.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", SharedAll);
+  Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", SharedAll);
+  this.exports = {};
+} else if (typeof "module" != "undefined" && typeof "require" != "undefined") {
+  // Module is loaded with require()
+  SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
 } else {
-  SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+  throw new Error("Please open this module with Component.utils.import or with require()");
+}
+
+let LOG = SharedAll.LOG.bind(SharedAll, "Unix", "allthreads");
+let Const = SharedAll.Constants.libc;
+
+// Open libc
+let libc;
+let libc_candidates =  [ "libSystem.B.dylib",
+                         "libc.so.6",
+                         "libc.so" ];
+for (let i = 0; i < libc_candidates.length; ++i) {
+  try {
+    libc = ctypes.open(libc_candidates[i]);
+    break;
+  } catch (x) {
+    LOG("Could not open libc ", libc_candidates[i]);
+  }
 }
 
-(function(exports) {
-  "use strict";
-  if ("OS" in exports && "Shared" in exports.OS && "Unix" in exports.OS.Shared) {
-    // Avoid double inclusion
-    return;
-  }
+if (!libc) {
+  // Note: If you change the string here, please adapt tests accordingly
+  throw new Error("Could not open system library: no libc");
+}
+exports.libc = libc;
 
-  exports.OS = SharedAll.OS;
+// Define declareFFI
+let declareFFI = SharedAll.declareFFI.bind(null, libc);
+exports.declareFFI = declareFFI;
 
-  exports.OS.Shared.Unix = {};
-
-  let LOG = OS.Shared.LOG.bind(OS.Shared, "Unix", "allthreads");
+// Define Error
+let strerror = libc.declare("strerror",
+  ctypes.default_abi,
+  /*return*/ ctypes.char.ptr,
+  /*errnum*/ ctypes.int);
 
-  // Open libc
-  let libc;
-  let libc_candidates =  [ "libSystem.B.dylib",
-                           "libc.so.6",
-                           "libc.so" ];
-  for (let i = 0; i < libc_candidates.length; ++i) {
-    try {
-      libc = ctypes.open(libc_candidates[i]);
-      break;
-    } catch (x) {
-      LOG("Could not open libc ", libc_candidates[i]);
-    }
-  }
-  if (!libc) {
-    // Note: If you change the string here, please adapt tests accordingly
-    throw new Error("Could not open system library: no libc");
-  }
-  exports.OS.Shared.Unix.libc = libc;
+/**
+ * A File-related error.
+ *
+ * To obtain a human-readable error message, use method |toString|.
+ * To determine the cause of the error, use the various |becauseX|
+ * getters. To determine the operation that failed, use field
+ * |operation|.
+ *
+ * Additionally, this implementation offers a field
+ * |unixErrno|, which holds the OS-specific error
+ * constant. If you need this level of detail, you may match the value
+ * of this field against the error constants of |OS.Constants.libc|.
+ *
+ * @param {string=} operation The operation that failed. If unspecified,
+ * the name of the calling function is taken to be the operation that
+ * failed.
+ * @param {number=} lastError The OS-specific constant detailing the
+ * reason of the error. If unspecified, this is fetched from the system
+ * status.
+ *
+ * @constructor
+ * @extends {OS.Shared.Error}
+ */
+let OSError = function OSError(operation, errno) {
+  operation = operation || "unknown operation";
+  SharedAll.OSError.call(this, operation);
+  this.unixErrno = errno || ctypes.errno;
+};
+OSError.prototype = Object.create(SharedAll.OSError.prototype);
+OSError.prototype.toString = function toString() {
+  return "Unix error " + this.unixErrno +
+    " during operation " + this.operation +
+    " (" + strerror(this.unixErrno).readString() + ")";
+};
 
-  // Define declareFFI
-  let declareFFI = OS.Shared.declareFFI.bind(null, libc);
-  exports.OS.Shared.Unix.declareFFI = declareFFI;
-
-  // Define Error
-  let strerror = libc.declare("strerror",
-    ctypes.default_abi,
-    /*return*/ ctypes.char.ptr,
-    /*errnum*/ ctypes.int);
+/**
+ * |true| if the error was raised because a file or directory
+ * already exists, |false| otherwise.
+ */
+Object.defineProperty(OSError.prototype, "becauseExists", {
+  get: function becauseExists() {
+    return this.unixErrno == Const.EEXIST;
+  }
+});
+/**
+ * |true| if the error was raised because a file or directory
+ * does not exist, |false| otherwise.
+ */
+Object.defineProperty(OSError.prototype, "becauseNoSuchFile", {
+  get: function becauseNoSuchFile() {
+    return this.unixErrno == Const.ENOENT;
+  }
+});
 
-  /**
-   * A File-related error.
-   *
-   * To obtain a human-readable error message, use method |toString|.
-   * To determine the cause of the error, use the various |becauseX|
-   * getters. To determine the operation that failed, use field
-   * |operation|.
-   *
-   * Additionally, this implementation offers a field
-   * |unixErrno|, which holds the OS-specific error
-   * constant. If you need this level of detail, you may match the value
-   * of this field against the error constants of |OS.Constants.libc|.
-   *
-   * @param {string=} operation The operation that failed. If unspecified,
-   * the name of the calling function is taken to be the operation that
-   * failed.
-   * @param {number=} lastError The OS-specific constant detailing the
-   * reason of the error. If unspecified, this is fetched from the system
-   * status.
-   *
-   * @constructor
-   * @extends {OS.Shared.Error}
-   */
-  let OSError = function OSError(operation, errno) {
-    operation = operation || "unknown operation";
-    exports.OS.Shared.Error.call(this, operation);
-    this.unixErrno = errno || ctypes.errno;
+/**
+ * |true| if the error was raised because a directory is not empty
+ * does not exist, |false| otherwise.
+ */
+ Object.defineProperty(OSError.prototype, "becauseNotEmpty", {
+   get: function becauseNotEmpty() {
+     return this.unixErrno == Const.ENOTEMPTY;
+   }
+ });
+/**
+ * |true| if the error was raised because a file or directory
+ * is closed, |false| otherwise.
+ */
+Object.defineProperty(OSError.prototype, "becauseClosed", {
+  get: function becauseClosed() {
+    return this.unixErrno == Const.EBADF;
+  }
+});
+/**
+ * |true| if the error was raised because permission is denied to
+ * access a file or directory, |false| otherwise.
+ */
+Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
+  get: function becauseAccessDenied() {
+    return this.unixErrno == Const.EACCES;
+  }
+});
+
+/**
+ * Serialize an instance of OSError to something that can be
+ * transmitted across threads (not necessarily a string).
+ */
+OSError.toMsg = function toMsg(error) {
+  return {
+    operation: error.operation,
+    unixErrno: error.unixErrno
   };
-  OSError.prototype = new exports.OS.Shared.Error();
-  OSError.prototype.toString = function toString() {
-    return "Unix error " + this.unixErrno +
-      " during operation " + this.operation +
-      " (" + strerror(this.unixErrno).readString() + ")";
-  };
+};
+
+/**
+ * Deserialize a message back to an instance of OSError
+ */
+OSError.fromMsg = function fromMsg(msg) {
+  return new OSError(msg.operation, msg.unixErrno);
+};
+exports.Error = OSError;
 
-  /**
-   * |true| if the error was raised because a file or directory
-   * already exists, |false| otherwise.
-   */
-  Object.defineProperty(OSError.prototype, "becauseExists", {
-    get: function becauseExists() {
-      return this.unixErrno == OS.Constants.libc.EEXIST;
-    }
-  });
-  /**
-   * |true| if the error was raised because a file or directory
-   * does not exist, |false| otherwise.
-   */
-  Object.defineProperty(OSError.prototype, "becauseNoSuchFile", {
-    get: function becauseNoSuchFile() {
-      return this.unixErrno == OS.Constants.libc.ENOENT;
-    }
-  });
+/**
+ * Code shared by implementations of File.Info on Unix
+ *
+ * @constructor
+*/
+let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, lastAccessDate,
+                                         lastModificationDate, unixLastStatusChangeDate,
+                                         unixOwner, unixGroup, unixMode) {
+  this._isDir = isDir;
+  this._isSymlLink = isSymLink;
+  this._size = size;
+  this._lastAccessDate = lastAccessDate;
+  this._lastModificationDate = lastModificationDate;
+  this._unixLastStatusChangeDate = unixLastStatusChangeDate;
+  this._unixOwner = unixOwner;
+  this._unixGroup = unixGroup;
+  this._unixMode = unixMode;
+};
 
-  /**
-   * |true| if the error was raised because a directory is not empty
-   * does not exist, |false| otherwise.
-   */
-   Object.defineProperty(OSError.prototype, "becauseNotEmpty", {
-     get: function becauseNotEmpty() {
-       return this.unixErrno == OS.Constants.libc.ENOTEMPTY;
-     }
-   });
+AbstractInfo.prototype = {
   /**
-   * |true| if the error was raised because a file or directory
-   * is closed, |false| otherwise.
+   * |true| if this file is a directory, |false| otherwise
    */
-  Object.defineProperty(OSError.prototype, "becauseClosed", {
-    get: function becauseClosed() {
-      return this.unixErrno == OS.Constants.libc.EBADF;
-    }
-  });
-  /**
-   * |true| if the error was raised because permission is denied to
-   * access a file or directory, |false| otherwise.
-   */
-  Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
-    get: function becauseAccessDenied() {
-      return this.unixErrno == OS.Constants.libc.EACCES;
-    }
-  });
-
+  get isDir() {
+    return this._isDir;
+  },
   /**
-   * Serialize an instance of OSError to something that can be
-   * transmitted across threads (not necessarily a string).
+   * |true| if this file is a symbolink link, |false| otherwise
    */
-  OSError.toMsg = function toMsg(error) {
-    return {
-      operation: error.operation,
-      unixErrno: error.unixErrno
-    };
-  };
-
+  get isSymLink() {
+    return this._isSymlLink;
+  },
   /**
-   * Deserialize a message back to an instance of OSError
-   */
-  OSError.fromMsg = function fromMsg(msg) {
-    return new OSError(msg.operation, msg.unixErrno);
-  };
-
-  exports.OS.Shared.Unix.Error = OSError;
-
-  /**
-   * Code shared by implementations of File.Info on Unix
+   * The size of the file, in bytes.
    *
-   * @constructor
-  */
-  let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, lastAccessDate,
-                                           lastModificationDate, unixLastStatusChangeDate,
-                                           unixOwner, unixGroup, unixMode) {
-    this._isDir = isDir;
-    this._isSymlLink = isSymLink;
-    this._size = size;
-    this._lastAccessDate = lastAccessDate;
-    this._lastModificationDate = lastModificationDate;
-    this._unixLastStatusChangeDate = unixLastStatusChangeDate;
-    this._unixOwner = unixOwner;
-    this._unixGroup = unixGroup;
-    this._unixMode = unixMode;
-  };
+   * Note that the result may be |NaN| if the size of the file cannot be
+   * represented in JavaScript.
+   *
+   * @type {number}
+   */
+  get size() {
+    return this._size;
+  },
+  /**
+   * The date of last access to this file.
+   *
+   * Note that the definition of last access may depend on the
+   * underlying operating system and file system.
+   *
+   * @type {Date}
+   */
+  get lastAccessDate() {
+    return this._lastAccessDate;
+  },
+  /**
+   * Return the date of last modification of this file.
+   */
+  get lastModificationDate() {
+    return this._lastModificationDate;
+  },
+  /**
+   * Return the date at which the status of this file was last modified
+   * (this is the date of the latest write/renaming/mode change/...
+   * of the file)
+   */
+  get unixLastStatusChangeDate() {
+    return this._unixLastStatusChangeDate;
+  },
+  /*
+   * Return the Unix owner of this file
+   */
+  get unixOwner() {
+    return this._unixOwner;
+  },
+  /*
+   * Return the Unix group of this file
+   */
+  get unixGroup() {
+    return this._unixGroup;
+  },
+  /*
+   * Return the Unix group of this file
+   */
+  get unixMode() {
+    return this._unixMode;
+  }
+};
+exports.AbstractInfo = AbstractInfo;
+
+/**
+ * Code shared by implementations of File.DirectoryIterator.Entry on Unix
+ *
+ * @constructor
+*/
+let AbstractEntry = function AbstractEntry(isDir, isSymLink, name, path) {
+  this._isDir = isDir;
+  this._isSymlLink = isSymLink;
+  this._name = name;
+  this._path = path;
+};
 
-  AbstractInfo.prototype = {
-    /**
-     * |true| if this file is a directory, |false| otherwise
-     */
-    get isDir() {
-      return this._isDir;
-    },
-    /**
-     * |true| if this file is a symbolink link, |false| otherwise
-     */
-    get isSymLink() {
-      return this._isSymlLink;
-    },
-    /**
-     * The size of the file, in bytes.
-     *
-     * Note that the result may be |NaN| if the size of the file cannot be
-     * represented in JavaScript.
-     *
-     * @type {number}
-     */
-    get size() {
-      return this._size;
-    },
-    /**
-     * The date of last access to this file.
-     *
-     * Note that the definition of last access may depend on the
-     * underlying operating system and file system.
-     *
-     * @type {Date}
-     */
-    get lastAccessDate() {
-      return this._lastAccessDate;
-    },
-    /**
-     * Return the date of last modification of this file.
-     */
-    get lastModificationDate() {
-      return this._lastModificationDate;
-    },
-    /**
-     * Return the date at which the status of this file was last modified
-     * (this is the date of the latest write/renaming/mode change/...
-     * of the file)
-     */
-    get unixLastStatusChangeDate() {
-      return this._unixLastStatusChangeDate;
-    },
-    /*
-     * Return the Unix owner of this file
-     */
-    get unixOwner() {
-      return this._unixOwner;
-    },
-    /*
-     * Return the Unix group of this file
-     */
-    get unixGroup() {
-      return this._unixGroup;
-    },
-    /*
-     * Return the Unix group of this file
-     */
-    get unixMode() {
-      return this._unixMode;
-    }
-  };
-  exports.OS.Shared.Unix.AbstractInfo = AbstractInfo;
-
+AbstractEntry.prototype = {
+  /**
+   * |true| if the entry is a directory, |false| otherwise
+   */
+  get isDir() {
+    return this._isDir;
+  },
+  /**
+   * |true| if the entry is a directory, |false| otherwise
+   */
+  get isSymLink() {
+    return this._isSymlLink;
+  },
+  /**
+   * The name of the entry
+   * @type {string}
+   */
+  get name() {
+    return this._name;
+  },
   /**
-   * Code shared by implementations of File.DirectoryIterator.Entry on Unix
-   *
-   * @constructor
-  */
-  let AbstractEntry = function AbstractEntry(isDir, isSymLink, name, path) {
-    this._isDir = isDir;
-    this._isSymlLink = isSymLink;
-    this._name = name;
-    this._path = path;
-  };
+   * The full path to the entry
+   */
+  get path() {
+    return this._path;
+  }
+};
+exports.AbstractEntry = AbstractEntry;
+
+// Special constants that need to be defined on all platforms
+
+exports.POS_START = Const.SEEK_SET;
+exports.POS_CURRENT = Const.SEEK_CUR;
+exports.POS_END = Const.SEEK_END;
+
+// Special types that need to be defined for communication
+// between threads
+let Type = Object.create(SharedAll.Type);
+exports.Type = Type;
+
+/**
+ * Native paths
+ *
+ * Under Unix, expressed as C strings
+ */
+Type.path = Type.cstring.withName("[in] path");
+Type.out_path = Type.out_cstring.withName("[out] path");
 
-  AbstractEntry.prototype = {
-    /**
-     * |true| if the entry is a directory, |false| otherwise
-     */
-    get isDir() {
-      return this._isDir;
-    },
-    /**
-     * |true| if the entry is a directory, |false| otherwise
-     */
-    get isSymLink() {
-      return this._isSymlLink;
-    },
-    /**
-     * The name of the entry
-     * @type {string}
-     */
-    get name() {
-      return this._name;
-    },
-    /**
-     * The full path to the entry
-     */
-    get path() {
-      return this._path;
-    }
-  };
-  exports.OS.Shared.Unix.AbstractEntry = AbstractEntry;
+// Special constructors that need to be defined on all threads
+OSError.closed = function closed(operation) {
+  return new OSError(operation, Const.EBADF);
+};
+
+OSError.exists = function exists(operation) {
+  return new OSError(operation, Const.EEXIST);
+};
+
+OSError.noSuchFile = function noSuchFile(operation) {
+  return new OSError(operation, Const.ENOENT);
+};
 
-  // Special constants that need to be defined on all platforms
-
-   Object.defineProperty(exports.OS.Shared, "POS_START", { value: exports.OS.Constants.libc.SEEK_SET });
-   Object.defineProperty(exports.OS.Shared, "POS_CURRENT", { value: exports.OS.Constants.libc.SEEK_CUR });
-   Object.defineProperty(exports.OS.Shared, "POS_END", { value: exports.OS.Constants.libc.SEEK_END });
-
-  // Special types that need to be defined for communication
-  // between threads
-  let Types = exports.OS.Shared.Type;
+let EXPORTED_SYMBOLS = [
+  "declareFFI",
+  "libc",
+  "Error",
+  "AbstractInfo",
+  "AbstractEntry",
+  "Type",
+  "POS_START",
+  "POS_CURRENT",
+  "POS_END"
+];
 
-   /**
-    * Native paths
-    *
-    * Under Unix, expressed as C strings
-    */
-  Types.path = Types.cstring.withName("[in] path");
-  Types.out_path = Types.out_cstring.withName("[out] path");
-
-  // Special constructors that need to be defined on all threads
-  OSError.closed = function closed(operation) {
-    return new OSError(operation, OS.Constants.libc.EBADF);
-  };
-
-  OSError.exists = function exists(operation) {
-    return new OSError(operation, OS.Constants.libc.EEXIST);
-  };
-
-  OSError.noSuchFile = function noSuchFile(operation) {
-    return new OSError(operation, OS.Constants.libc.ENOENT);
-  };
-})(this);
+//////////// Boilerplate
+if (typeof Components != "undefined") {
+  this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS;
+  for (let symbol of EXPORTED_SYMBOLS) {
+    this[symbol] = exports[symbol];
+  }
+}
--- a/toolkit/components/osfile/modules/osfile_unix_back.jsm
+++ b/toolkit/components/osfile/modules/osfile_unix_back.jsm
@@ -12,621 +12,620 @@
     throw new Error("osfile_unix_back.jsm cannot be used from the main thread yet");
   }
   (function(exports) {
      "use strict";
      if (exports.OS && exports.OS.Unix && exports.OS.Unix.File) {
        return; // Avoid double initialization
      }
 
-     exports.OS = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm").OS;
-
-     exports.OS.Unix.File = {};
-
-     let LOG = exports.OS.Shared.LOG.bind(OS.Shared, "Unix", "back");
-     let libc = exports.OS.Shared.Unix.libc;
+     let SharedAll =
+       require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+     let SysAll =
+       require("resource://gre/modules/osfile/osfile_unix_allthreads.jsm");
+     let LOG = SharedAll.LOG.bind(SharedAll, "Unix", "back");
+     let libc = SysAll.libc;
+     let Const = SharedAll.Constants.libc;
 
      /**
       * Initialize the Unix module.
       *
       * @param {function=} declareFFI
       */
      // FIXME: Both |init| and |aDeclareFFI| are deprecated, we should remove them
      let init = function init(aDeclareFFI) {
        let declareFFI;
        if (aDeclareFFI) {
          declareFFI = aDeclareFFI.bind(null, libc);
        } else {
-         declareFFI = exports.OS.Shared.Unix.declareFFI;
+         declareFFI = SysAll.declareFFI;
        }
 
-       // Shorthands
-       let OSUnix = exports.OS.Unix;
-       let UnixFile = exports.OS.Unix.File;
-       if (!exports.OS.Types) {
-         exports.OS.Types = {};
-       }
-       let Type = exports.OS.Shared.Type;
-       let Types = Type;
-
        // Initialize types that require additional OS-specific
        // support - either finalization or matching against
        // OS-specific constants.
+       let Type = Object.create(SysAll.Type);
+       let SysFile = exports.OS.Unix.File = { Type: Type };
 
        /**
         * A file descriptor.
         */
-       Types.fd = Type.int.withName("fd");
-       Types.fd.importFromC = function importFromC(fd_int) {
+       Type.fd = Type.int.withName("fd");
+       Type.fd.importFromC = function importFromC(fd_int) {
          return ctypes.CDataFinalizer(fd_int, _close);
        };
 
 
        /**
         * A C integer holding -1 in case of error or a file descriptor
         * in case of success.
         */
-       Types.negativeone_or_fd = Types.fd.withName("negativeone_or_fd");
-       Types.negativeone_or_fd.importFromC =
+       Type.negativeone_or_fd = Type.fd.withName("negativeone_or_fd");
+       Type.negativeone_or_fd.importFromC =
          function importFromC(fd_int) {
            if (fd_int == -1) {
              return -1;
            }
            return ctypes.CDataFinalizer(fd_int, _close);
          };
 
        /**
         * A C integer holding -1 in case of error or a meaningless value
         * in case of success.
         */
-       Types.negativeone_or_nothing =
-         Types.int.withName("negativeone_or_nothing");
+       Type.negativeone_or_nothing =
+         Type.int.withName("negativeone_or_nothing");
 
        /**
         * A C integer holding -1 in case of error or a positive integer
         * in case of success.
         */
-       Types.negativeone_or_ssize_t =
-         Types.ssize_t.withName("negativeone_or_ssize_t");
+       Type.negativeone_or_ssize_t =
+         Type.ssize_t.withName("negativeone_or_ssize_t");
 
        /**
         * Various libc integer types
         */
-       Types.mode_t =
-         Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_MODE_T).withName("mode_t");
-       Types.uid_t =
-         Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_UID_T).withName("uid_t");
-       Types.gid_t =
-         Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_GID_T).withName("gid_t");
+       Type.mode_t =
+         Type.intn_t(Const.OSFILE_SIZEOF_MODE_T).withName("mode_t");
+       Type.uid_t =
+         Type.intn_t(Const.OSFILE_SIZEOF_UID_T).withName("uid_t");
+       Type.gid_t =
+         Type.intn_t(Const.OSFILE_SIZEOF_GID_T).withName("gid_t");
 
        /**
         * Type |time_t|
         */
-       Types.time_t =
-         Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_TIME_T).withName("time_t");
+       Type.time_t =
+         Type.intn_t(Const.OSFILE_SIZEOF_TIME_T).withName("time_t");
 
        // Structure |dirent|
        // Building this type is rather complicated, as its layout varies between
        // variants of Unix. For this reason, we rely on a number of constants
        // (computed in C from the C data structures) that give us the layout.
        // The structure we compute looks like
        //  { int8_t[...] before_d_type; // ignored content
        //    int8_t      d_type       ;
        //    int8_t[...] before_d_name; // ignored content
        //    char[...]   d_name;
        //    };
        {
          let d_name_extra_size = 0;
-         if (OS.Constants.libc.OSFILE_SIZEOF_DIRENT_D_NAME < 8) {
+         if (Const.OSFILE_SIZEOF_DIRENT_D_NAME < 8) {
            // d_name is defined like "char d_name[1];" on some platforms
            // (e.g. Solaris), we need to give it more size for our structure.
            d_name_extra_size = 256;
          }
 
-         let dirent = new OS.Shared.HollowStructure("dirent",
-           OS.Constants.libc.OSFILE_SIZEOF_DIRENT + d_name_extra_size);
-         if (OS.Constants.libc.OSFILE_OFFSETOF_DIRENT_D_TYPE != undefined) {
+         let dirent = new SharedAll.HollowStructure("dirent",
+           Const.OSFILE_SIZEOF_DIRENT + d_name_extra_size);
+         if (Const.OSFILE_OFFSETOF_DIRENT_D_TYPE != undefined) {
            // |dirent| doesn't have d_type on some platforms (e.g. Solaris).
-           dirent.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_DIRENT_D_TYPE,
+           dirent.add_field_at(Const.OSFILE_OFFSETOF_DIRENT_D_TYPE,
              "d_type", ctypes.uint8_t);
          }
-         dirent.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_DIRENT_D_NAME,
+         dirent.add_field_at(Const.OSFILE_OFFSETOF_DIRENT_D_NAME,
            "d_name", ctypes.ArrayType(ctypes.char,
-             OS.Constants.libc.OSFILE_SIZEOF_DIRENT_D_NAME + d_name_extra_size));
+             Const.OSFILE_SIZEOF_DIRENT_D_NAME + d_name_extra_size));
 
          // We now have built |dirent|.
-         Types.dirent = dirent.getType();
+         Type.dirent = dirent.getType();
        }
-       Types.null_or_dirent_ptr =
-         new Type("null_of_dirent",
-                  Types.dirent.out_ptr.implementation);
+       Type.null_or_dirent_ptr =
+         new SharedAll.Type("null_of_dirent",
+                  Type.dirent.out_ptr.implementation);
 
        // Structure |stat|
        // Same technique
        {
-         let stat = new OS.Shared.HollowStructure("stat",
-           OS.Constants.libc.OSFILE_SIZEOF_STAT);
-         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_MODE,
-                        "st_mode", Types.mode_t.implementation);
-         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_UID,
-                          "st_uid", Types.uid_t.implementation);
-         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_GID,
-                          "st_gid", Types.gid_t.implementation);
+         let stat = new SharedAll.HollowStructure("stat",
+           Const.OSFILE_SIZEOF_STAT);
+         stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_MODE,
+                        "st_mode", Type.mode_t.implementation);
+         stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_UID,
+                          "st_uid", Type.uid_t.implementation);
+         stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_GID,
+                          "st_gid", Type.gid_t.implementation);
 
          // Here, things get complicated with different data structures.
          // Some platforms have |time_t st_atime| and some platforms have
          // |timespec st_atimespec|. However, since |timespec| starts with
          // a |time_t|, followed by nanoseconds, we just cheat and pretend
          // that everybody has |time_t st_atime|, possibly followed by padding
-         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_ATIME,
-                          "st_atime", Types.time_t.implementation);
-         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_MTIME,
-                          "st_mtime", Types.time_t.implementation);
-         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_CTIME,
-                          "st_ctime", Types.time_t.implementation);
+         stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_ATIME,
+                          "st_atime", Type.time_t.implementation);
+         stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_MTIME,
+                          "st_mtime", Type.time_t.implementation);
+         stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_CTIME,
+                          "st_ctime", Type.time_t.implementation);
 
          // To complicate further, MacOS and some BSDs have a field |birthtime|
-         if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in OS.Constants.libc) {
-           stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_BIRTHTIME,
-                             "st_birthtime", Types.time_t.implementation);
+         if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in Const) {
+           stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_BIRTHTIME,
+                             "st_birthtime", Type.time_t.implementation);
          }
 
-         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_SIZE,
-                        "st_size", Types.size_t.implementation);
-         Types.stat = stat.getType();
+         stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_SIZE,
+                        "st_size", Type.size_t.implementation);
+         Type.stat = stat.getType();
        }
 
        // Structure |DIR|
-       if ("OSFILE_SIZEOF_DIR" in OS.Constants.libc) {
+       if ("OSFILE_SIZEOF_DIR" in Const) {
          // On platforms for which we need to access the fields of DIR
          // directly (e.g. because certain functions are implemented
          // as macros), we need to define DIR as a hollow structure.
-         let DIR = new OS.Shared.HollowStructure(
+         let DIR = new SharedAll.HollowStructure(
            "DIR",
-           OS.Constants.libc.OSFILE_SIZEOF_DIR);
+           Const.OSFILE_SIZEOF_DIR);
 
          DIR.add_field_at(
-           OS.Constants.libc.OSFILE_OFFSETOF_DIR_DD_FD,
+           Const.OSFILE_OFFSETOF_DIR_DD_FD,
            "dd_fd",
-           Types.fd.implementation);
+           Type.fd.implementation);
 
-         Types.DIR = DIR.getType();
+         Type.DIR = DIR.getType();
        } else {
          // On other platforms, we keep DIR as a blackbox
-         Types.DIR =
-           new Type("DIR",
+         Type.DIR =
+           new SharedAll.Type("DIR",
              ctypes.StructType("DIR"));
        }
 
-       Types.null_or_DIR_ptr =
-         Types.DIR.out_ptr.withName("null_or_DIR*");
-       Types.null_or_DIR_ptr.importFromC = function importFromC(dir) {
+       Type.null_or_DIR_ptr =
+         Type.DIR.out_ptr.withName("null_or_DIR*");
+       Type.null_or_DIR_ptr.importFromC = function importFromC(dir) {
          if (dir == null || dir.isNull()) {
            return null;
          }
          return ctypes.CDataFinalizer(dir, _close_dir);
        };
 
        // Declare libc functions as functions of |OS.Unix.File|
 
        // Finalizer-related functions
-       let _close = UnixFile._close =
+       let _close = SysFile._close =
          libc.declare("close", ctypes.default_abi,
                         /*return */ctypes.int,
                         /*fd*/     ctypes.int);
 
-       UnixFile.close = function close(fd) {
+       SysFile.close = function close(fd) {
          // Detach the finalizer and call |_close|.
          return fd.dispose();
        };
 
        let _close_dir =
          libc.declare("closedir", ctypes.default_abi,
                         /*return */ctypes.int,
-                        /*dirp*/   Types.DIR.in_ptr.implementation);
+                        /*dirp*/   Type.DIR.in_ptr.implementation);
 
-       UnixFile.closedir = function closedir(fd) {
+       SysFile.closedir = function closedir(fd) {
          // Detach the finalizer and call |_close_dir|.
          return fd.dispose();
        };
 
        {
          // Symbol free() is special.
          // We override the definition of free() on several platforms.
          let default_lib = libc;
          try {
            // On platforms for which we override free(), nspr defines
            // a special library name "a.out" that will resolve to the
            // correct implementation free().
            default_lib = ctypes.open("a.out");
 
-           UnixFile.free =
+           SysFile.free =
              default_lib.declare("free", ctypes.default_abi,
              /*return*/ ctypes.void_t,
              /*ptr*/    ctypes.voidptr_t);
 
          } catch (ex) {
            // We don't have an a.out library or a.out doesn't contain free.
            // Either way, use the ordinary libc free.
 
-           UnixFile.free =
+           SysFile.free =
              libc.declare("free", ctypes.default_abi,
              /*return*/ ctypes.void_t,
              /*ptr*/    ctypes.voidptr_t);
          }
       }
 
 
        // Other functions
-       UnixFile.access =
+       SysFile.access =
          declareFFI("access", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*path*/   Types.path,
-                    /*mode*/   Types.int);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*path*/   Type.path,
+                    /*mode*/   Type.int);
 
-       UnixFile.chdir =
+       SysFile.chdir =
          declareFFI("chdir", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*path*/   Types.path);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*path*/   Type.path);
 
-       UnixFile.chmod =
+       SysFile.chmod =
          declareFFI("chmod", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*path*/   Types.path,
-                    /*mode*/   Types.mode_t);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*path*/   Type.path,
+                    /*mode*/   Type.mode_t);
 
-       UnixFile.chown =
+       SysFile.chown =
          declareFFI("chown", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*path*/   Types.path,
-                    /*uid*/    Types.uid_t,
-                    /*gid*/    Types.gid_t);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*path*/   Type.path,
+                    /*uid*/    Type.uid_t,
+                    /*gid*/    Type.gid_t);
 
-       UnixFile.copyfile =
+       SysFile.copyfile =
          declareFFI("copyfile", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*source*/ Types.path,
-                    /*dest*/   Types.path,
-                    /*state*/  Types.void_t.in_ptr, // Ignored atm
-                    /*flags*/  Types.uint32_t);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*source*/ Type.path,
+                    /*dest*/   Type.path,
+                    /*state*/  Type.void_t.in_ptr, // Ignored atm
+                    /*flags*/  Type.uint32_t);
 
-       UnixFile.dup =
+       SysFile.dup =
          declareFFI("dup", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_fd,
-                    /*fd*/     Types.fd);
+                    /*return*/ Type.negativeone_or_fd,
+                    /*fd*/     Type.fd);
 
-       if ("OSFILE_SIZEOF_DIR" in OS.Constants.libc) {
+       if ("OSFILE_SIZEOF_DIR" in Const) {
          // On platforms for which |dirfd| is a macro
-         UnixFile.dirfd =
+         SysFile.dirfd =
            function dirfd(DIRp) {
-             return Types.DIR.in_ptr.implementation(DIRp).contents.dd_fd;
+             return Type.DIR.in_ptr.implementation(DIRp).contents.dd_fd;
            };
        } else {
          // On platforms for which |dirfd| is a function
-         UnixFile.dirfd =
+         SysFile.dirfd =
            declareFFI("dirfd", ctypes.default_abi,
-                      /*return*/ Types.negativeone_or_fd,
-                      /*dir*/    Types.DIR.in_ptr);
+                      /*return*/ Type.negativeone_or_fd,
+                      /*dir*/    Type.DIR.in_ptr);
        }
 
-       UnixFile.chdir =
+       SysFile.chdir =
          declareFFI("chdir", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*path*/   Types.path);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*path*/   Type.path);
 
-       UnixFile.fchdir =
+       SysFile.fchdir =
          declareFFI("fchdir", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*fd*/     Types.fd);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*fd*/     Type.fd);
 
-       UnixFile.fchown =
+       SysFile.fchown =
          declareFFI("fchown", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*fd*/     Types.fd,
-                    /*uid_t*/  Types.uid_t,
-                    /*gid_t*/  Types.gid_t);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*fd*/     Type.fd,
+                    /*uid_t*/  Type.uid_t,
+                    /*gid_t*/  Type.gid_t);
 
-       UnixFile.fsync =
+       SysFile.fsync =
          declareFFI("fsync", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*fd*/     Types.fd);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*fd*/     Type.fd);
 
-       UnixFile.getcwd =
+       SysFile.getcwd =
          declareFFI("getcwd", ctypes.default_abi,
-                    /*return*/ Types.out_path,
-                    /*buf*/    Types.out_path,
-                    /*size*/   Types.size_t);
+                    /*return*/ Type.out_path,
+                    /*buf*/    Type.out_path,
+                    /*size*/   Type.size_t);
 
-       UnixFile.getwd =
+       SysFile.getwd =
          declareFFI("getwd", ctypes.default_abi,
-                    /*return*/ Types.out_path,
-                    /*buf*/    Types.out_path);
+                    /*return*/ Type.out_path,
+                    /*buf*/    Type.out_path);
 
        // Two variants of |getwd| which allocate the memory
        // dynamically.
 
        // Linux/Android version
-       UnixFile.get_current_dir_name =
+       SysFile.get_current_dir_name =
          declareFFI("get_current_dir_name", ctypes.default_abi,
-                    /*return*/ Types.out_path.releaseWith(UnixFile.free));
+                    /*return*/ Type.out_path.releaseWith(SysFile.free));
 
        // MacOS/BSD version (will return NULL on Linux/Android)
-       UnixFile.getwd_auto =
+       SysFile.getwd_auto =
          declareFFI("getwd", ctypes.default_abi,
-                    /*return*/ Types.out_path.releaseWith(UnixFile.free),
-                    /*buf*/    Types.void_t.out_ptr);
+                    /*return*/ Type.out_path.releaseWith(SysFile.free),
+                    /*buf*/    Type.void_t.out_ptr);
 
-       UnixFile.fdatasync =
+       SysFile.fdatasync =
          declareFFI("fdatasync", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*fd*/     Types.fd); // Note: MacOS/BSD-specific
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*fd*/     Type.fd); // Note: MacOS/BSD-specific
 
-       UnixFile.ftruncate =
+       SysFile.ftruncate =
          declareFFI("ftruncate", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*fd*/     Types.fd,
-                    /*length*/ Types.off_t);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*fd*/     Type.fd,
+                    /*length*/ Type.off_t);
 
-       if (OS.Constants.libc._DARWIN_FEATURE_64_BIT_INODE) {
-         UnixFile.fstat =
+       if (Const._DARWIN_FEATURE_64_BIT_INODE) {
+         SysFile.fstat =
            declareFFI("fstat$INODE64", ctypes.default_abi,
-                      /*return*/ Types.negativeone_or_nothing,
-                      /*path*/   Types.fd,
-                      /*buf*/    Types.stat.out_ptr
+                      /*return*/ Type.negativeone_or_nothing,
+                      /*path*/   Type.fd,
+                      /*buf*/    Type.stat.out_ptr
                      );
        } else {
-         UnixFile.fstat =
+         SysFile.fstat =
            declareFFI("fstat", ctypes.default_abi,
-                      /*return*/ Types.negativeone_or_nothing,
-                      /*path*/   Types.fd,
-                      /*buf*/    Types.stat.out_ptr
+                      /*return*/ Type.negativeone_or_nothing,
+                      /*path*/   Type.fd,
+                      /*buf*/    Type.stat.out_ptr
                      );
        }
 
-       UnixFile.lchown =
+       SysFile.lchown =
          declareFFI("lchown", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*path*/   Types.path,
-                    /*uid_t*/  Types.uid_t,
-                    /*gid_t*/  Types.gid_t);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*path*/   Type.path,
+                    /*uid_t*/  Type.uid_t,
+                    /*gid_t*/  Type.gid_t);
 
-       UnixFile.link =
+       SysFile.link =
          declareFFI("link", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*source*/ Types.path,
-                    /*dest*/   Types.path);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*source*/ Type.path,
+                    /*dest*/   Type.path);
 
-       UnixFile.lseek =
+       SysFile.lseek =
          declareFFI("lseek", ctypes.default_abi,
-                    /*return*/ Types.off_t,
-                    /*fd*/     Types.fd,
-                    /*offset*/ Types.off_t,
-                    /*whence*/ Types.int);
+                    /*return*/ Type.off_t,
+                    /*fd*/     Type.fd,
+                    /*offset*/ Type.off_t,
+                    /*whence*/ Type.int);
 
-       UnixFile.mkdir =
+       SysFile.mkdir =
          declareFFI("mkdir", ctypes.default_abi,
-                    /*return*/ Types.int,
-                    /*path*/ Types.path,
-                    /*mode*/ Types.int);
+                    /*return*/ Type.int,
+                    /*path*/ Type.path,
+                    /*mode*/ Type.int);
 
-       UnixFile.mkstemp =
+       SysFile.mkstemp =
          declareFFI("mkstemp", ctypes.default_abi,
-                    /*return*/ Types.fd,
-                    /*template*/Types.out_path);
+                    /*return*/ Type.fd,
+                    /*template*/Type.out_path);
 
-       UnixFile.open =
+       SysFile.open =
          declareFFI("open", ctypes.default_abi,
-                    /*return*/Types.negativeone_or_fd,
-                    /*path*/  Types.path,
-                    /*oflags*/Types.int,
-                    /*mode*/  Types.int);
+                    /*return*/Type.negativeone_or_fd,
+                    /*path*/  Type.path,
+                    /*oflags*/Type.int,
+                    /*mode*/  Type.int);
 
-       UnixFile.opendir =
+       SysFile.opendir =
          declareFFI("opendir", ctypes.default_abi,
-                    /*return*/ Types.null_or_DIR_ptr,
-                    /*path*/   Types.path);
+                    /*return*/ Type.null_or_DIR_ptr,
+                    /*path*/   Type.path);
 
-       UnixFile.pread =
+       SysFile.pread =
          declareFFI("pread", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_ssize_t,
-                    /*fd*/     Types.fd,
-                    /*buf*/    Types.void_t.out_ptr,
-                    /*nbytes*/ Types.size_t,
-                    /*offset*/ Types.off_t);
+                    /*return*/ Type.negativeone_or_ssize_t,
+                    /*fd*/     Type.fd,
+                    /*buf*/    Type.void_t.out_ptr,
+                    /*nbytes*/ Type.size_t,
+                    /*offset*/ Type.off_t);
 
-       UnixFile.pwrite =
+       SysFile.pwrite =
          declareFFI("pwrite", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_ssize_t,
-                    /*fd*/     Types.fd,
-                    /*buf*/    Types.void_t.in_ptr,
-                    /*nbytes*/ Types.size_t,
-                    /*offset*/ Types.off_t);
+                    /*return*/ Type.negativeone_or_ssize_t,
+                    /*fd*/     Type.fd,
+                    /*buf*/    Type.void_t.in_ptr,
+                    /*nbytes*/ Type.size_t,
+                    /*offset*/ Type.off_t);
 
-       UnixFile.read =
+       SysFile.read =
          declareFFI("read", ctypes.default_abi,
-                    /*return*/Types.negativeone_or_ssize_t,
-                    /*fd*/    Types.fd,
-                    /*buf*/   Types.void_t.out_ptr,
-                    /*nbytes*/Types.size_t);
+                    /*return*/Type.negativeone_or_ssize_t,
+                    /*fd*/    Type.fd,
+                    /*buf*/   Type.void_t.out_ptr,
+                    /*nbytes*/Type.size_t);
 
-       UnixFile.posix_fadvise =
+       SysFile.posix_fadvise =
          declareFFI("posix_fadvise", ctypes.default_abi,
-                    /*return*/ Types.int,
-                    /*fd*/     Types.fd,
-                    /*offset*/ Types.off_t,
-                    /*len*/    Types.off_t,
-                    /*advise*/ Types.int);
+                    /*return*/ Type.int,
+                    /*fd*/     Type.fd,
+                    /*offset*/ Type.off_t,
+                    /*len*/    Type.off_t,
+                    /*advise*/ Type.int);
 
-       if (OS.Constants.libc._DARWIN_FEATURE_64_BIT_INODE) {
+       if (Const._DARWIN_FEATURE_64_BIT_INODE) {
          // Special case for MacOS X 10.5+
          // Symbol name "readdir" still exists but is used for a
          // deprecated function that does not match the
-         // constants of |OS.Constants.libc|.
-         UnixFile.readdir =
+         // constants of |Const|.
+         SysFile.readdir =
            declareFFI("readdir$INODE64", ctypes.default_abi,
-                     /*return*/Types.null_or_dirent_ptr,
-                      /*dir*/   Types.DIR.in_ptr); // For MacOS X
+                     /*return*/Type.null_or_dirent_ptr,
+                      /*dir*/   Type.DIR.in_ptr); // For MacOS X
        } else {
-         UnixFile.readdir =
+         SysFile.readdir =
            declareFFI("readdir", ctypes.default_abi,
-                      /*return*/Types.null_or_dirent_ptr,
-                      /*dir*/   Types.DIR.in_ptr); // Other Unices
+                      /*return*/Type.null_or_dirent_ptr,
+                      /*dir*/   Type.DIR.in_ptr); // Other Unices
        }
 
-       UnixFile.rename =
+       SysFile.rename =
          declareFFI("rename", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*old*/    Types.path,
-                    /*new*/    Types.path);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*old*/    Type.path,
+                    /*new*/    Type.path);
 
-       UnixFile.rmdir =
+       SysFile.rmdir =
          declareFFI("rmdir", ctypes.default_abi,
-                    /*return*/ Types.int,
-                    /*path*/   Types.path);
+                    /*return*/ Type.int,
+                    /*path*/   Type.path);
 
-       UnixFile.splice =
+       SysFile.splice =
          declareFFI("splice", ctypes.default_abi,
-                    /*return*/ Types.long,
-                    /*fd_in*/  Types.fd,
-                    /*off_in*/ Types.off_t.in_ptr,
-                    /*fd_out*/ Types.fd,
-                    /*off_out*/Types.off_t.in_ptr,
-                    /*len*/    Types.size_t,
-                    /*flags*/  Types.unsigned_int); // Linux/Android-specific
+                    /*return*/ Type.long,
+                    /*fd_in*/  Type.fd,
+                    /*off_in*/ Type.off_t.in_ptr,
+                    /*fd_out*/ Type.fd,
+                    /*off_out*/Type.off_t.in_ptr,
+                    /*len*/    Type.size_t,
+                    /*flags*/  Type.unsigned_int); // Linux/Android-specific
 
-       UnixFile.symlink =
+       SysFile.symlink =
          declareFFI("symlink", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*source*/ Types.path,
-                    /*dest*/   Types.path);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*source*/ Type.path,
+                    /*dest*/   Type.path);
 
-       UnixFile.truncate =
+       SysFile.truncate =
          declareFFI("truncate", ctypes.default_abi,
-                    /*return*/Types.negativeone_or_nothing,
-                    /*path*/  Types.path,
-                    /*length*/ Types.off_t);
+                    /*return*/Type.negativeone_or_nothing,
+                    /*path*/  Type.path,
+                    /*length*/ Type.off_t);
 
-       UnixFile.unlink =
+       SysFile.unlink =
          declareFFI("unlink", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*path*/ Types.path);
+                    /*return*/ Type.negativeone_or_nothing,
+                    /*path*/ Type.path);
 
-       UnixFile.write =
+       SysFile.write =
          declareFFI("write", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_ssize_t,
-                    /*fd*/     Types.fd,
-                    /*buf*/    Types.void_t.in_ptr,
-                    /*nbytes*/ Types.size_t);
+                    /*return*/ Type.negativeone_or_ssize_t,
+                    /*fd*/     Type.fd,
+                    /*buf*/    Type.void_t.in_ptr,
+                    /*nbytes*/ Type.size_t);
 
        // Weird cases that require special treatment
 
        // OSes use a variety of hacks to differentiate between
        // 32-bits and 64-bits versions of |stat|, |lstat|, |fstat|.
-       if (OS.Constants.libc._DARWIN_FEATURE_64_BIT_INODE) {
+       if (Const._DARWIN_FEATURE_64_BIT_INODE) {
          // MacOS X 64-bits
-         UnixFile.stat =
+         SysFile.stat =
            declareFFI("stat$INODE64", ctypes.default_abi,
-                      /*return*/ Types.negativeone_or_nothing,
-                      /*path*/   Types.path,
-                      /*buf*/    Types.stat.out_ptr
+                      /*return*/ Type.negativeone_or_nothing,
+                      /*path*/   Type.path,
+                      /*buf*/    Type.stat.out_ptr
                      );
-         UnixFile.lstat =
+         SysFile.lstat =
            declareFFI("lstat$INODE64", ctypes.default_abi,
-                      /*return*/ Types.negativeone_or_nothing,
-                      /*path*/   Types.path,
-                      /*buf*/    Types.stat.out_ptr
+                      /*return*/ Type.negativeone_or_nothing,
+                      /*path*/   Type.path,
+                      /*buf*/    Type.stat.out_ptr
                      );
-         UnixFile.fstat =
+         SysFile.fstat =
            declareFFI("fstat$INODE64", ctypes.default_abi,
-                      /*return*/ Types.negativeone_or_nothing,
-                      /*path*/   Types.fd,
-                      /*buf*/    Types.stat.out_ptr
+                      /*return*/ Type.negativeone_or_nothing,
+                      /*path*/   Type.fd,
+                      /*buf*/    Type.stat.out_ptr
                      );
-       } else if (OS.Constants.libc._STAT_VER != undefined) {
-         const ver = OS.Constants.libc._STAT_VER;
-         let xstat_name, lxstat_name, fxstat_name
+       } else if (Const._STAT_VER != undefined) {
+         const ver = Const._STAT_VER;
+         let xstat_name, lxstat_name, fxstat_name;
          if (OS.Constants.Sys.Name == "SunOS") {
            // Solaris
            xstat_name = "_xstat";
            lxstat_name = "_lxstat";
            fxstat_name = "_fxstat";
          } else {
            // Linux, all widths
            xstat_name = "__xstat";
            lxstat_name = "__lxstat";
            fxstat_name = "__fxstat";
          }
 
          let xstat =
            declareFFI(xstat_name, ctypes.default_abi,
-                      /*return*/    Types.negativeone_or_nothing,
-                      /*_stat_ver*/ Types.int,
-                      /*path*/      Types.path,
-                      /*buf*/       Types.stat.out_ptr);
+                      /*return*/    Type.negativeone_or_nothing,
+                      /*_stat_ver*/ Type.int,
+                      /*path*/      Type.path,
+                      /*buf*/       Type.stat.out_ptr);
          let lxstat =
            declareFFI(lxstat_name, ctypes.default_abi,
-                      /*return*/    Types.negativeone_or_nothing,
-                      /*_stat_ver*/ Types.int,
-                      /*path*/      Types.path,
-                      /*buf*/       Types.stat.out_ptr);
+                      /*return*/    Type.negativeone_or_nothing,
+                      /*_stat_ver*/ Type.int,
+                      /*path*/      Type.path,
+                      /*buf*/       Type.stat.out_ptr);
          let fxstat =
            declareFFI(fxstat_name, ctypes.default_abi,
-                      /*return*/    Types.negativeone_or_nothing,
-                      /*_stat_ver*/ Types.int,
-                      /*fd*/        Types.fd,
-                      /*buf*/       Types.stat.out_ptr);
+                      /*return*/    Type.negativeone_or_nothing,
+                      /*_stat_ver*/ Type.int,
+                      /*fd*/        Type.fd,
+                      /*buf*/       Type.stat.out_ptr);
 
-         UnixFile.stat = function stat(path, buf) {
+         SysFile.stat = function stat(path, buf) {
            return xstat(ver, path, buf);
          };
-         UnixFile.lstat = function stat(path, buf) {
+         SysFile.lstat = function stat(path, buf) {
            return lxstat(ver, path, buf);
          };
-         UnixFile.fstat = function stat(fd, buf) {
+         SysFile.fstat = function stat(fd, buf) {
            return fxstat(ver, fd, buf);
          };
        } else {
          // Mac OS X 32-bits, other Unix
-         UnixFile.stat =
+         SysFile.stat =
            declareFFI("stat", ctypes.default_abi,
-                      /*return*/ Types.negativeone_or_nothing,
-                      /*path*/   Types.path,
-                      /*buf*/    Types.stat.out_ptr
+                      /*return*/ Type.negativeone_or_nothing,
+                      /*path*/   Type.path,
+                      /*buf*/    Type.stat.out_ptr
                      );
-         UnixFile.lstat =
+         SysFile.lstat =
            declareFFI("lstat", ctypes.default_abi,
-                      /*return*/ Types.negativeone_or_nothing,
-                      /*path*/   Types.path,
-                      /*buf*/    Types.stat.out_ptr
+                      /*return*/ Type.negativeone_or_nothing,
+                      /*path*/   Type.path,
+                      /*buf*/    Type.stat.out_ptr
                      );
-         UnixFile.fstat =
+         SysFile.fstat =
            declareFFI("fstat", ctypes.default_abi,
-                      /*return*/ Types.negativeone_or_nothing,
-                      /*fd*/     Types.fd,
-                      /*buf*/    Types.stat.out_ptr
+                      /*return*/ Type.negativeone_or_nothing,
+                      /*fd*/     Type.fd,
+                      /*buf*/    Type.stat.out_ptr
                      );
        }
 
        // We cannot make a C array of CDataFinalizer, so
        // pipe cannot be directly defined as a C function.
 
        let _pipe =
          declareFFI("pipe", ctypes.default_abi,
-           /*return*/ Types.negativeone_or_nothing,
-           /*fds*/    new Type("two file descriptors",
+           /*return*/ Type.negativeone_or_nothing,
+           /*fds*/    new SharedAll.Type("two file descriptors",
              ctypes.ArrayType(ctypes.int, 2)));
 
        // A shared per-thread buffer used to communicate with |pipe|
        let _pipebuf = new (ctypes.ArrayType(ctypes.int, 2))();
 
-       UnixFile.pipe = function pipe(array) {
+       SysFile.pipe = function pipe(array) {
          let result = _pipe(_pipebuf);
          if (result == -1) {
            return result;
          }
          array[0] = ctypes.CDataFinalizer(_pipebuf[0], _close);
          array[1] = ctypes.CDataFinalizer(_pipebuf[1], _close);
          return result;
        };
      };
-     exports.OS.Unix.File._init = init;
+
+     exports.OS.Unix = {
+       File: {
+         _init: init
+       }
+     };
    })(this);
 }
--- a/toolkit/components/osfile/modules/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_unix_front.jsm
@@ -14,28 +14,29 @@
     // We do not wish osfile_unix_front.jsm to be used directly as a main thread
     // module yet.
 
     throw new Error("osfile_unix_front.jsm cannot be used from the main thread yet");
   }
   (function(exports) {
      "use strict";
 
-     exports.OS = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm").OS;
-     let Path = require("resource://gre/modules/osfile/ospath.jsm");
-
      // exports.OS.Unix is created by osfile_unix_back.jsm
      if (exports.OS && exports.OS.File) {
        return; // Avoid double-initialization
      }
 
+     let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+     let Path = require("resource://gre/modules/osfile/ospath.jsm");
+     let SysAll = require("resource://gre/modules/osfile/osfile_unix_allthreads.jsm");
      exports.OS.Unix.File._init();
-     let Const = exports.OS.Constants.libc;
+     let LOG = SharedAll.LOG.bind(SharedAll, "Unix front-end");
+     let Const = SharedAll.Constants.libc;
      let UnixFile = exports.OS.Unix.File;
-     let LOG = OS.Shared.LOG.bind(OS.Shared, "Unix front-end");
+     let Type = UnixFile.Type;
 
      /**
       * Representation of a file.
       *
       * You generally do not need to call this constructor yourself. Rather,
       * to open a file, use function |OS.File.open|.
       *
       * @param fd A OS-specific file descriptor.
@@ -264,17 +265,17 @@
      /**
       * Checks if a file exists
       *
       * @param {string} path The path to the file.
       *
       * @return {bool} true if the file exists, false otherwise.
       */
      File.exists = function Unix_exists(path) {
-       if (UnixFile.access(path, OS.Constants.libc.F_OK) == -1) {
+       if (UnixFile.access(path, Const.F_OK) == -1) {
          return false;
        } else {
          return true;
        }
      };
 
      /**
       * Remove an existing file.
@@ -333,17 +334,17 @@
       * the user, the user can read, write and execute).
       * - {bool} ignoreExisting If |true|, do not fail if the
       * directory already exists.
       */
      File.makeDir = function makeDir(path, options = {}) {
        let omode = options.unixMode !== undefined ? options.unixMode : DEFAULT_UNIX_MODE_DIR;
        let result = UnixFile.mkdir(path, omode);
        if (result != -1 ||
-           options.ignoreExisting && ctypes.errno == OS.Constants.libc.EEXIST) {
+           options.ignoreExisting && ctypes.errno == Const.EEXIST) {
         return;
        }
        throw new File.Error("makeDir");
      };
 
      /**
       * Copy a file to a destination.
       *
@@ -513,20 +514,20 @@
                }
              }
              return total_written;
            } catch (x) {
              if (x.unixErrno == Const.EINVAL) {
                // We *might* be on a file system that does not support splice.
                // Try again with a fallback pump.
                if (total_read) {
-                 source.setPosition(-total_read, OS.File.POS_CURRENT);
+                 source.setPosition(-total_read, File.POS_CURRENT);
                }
                if (total_written) {
-                 dest.setPosition(-total_written, OS.File.POS_CURRENT);
+                 dest.setPosition(-total_written, File.POS_CURRENT);
                }
                return pump_userland(source, dest, options);
              }
              throw x;
            } finally {
              pipe_read.dispose();
              pipe_write.dispose();
            }
@@ -615,17 +616,17 @@
       * @constructor
       */
      File.DirectoryIterator = function DirectoryIterator(path, options) {
        exports.OS.Shared.AbstractFile.AbstractIterator.call(this);
        this._path = path;
        this._dir = UnixFile.opendir(this._path);
        if (this._dir == null) {
          let error = ctypes.errno;
-         if (error != OS.Constants.libc.ENOENT) {
+         if (error != Const.ENOENT) {
            throw new File.Error("DirectoryIterator", error);
          }
          this._exists = false;
          this._closed = true;
        } else {
          this._exists = true;
          this._closed = false;
        }
@@ -658,21 +659,21 @@
            continue;
          }
 
          let isDir, isSymLink;
          if (!("d_type" in contents)) {
            // |dirent| doesn't have d_type on some platforms (e.g. Solaris).
            let path = Path.join(this._path, name);
            throw_on_negative("lstat", UnixFile.lstat(path, gStatDataPtr));
-           isDir = (gStatData.st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.libc.S_IFDIR;
-           isSymLink = (gStatData.st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.libc.S_IFLNK;
+           isDir = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFDIR;
+           isSymLink = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFLNK;
          } else {
-           isDir = contents.d_type == OS.Constants.libc.DT_DIR;
-           isSymLink = contents.d_type == OS.Constants.libc.DT_LNK;
+           isDir = contents.d_type == Const.DT_DIR;
+           isSymLink = contents.d_type == Const.DT_LNK;
          }
 
          return new File.DirectoryIterator.Entry(isDir, isSymLink, name, this._path);
        }
        this.close();
        throw StopIteration;
      };
 
@@ -708,19 +709,19 @@
       * An entry in a directory.
       */
      File.DirectoryIterator.Entry = function Entry(isDir, isSymLink, name, parent) {
        // Copy the relevant part of |unix_entry| to ensure that
        // our data is not overwritten prematurely.
        this._parent = parent;
        let path = Path.join(this._parent, name);
 
-       exports.OS.Shared.Unix.AbstractEntry.call(this, isDir, isSymLink, name, path);
+       SysAll.AbstractEntry.call(this, isDir, isSymLink, name, path);
      };
-     File.DirectoryIterator.Entry.prototype = Object.create(exports.OS.Shared.Unix.AbstractEntry.prototype);
+     File.DirectoryIterator.Entry.prototype = Object.create(SysAll.AbstractEntry.prototype);
 
      /**
       * Return a version of an instance of
       * File.DirectoryIterator.Entry that can be sent from a worker
       * thread to the main thread. Note that deserialization is
       * asymmetric and returns an object with a different
       * implementation.
       */
@@ -732,53 +733,53 @@
        }
        let serialized = {};
        for (let key in File.DirectoryIterator.Entry.prototype) {
          serialized[key] = value[key];
        }
        return serialized;
      };
 
-     let gStatData = new OS.Shared.Type.stat.implementation();
+     let gStatData = new Type.stat.implementation();
      let gStatDataPtr = gStatData.address();
      let MODE_MASK = 4095 /*= 07777*/;
      File.Info = function Info(stat) {
-       let isDir = (stat.st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.libc.S_IFDIR;
-       let isSymLink = (stat.st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.libc.S_IFLNK;
-       let size = exports.OS.Shared.Type.size_t.importFromC(stat.st_size);
+       let isDir = (stat.st_mode & Const.S_IFMT) == Const.S_IFDIR;
+       let isSymLink = (stat.st_mode & Const.S_IFMT) == Const.S_IFLNK;
+       let size = Type.size_t.importFromC(stat.st_size);
 
        let lastAccessDate = new Date(stat.st_atime * 1000);
        let lastModificationDate = new Date(stat.st_mtime * 1000);
        let unixLastStatusChangeDate = new Date(stat.st_ctime * 1000);
 
-       let unixOwner = exports.OS.Shared.Type.uid_t.importFromC(stat.st_uid);
-       let unixGroup = exports.OS.Shared.Type.gid_t.importFromC(stat.st_gid);
-       let unixMode = exports.OS.Shared.Type.mode_t.importFromC(stat.st_mode & MODE_MASK);
+       let unixOwner = Type.uid_t.importFromC(stat.st_uid);
+       let unixGroup = Type.gid_t.importFromC(stat.st_gid);
+       let unixMode = Type.mode_t.importFromC(stat.st_mode & MODE_MASK);
 
-       exports.OS.Shared.Unix.AbstractInfo.call(this, isDir, isSymLink, size, lastAccessDate,
-                                                lastModificationDate, unixLastStatusChangeDate,
-                                                unixOwner, unixGroup, unixMode);
+       SysAll.AbstractInfo.call(this, isDir, isSymLink, size, lastAccessDate,
+           lastModificationDate, unixLastStatusChangeDate,
+           unixOwner, unixGroup, unixMode);
 
        // Some platforms (e.g. MacOS X, some BSDs) store a file creation date
-       if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in OS.Constants.libc) {
+       if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in Const) {
          let date = new Date(stat.st_birthtime * 1000);
 
         /**
          * The date of creation of this file.
          *
          * Note that the date returned by this method is not always
          * reliable. Not all file systems are able to provide this
          * information.
          *
          * @type {Date}
          */
          this.macBirthDate = date;
        }
      };
-     File.Info.prototype = Object.create(exports.OS.Shared.Unix.AbstractInfo.prototype);
+     File.Info.prototype = Object.create(SysAll.AbstractInfo.prototype);
 
      // Deprecated, use macBirthDate/winBirthDate instead
      Object.defineProperty(File.Info.prototype, "creationDate", {
       get: function creationDate() {
         // On the Macintosh, returns the birth date if available.
         // On other Unix, as the birth date is not available,
         // returns the epoch.
         return this.macBirthDate || new Date(0);
@@ -819,16 +820,17 @@
        } else {
          throw_on_negative("stat", UnixFile.stat(path, gStatDataPtr));
        }
        return new File.Info(gStatData);
      };
 
      File.read = exports.OS.Shared.AbstractFile.read;
      File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
+     File.openUnique = exports.OS.Shared.AbstractFile.openUnique;
      File.removeDir = exports.OS.Shared.AbstractFile.removeDir;
 
      /**
       * Get the current directory by getCurrentDirectory.
       */
      File.getCurrentDirectory = function getCurrentDirectory() {
        let path = UnixFile.get_current_dir_name?UnixFile.get_current_dir_name():
          UnixFile.getwd_auto(null);
@@ -889,16 +891,17 @@
      function throw_on_null(operation, result) {
        if (result == null || (result.isNull && result.isNull())) {
          throw new File.Error(operation);
        }
        return result;
      }
 
      File.Unix = exports.OS.Unix.File;
-     File.Error = exports.OS.Shared.Unix.Error;
+     File.Error = SysAll.Error;
      exports.OS.File = File;
+     exports.OS.Shared.Type = Type;
 
-     Object.defineProperty(File, "POS_START", { value: OS.Shared.POS_START });
-     Object.defineProperty(File, "POS_CURRENT", { value: OS.Shared.POS_CURRENT });
-     Object.defineProperty(File, "POS_END", { value: OS.Shared.POS_END });
+     Object.defineProperty(File, "POS_START", { value: SysAll.POS_START });
+     Object.defineProperty(File, "POS_CURRENT", { value: SysAll.POS_CURRENT });
+     Object.defineProperty(File, "POS_END", { value: SysAll.POS_END });
    })(this);
 }
--- a/toolkit/components/osfile/modules/osfile_win_allthreads.jsm
+++ b/toolkit/components/osfile/modules/osfile_win_allthreads.jsm
@@ -3,359 +3,376 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * This module defines the thread-agnostic components of the Win version
  * of OS.File. It depends on the thread-agnostic cross-platform components
  * of OS.File.
  *
  * It serves the following purposes:
- * - open libc;
+ * - open kernel32;
  * - define OS.Shared.Win.Error;
  * - define a few constants and types that need to be defined on all platforms.
  *
  * This module can be:
  * - opened from the main thread as a jsm module;
- * - opened from a chrome worker through importScripts.
+ * - opened from a chrome worker through require().
  */
 
+"use strict";
+
 let SharedAll;
 if (typeof Components != "undefined") {
+  let Cu = Components.utils;
   // Module is opened as a jsm module
-  this.EXPORTED_SYMBOLS = ["OS"];
-  Components.utils.import("resource://gre/modules/ctypes.jsm");
+  Cu.import("resource://gre/modules/ctypes.jsm", this);
 
   SharedAll = {};
-  Components.utils.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", SharedAll);
+  Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", SharedAll);
+  this.exports = {};
+} else if (typeof "module" != "undefined" && typeof "require" != "undefined") {
+  // Module is loaded with require()
+  SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
 } else {
-  SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+  throw new Error("Please open this module with Component.utils.import or with require()");
 }
 
-(function(exports) {
-  "use strict";
-  if (exports.OS && exports.OS.Shared && exports.OS.Shared.Win) {
-    // Avoid double inclusion
-    return;
-  }
-  exports.OS = SharedAll.OS;
-  exports.OS.Shared.Win = {};
+let LOG = SharedAll.LOG.bind(SharedAll, "Win", "allthreads");
+let Const = SharedAll.Constants.Win;
 
-  let LOG = OS.Shared.LOG.bind(OS.Shared, "Win", "allthreads");
+// Open libc
+let libc;
+try {
+  libc = ctypes.open("kernel32.dll");
+} catch (ex) {
+  // Note: If you change the string here, please adapt consumers and
+  // tests accordingly
+  throw new Error("Could not open system library: " + ex.message);
+}
+exports.libc = libc;
 
-  // Open libc
-  let libc;
-  try {
-    libc = ctypes.open("kernel32.dll");
-  } catch (ex) {
-    // Note: If you change the string here, please adapt consumers and
-    // tests accordingly
-    throw new Error("Could not open system library: " + ex.message);
-  }
-  exports.OS.Shared.Win.libc = libc;
+// Define declareFFI
+let declareFFI = SharedAll.declareFFI.bind(null, libc);
+exports.declareFFI = declareFFI;
 
-  // Define declareFFI
-  let declareFFI = OS.Shared.declareFFI.bind(null, libc);
-  exports.OS.Shared.Win.declareFFI = declareFFI;
+// Define Error
+let FormatMessage = libc.declare("FormatMessageW", ctypes.winapi_abi,
+  /*return*/ ctypes.uint32_t,
+  /*flags*/  ctypes.uint32_t,
+  /*source*/ ctypes.voidptr_t,
+  /*msgid*/  ctypes.uint32_t,
+  /*langid*/ ctypes.uint32_t,
+  /*buf*/    ctypes.jschar.ptr,
+  /*size*/   ctypes.uint32_t,
+  /*Arguments*/ctypes.voidptr_t
+);
 
-  // Define Error
-  let FormatMessage = libc.declare("FormatMessageW", ctypes.winapi_abi,
-    /*return*/ ctypes.uint32_t,
-    /*flags*/  ctypes.uint32_t,
-    /*source*/ ctypes.voidptr_t,
-    /*msgid*/  ctypes.uint32_t,
-    /*langid*/ ctypes.uint32_t,
-    /*buf*/    ctypes.jschar.ptr,
-    /*size*/   ctypes.uint32_t,
-    /*Arguments*/ctypes.voidptr_t
+/**
+ * A File-related error.
+ *
+ * To obtain a human-readable error message, use method |toString|.
+ * To determine the cause of the error, use the various |becauseX|
+ * getters. To determine the operation that failed, use field
+ * |operation|.
+ *
+ * Additionally, this implementation offers a field
+ * |winLastError|, which holds the OS-specific error
+ * constant. If you need this level of detail, you may match the value
+ * of this field against the error constants of |OS.Constants.Win|.
+ *
+ * @param {string=} operation The operation that failed. If unspecified,
+ * the name of the calling function is taken to be the operation that
+ * failed.
+ * @param {number=} lastError The OS-specific constant detailing the
+ * reason of the error. If unspecified, this is fetched from the system
+ * status.
+ *
+ * @constructor
+ * @extends {OS.Shared.Error}
+ */
+let OSError = function OSError(operation, lastError) {
+  operation = operation || "unknown operation";
+  SharedAll.OSError.call(this, operation);
+  this.winLastError = lastError || ctypes.winLastError;
+};
+OSError.prototype = Object.create(SharedAll.OSError.prototype);
+OSError.prototype.toString = function toString() {
+  let buf = new (ctypes.ArrayType(ctypes.jschar, 1024))();
+  let result = FormatMessage(
+    Const.FORMAT_MESSAGE_FROM_SYSTEM |
+    Const.FORMAT_MESSAGE_IGNORE_INSERTS,
+    null,
+    /* The error number */ this.winLastError,
+    /* Default language */ 0,
+    /* Output buffer*/     buf,
+    /* Minimum size of buffer */ 1024,
+    /* Format args*/       null
   );
+  if (!result) {
+    buf = "additional error " +
+      ctypes.winLastError +
+      " while fetching system error message";
+  }
+  return "Win error " + this.winLastError + " during operation "
+    + this.operation + " (" + buf.readString() + ")";
+};
 
-  /**
-   * A File-related error.
-   *
-   * To obtain a human-readable error message, use method |toString|.
-   * To determine the cause of the error, use the various |becauseX|
-   * getters. To determine the operation that failed, use field
-   * |operation|.
-   *
-   * Additionally, this implementation offers a field
-   * |winLastError|, which holds the OS-specific error
-   * constant. If you need this level of detail, you may match the value
-   * of this field against the error constants of |OS.Constants.Win|.
-   *
-   * @param {string=} operation The operation that failed. If unspecified,
-   * the name of the calling function is taken to be the operation that
-   * failed.
-   * @param {number=} lastError The OS-specific constant detailing the
-   * reason of the error. If unspecified, this is fetched from the system
-   * status.
-   *
-   * @constructor
-   * @extends {OS.Shared.Error}
-   */
-  let OSError = function OSError(operation, lastError) {
-    operation = operation || "unknown operation";
-    exports.OS.Shared.Error.call(this, operation);
-    this.winLastError = lastError || ctypes.winLastError;
+/**
+ * |true| if the error was raised because a file or directory
+ * already exists, |false| otherwise.
+ */
+Object.defineProperty(OSError.prototype, "becauseExists", {
+  get: function becauseExists() {
+    return this.winLastError == Const.ERROR_FILE_EXISTS ||
+      this.winLastError == Const.ERROR_ALREADY_EXISTS;
+  }
+});
+/**
+ * |true| if the error was raised because a file or directory
+ * does not exist, |false| otherwise.
+ */
+Object.defineProperty(OSError.prototype, "becauseNoSuchFile", {
+  get: function becauseNoSuchFile() {
+    return this.winLastError == Const.ERROR_FILE_NOT_FOUND;
+  }
+});
+/**
+ * |true| if the error was raised because a directory is not empty
+ * does not exist, |false| otherwise.
+ */
+Object.defineProperty(OSError.prototype, "becauseNotEmpty", {
+  get: function becauseNotEmpty() {
+    return this.winLastError == Const.ERROR_DIR_NOT_EMPTY;
+  }
+});
+/**
+ * |true| if the error was raised because a file or directory
+ * is closed, |false| otherwise.
+ */
+Object.defineProperty(OSError.prototype, "becauseClosed", {
+  get: function becauseClosed() {
+    return this.winLastError == Const.ERROR_INVALID_HANDLE;
+  }
+});
+/**
+ * |true| if the error was raised because permission is denied to
+ * access a file or directory, |false| otherwise.
+ */
+Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
+  get: function becauseAccessDenied() {
+    return this.winLastError == Const.ERROR_ACCESS_DENIED;
+  }
+});
+
+/**
+ * Serialize an instance of OSError to something that can be
+ * transmitted across threads (not necessarily a string).
+ */
+OSError.toMsg = function toMsg(error) {
+  return {
+    operation: error.operation,
+    winLastError: error.winLastError
   };
-  OSError.prototype = new exports.OS.Shared.Error();
-  OSError.prototype.toString = function toString() {
-    let buf = new (ctypes.ArrayType(ctypes.jschar, 1024))();
-    let result = FormatMessage(
-      exports.OS.Constants.Win.FORMAT_MESSAGE_FROM_SYSTEM |
-      exports.OS.Constants.Win.FORMAT_MESSAGE_IGNORE_INSERTS,
-      null,
-      /* The error number */ this.winLastError,
-      /* Default language */ 0,
-      /* Output buffer*/     buf,
-      /* Minimum size of buffer */ 1024,
-      /* Format args*/       null
-    );
-    if (!result) {
-      buf = "additional error " +
-        ctypes.winLastError +
-        " while fetching system error message";
-    }
-    return "Win error " + this.winLastError + " during operation "
-      + this.operation + " (" + buf.readString() + ")";
-  };
+};
+
+/**
+ * Deserialize a message back to an instance of OSError
+ */
+OSError.fromMsg = function fromMsg(msg) {
+  return new OSError(msg.operation, msg.winLastError);
+};
+exports.Error = OSError;
 
-  /**
-   * |true| if the error was raised because a file or directory
-   * already exists, |false| otherwise.
-   */
-  Object.defineProperty(OSError.prototype, "becauseExists", {
-    get: function becauseExists() {
-      return this.winLastError == exports.OS.Constants.Win.ERROR_FILE_EXISTS ||
-        this.winLastError == exports.OS.Constants.Win.ERROR_ALREADY_EXISTS;
-    }
-  });
+/**
+ * Code shared by implementation of File.Info on Windows
+ *
+ * @constructor
+ */
+let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, winBirthDate,
+                                         lastAccessDate, lastWriteDate) {
+  this._isDir = isDir;
+  this._isSymLink = isSymLink;
+  this._size = size;
+  this._winBirthDate = winBirthDate;
+  this._lastAccessDate = lastAccessDate;
+  this._lastModificationDate = lastWriteDate;
+};
+
+AbstractInfo.prototype = {
   /**
-   * |true| if the error was raised because a file or directory
-   * does not exist, |false| otherwise.
-   */
-  Object.defineProperty(OSError.prototype, "becauseNoSuchFile", {
-    get: function becauseNoSuchFile() {
-      return this.winLastError == exports.OS.Constants.Win.ERROR_FILE_NOT_FOUND;
-    }
-  });
-  /**
-   * |true| if the error was raised because a directory is not empty
-   * does not exist, |false| otherwise.
+   * |true| if this file is a directory, |false| otherwise
    */
-  Object.defineProperty(OSError.prototype, "becauseNotEmpty", {
-    get: function becauseNotEmpty() {
-      return this.winLastError == OS.Constants.Win.ERROR_DIR_NOT_EMPTY;
-    }
-  });
-  /**
-   * |true| if the error was raised because a file or directory
-   * is closed, |false| otherwise.
-   */
-  Object.defineProperty(OSError.prototype, "becauseClosed", {
-    get: function becauseClosed() {
-      return this.winLastError == exports.OS.Constants.Win.ERROR_INVALID_HANDLE;
-    }
-  });
+  get isDir() {
+    return this._isDir;
+  },
   /**
-   * |true| if the error was raised because permission is denied to
-   * access a file or directory, |false| otherwise.
-   */
-  Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
-    get: function becauseAccessDenied() {
-      return this.winLastError == exports.OS.Constants.Win.ERROR_ACCESS_DENIED;
-    }
-  });
-
-  /**
-   * Serialize an instance of OSError to something that can be
-   * transmitted across threads (not necessarily a string).
-   */
-  OSError.toMsg = function toMsg(error) {
-    return {
-      operation: error.operation,
-      winLastError: error.winLastError
-    };
-  };
-
-  /**
-   * Deserialize a message back to an instance of OSError
-   */
-  OSError.fromMsg = function fromMsg(msg) {
-    return new OSError(msg.operation, msg.winLastError);
-  };
-
-  exports.OS.Shared.Win.Error = OSError;
-
-  /**
-   * Code shared by implementation of File.Info on Windows
-   *
-   * @constructor
+   * |true| if this file is a symbolic link, |false| otherwise
    */
-  let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, winBirthDate,
-                                           lastAccessDate, lastWriteDate) {
-    this._isDir = isDir;
-    this._isSymLink = isSymLink;
-    this._size = size;
-    this._winBirthDate = winBirthDate;
-    this._lastAccessDate = lastAccessDate;
-    this._lastModificationDate = lastWriteDate;
-  };
+  get isSymLink() {
+    return this._isSymLink;
+  },
+  /**
+   * The size of the file, in bytes.
+   *
+   * Note that the result may be |NaN| if the size of the file cannot be
+   * represented in JavaScript.
+   *
+   * @type {number}
+   */
+  get size() {
+    return this._size;
+  },
+  // Deprecated
+  get creationDate() {
+    return this._winBirthDate;
+  },
+  /**
+   * The date of creation of this file.
+   *
+   * @type {Date}
+   */
+  get winBirthDate() {
+    return this._winBirthDate;
+  },
+  /**
+   * The date of last access to this file.
+   *
+   * Note that the definition of last access may depend on the underlying
+   * operating system and file system.
+   *
+   * @type {Date}
+   */
+  get lastAccessDate() {
+    return this._lastAccessDate;
+  },
+  /**
+   * The date of last modification of this file.
+   *
+   * Note that the definition of last access may depend on the underlying
+   * operating system and file system.
+   *
+   * @type {Date}
+   */
+  get lastModificationDate() {
+    return this._lastModificationDate;
+  }
+};
+exports.AbstractInfo = AbstractInfo;
 
-  AbstractInfo.prototype = {
-    /**
-     * |true| if this file is a directory, |false| otherwise
-     */
-    get isDir() {
-      return this._isDir;
-    },
-    /**
-     * |true| if this file is a symbolic link, |false| otherwise
-     */
-    get isSymLink() {
-      return this._isSymLink;
-    },
-    /**
-     * The size of the file, in bytes.
-     *
-     * Note that the result may be |NaN| if the size of the file cannot be
-     * represented in JavaScript.
-     *
-     * @type {number}
-     */
-    get size() {
-      return this._size;
-    },
-    // Deprecated
-    get creationDate() {
-      return this._winBirthDate;
-    },
-    /**
-     * The date of creation of this file.
-     *
-     * @type {Date}
-     */
-    get winBirthDate() {
-      return this._winBirthDate;
-    },
-    /**
-     * The date of last access to this file.
-     *
-     * Note that the definition of last access may depend on the underlying
-     * operating system and file system.
-     *
-     * @type {Date}
-     */
-    get lastAccessDate() {
-      return this._lastAccessDate;
-    },
-    /**
-     * The date of last modification of this file.
-     *
-     * Note that the definition of last access may depend on the underlying
-     * operating system and file system.
-     *
-     * @type {Date}
-     */
-    get lastModificationDate() {
-      return this._lastModificationDate;
-    }
-  };
-  exports.OS.Shared.Win.AbstractInfo = AbstractInfo;
+/**
+ * Code shared by implementation of File.DirectoryIterator.Entry on Windows
+ *
+ * @constructor
+ */
+let AbstractEntry = function AbstractEntry(isDir, isSymLink, name,
+                                           winCreationDate, winLastWriteDate,
+                                           winLastAccessDate, path) {
+  this._isDir = isDir;
+  this._isSymLink = isSymLink;
+  this._name = name;
+  this._winCreationDate = winCreationDate;
+  this._winLastWriteDate = winLastWriteDate;
+  this._winLastAccessDate = winLastAccessDate;
+  this._path = path;
+};
 
+AbstractEntry.prototype = {
   /**
-   * Code shared by implementation of File.DirectoryIterator.Entry on Windows
-   *
-   * @constructor
+   * |true| if the entry is a directory, |false| otherwise
+   */
+  get isDir() {
+    return this._isDir;
+  },
+  /**
+   * |true| if the entry is a symbolic link, |false| otherwise
+   */
+  get isSymLink() {
+    return this._isSymLink;
+  },
+  /**
+   * The name of the entry.
+   * @type {string}
    */
-  let AbstractEntry = function AbstractEntry(isDir, isSymLink, name,
-                                             winCreationDate, winLastWriteDate,
-                                             winLastAccessDate, path) {
-    this._isDir = isDir;
-    this._isSymLink = isSymLink;
-    this._name = name;
-    this._winCreationDate = winCreationDate;
-    this._winLastWriteDate = winLastWriteDate;
-    this._winLastAccessDate = winLastAccessDate;
-    this._path = path;
-  };
+  get name() {
+    return this._name;
+  },
+  /**
+   * The creation time of this file.
+   * @type {Date}
+   */
+  get winCreationDate() {
+    return this._winCreationDate;
+  },
+  /**
+   * The last modification time of this file.
+   * @type {Date}
+   */
+  get winLastWriteDate() {
+    return this._winLastWriteDate;
+  },
+  /**
+   * The last access time of this file.
+   * @type {Date}
+   */
+  get winLastAccessDate() {
+    return this._winLastAccessDate;
+  },
+  /**
+   * The full path of the entry
+   * @type {string}
+   */
+  get path() {
+    return this._path;
+  }
+};
+exports.AbstractEntry = AbstractEntry;
+
+// Special constants that need to be defined on all platforms
+
+exports.POS_START = Const.FILE_BEGIN;
+exports.POS_CURRENT = Const.FILE_CURRENT;
+exports.POS_END = Const.FILE_END;
 
-  AbstractEntry.prototype = {
-    /**
-     * |true| if the entry is a directory, |false| otherwise
-     */
-    get isDir() {
-      return this._isDir;
-    },
-    /**
-     * |true| if the entry is a symbolic link, |false| otherwise
-     */
-    get isSymLink() {
-      return this._isSymLink;
-    },
-    /**
-     * The name of the entry.
-     * @type {string}
-     */
-    get name() {
-      return this._name;
-    },
-    /**
-     * The creation time of this file.
-     * @type {Date}
-     */
-    get winCreationDate() {
-      return this._winCreationDate;
-    },
-    /**
-     * The last modification time of this file.
-     * @type {Date}
-     */
-    get winLastWriteDate() {
-      return this._winLastWriteDate;
-    },
-    /**
-     * The last access time of this file.
-     * @type {Date}
-     */
-    get winLastAccessDate() {
-      return this._winLastAccessDate;
-    },
-    /**
-     * The full path of the entry
-     * @type {string}
-     */
-    get path() {
-      return this._path;
-    }
-  };
-  exports.OS.Shared.Win.AbstractEntry = AbstractEntry;
+// Special types that need to be defined for communication
+// between threads
+let Type = Object.create(SharedAll.Type);
+exports.Type = Type;
+
+/**
+ * Native paths
+ *
+ * Under Windows, expressed as wide strings
+ */
+Type.path = Type.wstring.withName("[in] path");
+Type.out_path = Type.out_wstring.withName("[out] path");
+
+// Special constructors that need to be defined on all threads
+OSError.closed = function closed(operation) {
+  return new OSError(operation, Const.ERROR_INVALID_HANDLE);
+};
+
+OSError.exists = function exists(operation) {
+  return new OSError(operation, Const.ERROR_FILE_EXISTS);
+};
 
-  // Special constants that need to be defined on all platforms
-
-  Object.defineProperty(exports.OS.Shared, "POS_START", { value: exports.OS.Constants.Win.FILE_BEGIN });
-  Object.defineProperty(exports.OS.Shared, "POS_CURRENT", { value: exports.OS.Constants.Win.FILE_CURRENT });
-  Object.defineProperty(exports.OS.Shared, "POS_END", { value: exports.OS.Constants.Win.FILE_END });
-
-  // Special types that need to be defined for communication
-  // between threads
-  let Types = exports.OS.Shared.Type;
+OSError.noSuchFile = function noSuchFile(operation) {
+  return new OSError(operation, Const.ERROR_FILE_NOT_FOUND);
+};
 
-  /**
-   * Native paths
-   *
-   * Under Windows, expressed as wide strings
-   */
-  Types.path = Types.wstring.withName("[in] path");
-  Types.out_path = Types.out_wstring.withName("[out] path");
+let EXPORTED_SYMBOLS = [
+  "declareFFI",
+  "libc",
+  "Error",
+  "AbstractInfo",
+  "AbstractEntry",
+  "Type",
+  "POS_START",
+  "POS_CURRENT",
+  "POS_END"
+];
 
-  // Special constructors that need to be defined on all threads
-  OSError.closed = function closed(operation) {
-    return new OSError(operation, exports.OS.Constants.Win.ERROR_INVALID_HANDLE);
-  };
-
-  OSError.exists = function exists(operation) {
-    return new OSError(operation, exports.OS.Constants.Win.ERROR_FILE_EXISTS);
-  };
-
-  OSError.noSuchFile = function noSuchFile(operation) {
-    return new OSError(operation, exports.OS.Constants.Win.ERROR_FILE_NOT_FOUND);
-  };
-})(this);
+//////////// Boilerplate
+if (typeof Components != "undefined") {
+  this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS;
+  for (let symbol of EXPORTED_SYMBOLS) {
+    this[symbol] = exports[symbol];
+  }
+}
--- a/toolkit/components/osfile/modules/osfile_win_back.jsm
+++ b/toolkit/components/osfile/modules/osfile_win_back.jsm
@@ -29,309 +29,312 @@
     throw new Error("osfile_win.jsm cannot be used from the main thread yet");
   }
 
   (function(exports) {
      "use strict";
      if (exports.OS && exports.OS.Win && exports.OS.Win.File) {
        return; // Avoid double initialization
      }
-     exports.OS = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm").OS;
-     exports.OS.Win.File = {};
 
-     let LOG = OS.Shared.LOG.bind(OS.Shared, "Win", "back");
-     let libc = exports.OS.Shared.Win.libc;
+     let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+     let SysAll = require("resource://gre/modules/osfile/osfile_win_allthreads.jsm");
+     let LOG = SharedAll.LOG.bind(SharedAll, "Unix", "back");
+     let libc = SysAll.libc;
+     let Const = SharedAll.Constants.Win;
 
      /**
       * Initialize the Windows module.
       *
       * @param {function=} declareFFI
       */
      // FIXME: Both |init| and |aDeclareFFI| are deprecated, we should remove them
      let init = function init(aDeclareFFI) {
        let declareFFI;
        if (aDeclareFFI) {
          declareFFI = aDeclareFFI.bind(null, libc);
        } else {
-         declareFFI = exports.OS.Shared.Win.declareFFI;
+         declareFFI = SysAll.declareFFI;
        }
 
-       // Shorthands
-       let OSWin = exports.OS.Win;
-       let WinFile = exports.OS.Win.File;
-       if (!exports.OS.Types) {
-         exports.OS.Types = {};
-       }
-       let Type = exports.OS.Shared.Type;
-       let Types = Type;
+       // Initialize types that require additional OS-specific
+       // support - either finalization or matching against
+       // OS-specific constants.
+       let Type = Object.create(SysAll.Type);
+       let SysFile = exports.OS.Win.File = { Type: Type };
 
        // Initialize types
 
        /**
         * A C integer holding INVALID_HANDLE_VALUE in case of error or
         * a file descriptor in case of success.
         */
-       Types.HANDLE =
-         Types.voidptr_t.withName("HANDLE");
-       Types.HANDLE.importFromC = function importFromC(maybe) {
-         if (Types.int.cast(maybe).value == INVALID_HANDLE) {
+       Type.HANDLE =
+         Type.voidptr_t.withName("HANDLE");
+       Type.HANDLE.importFromC = function importFromC(maybe) {
+         if (Type.int.cast(maybe).value == INVALID_HANDLE) {
            // Ensure that API clients can effectively compare against
            // Const.INVALID_HANDLE_VALUE. Without this cast,
            // == would always return |false|.
            return INVALID_HANDLE;
          }
          return ctypes.CDataFinalizer(maybe, this.finalizeHANDLE);
        };
-       Types.HANDLE.finalizeHANDLE = function placeholder() {
+       Type.HANDLE.finalizeHANDLE = function placeholder() {
          throw new Error("finalizeHANDLE should be implemented");
        };
-       let INVALID_HANDLE = exports.OS.Constants.Win.INVALID_HANDLE_VALUE;
+       let INVALID_HANDLE = Const.INVALID_HANDLE_VALUE;
 
-       Types.file_HANDLE = Types.HANDLE.withName("file HANDLE");
-       exports.OS.Shared.defineLazyGetter(Types.file_HANDLE,
+       Type.file_HANDLE = Type.HANDLE.withName("file HANDLE");
+       SharedAll.defineLazyGetter(Type.file_HANDLE,
          "finalizeHANDLE",
          function() {
            return _CloseHandle;
          });
 
-       Types.find_HANDLE = Types.HANDLE.withName("find HANDLE");
-       exports.OS.Shared.defineLazyGetter(Types.find_HANDLE,
+       Type.find_HANDLE = Type.HANDLE.withName("find HANDLE");
+       SharedAll.defineLazyGetter(Type.find_HANDLE,
          "finalizeHANDLE",
          function() {
            return _FindClose;
          });
 
-       Types.DWORD = Types.int32_t.withName("DWORD");
+       Type.DWORD = Type.int32_t.withName("DWORD");
 
        /**
         * A C integer holding -1 in case of error or a positive integer
         * in case of success.
         */
-       Types.negative_or_DWORD =
-         Types.DWORD.withName("negative_or_DWORD");
+       Type.negative_or_DWORD =
+         Type.DWORD.withName("negative_or_DWORD");
 
        /**
         * A C integer holding 0 in case of error or a positive integer
         * in case of success.
         */
-       Types.zero_or_DWORD =
-         Types.DWORD.withName("zero_or_DWORD");
+       Type.zero_or_DWORD =
+         Type.DWORD.withName("zero_or_DWORD");
 
        /**
         * A C integer holding 0 in case of error, any other value in
         * case of success.
         */
-       Types.zero_or_nothing =
-         Types.int.withName("zero_or_nothing");
+       Type.zero_or_nothing =
+         Type.int.withName("zero_or_nothing");
 
-       Types.SECURITY_ATTRIBUTES =
-         Types.void_t.withName("SECURITY_ATTRIBUTES");
+       Type.SECURITY_ATTRIBUTES =
+         Type.void_t.withName("SECURITY_ATTRIBUTES");
 
-       Types.FILETIME =
-         new Type("FILETIME",
+       Type.FILETIME =
+         new SharedAll.Type("FILETIME",
                   ctypes.StructType("FILETIME", [
-                  { lo: Types.DWORD.implementation },
-                  { hi: Types.DWORD.implementation }]));
+                  { lo: Type.DWORD.implementation },
+                  { hi: Type.DWORD.implementation }]));
 
-       Types.FindData =
-         new Type("FIND_DATA",
+       Type.FindData =
+         new SharedAll.Type("FIND_DATA",
                   ctypes.StructType("FIND_DATA", [
                     { dwFileAttributes: ctypes.uint32_t },
-                    { ftCreationTime:   Types.FILETIME.implementation },
-                    { ftLastAccessTime: Types.FILETIME.implementation },
-                    { ftLastWriteTime:  Types.FILETIME.implementation },
-                    { nFileSizeHigh:    Types.DWORD.implementation },
-                    { nFileSizeLow:     Types.DWORD.implementation },
-                    { dwReserved0:      Types.DWORD.implementation },
-                    { dwReserved1:      Types.DWORD.implementation },
-                    { cFileName:        ctypes.ArrayType(ctypes.jschar, exports.OS.Constants.Win.MAX_PATH) },
+                    { ftCreationTime:   Type.FILETIME.implementation },
+                    { ftLastAccessTime: Type.FILETIME.implementation },
+                    { ftLastWriteTime:  Type.FILETIME.implementation },
+                    { nFileSizeHigh:    Type.DWORD.implementation },
+                    { nFileSizeLow:     Type.DWORD.implementation },
+                    { dwReserved0:      Type.DWORD.implementation },
+                    { dwReserved1:      Type.DWORD.implementation },
+                    { cFileName:        ctypes.ArrayType(ctypes.jschar, Const.MAX_PATH) },
                     { cAlternateFileName: ctypes.ArrayType(ctypes.jschar, 14) }
                       ]));
 
-       Types.FILE_INFORMATION =
-         new Type("FILE_INFORMATION",
+       Type.FILE_INFORMATION =
+         new SharedAll.Type("FILE_INFORMATION",
                   ctypes.StructType("FILE_INFORMATION", [
                     { dwFileAttributes: ctypes.uint32_t },
-                    { ftCreationTime:   Types.FILETIME.implementation },
-                    { ftLastAccessTime: Types.FILETIME.implementation },
-                    { ftLastWriteTime:  Types.FILETIME.implementation },
+                    { ftCreationTime:   Type.FILETIME.implementation },
+                    { ftLastAccessTime: Type.FILETIME.implementation },
+                    { ftLastWriteTime:  Type.FILETIME.implementation },
                     { dwVolumeSerialNumber: ctypes.uint32_t },
-                    { nFileSizeHigh:    Types.DWORD.implementation },
-                    { nFileSizeLow:     Types.DWORD.implementation },
+                    { nFileSizeHigh:    Type.DWORD.implementation },
+                    { nFileSizeLow:     Type.DWORD.implementation },
                     { nNumberOfLinks:   ctypes.uint32_t },
                     { nFileIndex: ctypes.uint64_t }
                    ]));
 
-       Types.SystemTime =
-         new Type("SystemTime",
+       Type.SystemTime =
+         new SharedAll.Type("SystemTime",
                   ctypes.StructType("SystemTime", [
                   { wYear:      ctypes.int16_t },
                   { wMonth:     ctypes.int16_t },
                   { wDayOfWeek: ctypes.int16_t },
                   { wDay:       ctypes.int16_t },
                   { wHour:      ctypes.int16_t },
                   { wMinute:    ctypes.int16_t },
                   { wSecond:    ctypes.int16_t },
                   { wMilliSeconds: ctypes.int16_t }
                   ]));
 
        // Special case: these functions are used by the
        // finalizer
-       let _CloseHandle = WinFile._CloseHandle =
+       let _CloseHandle = SysFile._CloseHandle =
          libc.declare("CloseHandle", ctypes.winapi_abi,
                         /*return */ctypes.bool,
                         /*handle*/ ctypes.voidptr_t);
 
-       WinFile.CloseHandle = function(fd) {
+       SysFile.CloseHandle = function(fd) {
          if (fd == INVALID_HANDLE) {
            return true;
          } else {
            return fd.dispose(); // Returns the value of |CloseHandle|.
          }
        };
 
        let _FindClose =
          libc.declare("FindClose", ctypes.winapi_abi,
                         /*return */ctypes.bool,
                         /*handle*/ ctypes.voidptr_t);
 
-       WinFile.FindClose = function(handle) {
+       SysFile.FindClose = function(handle) {
          if (handle == INVALID_HANDLE) {
            return true;
          } else {
            return handle.dispose(); // Returns the value of |FindClose|.
          }
        };
 
        // Declare libc functions as functions of |OS.Win.File|
 
-       WinFile.CopyFile =
+       SysFile.CopyFile =
          declareFFI("CopyFileW", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*sourcePath*/ Types.path,
-                    /*destPath*/   Types.path,
-                    /*bailIfExist*/Types.bool);
+                    /*return*/ Type.zero_or_nothing,
+                    /*sourcePath*/ Type.path,
+                    /*destPath*/   Type.path,
+                    /*bailIfExist*/Type.bool);
 
-       WinFile.CreateDirectory =
+       SysFile.CreateDirectory =
          declareFFI("CreateDirectoryW", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*name*/   Types.jschar.in_ptr,
-                    /*security*/Types.SECURITY_ATTRIBUTES.in_ptr);
+                    /*return*/ Type.zero_or_nothing,
+                    /*name*/   Type.jschar.in_ptr,
+                    /*security*/Type.SECURITY_ATTRIBUTES.in_ptr);
 
-       WinFile.CreateFile =
+       SysFile.CreateFile =
          declareFFI("CreateFileW", ctypes.winapi_abi,
-                    /*return*/  Types.file_HANDLE,
-                    /*name*/    Types.path,
-                    /*access*/  Types.DWORD,
-                    /*share*/   Types.DWORD,
-                    /*security*/Types.SECURITY_ATTRIBUTES.in_ptr,
-                    /*creation*/Types.DWORD,
-                    /*flags*/   Types.DWORD,
-                    /*template*/Types.HANDLE);
+                    /*return*/  Type.file_HANDLE,
+                    /*name*/    Type.path,
+                    /*access*/  Type.DWORD,
+                    /*share*/   Type.DWORD,
+                    /*security*/Type.SECURITY_ATTRIBUTES.in_ptr,
+                    /*creation*/Type.DWORD,
+                    /*flags*/   Type.DWORD,
+                    /*template*/Type.HANDLE);
 
-       WinFile.DeleteFile =
+       SysFile.DeleteFile =
          declareFFI("DeleteFileW", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*path*/   Types.path);
+                    /*return*/ Type.zero_or_nothing,
+                    /*path*/   Type.path);
 
-       WinFile.FileTimeToSystemTime =
+       SysFile.FileTimeToSystemTime =
          declareFFI("FileTimeToSystemTime", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*filetime*/Types.FILETIME.in_ptr,
-                    /*systime*/ Types.SystemTime.out_ptr);
+                    /*return*/ Type.zero_or_nothing,
+                    /*filetime*/Type.FILETIME.in_ptr,
+                    /*systime*/ Type.SystemTime.out_ptr);
 
-       WinFile.FindFirstFile =
+       SysFile.FindFirstFile =
          declareFFI("FindFirstFileW", ctypes.winapi_abi,
-                    /*return*/ Types.find_HANDLE,
-                    /*pattern*/Types.path,
-                    /*data*/   Types.FindData.out_ptr);
+                    /*return*/ Type.find_HANDLE,
+                    /*pattern*/Type.path,
+                    /*data*/   Type.FindData.out_ptr);
 
-       WinFile.FindNextFile =
+       SysFile.FindNextFile =
          declareFFI("FindNextFileW", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*prev*/   Types.find_HANDLE,
-                    /*data*/   Types.FindData.out_ptr);
+                    /*return*/ Type.zero_or_nothing,
+                    /*prev*/   Type.find_HANDLE,
+                    /*data*/   Type.FindData.out_ptr);
 
-       WinFile.FormatMessage =
+       SysFile.FormatMessage =
          declareFFI("FormatMessageW", ctypes.winapi_abi,
-                    /*return*/ Types.DWORD,
-                    /*flags*/  Types.DWORD,
-                    /*source*/ Types.void_t.in_ptr,
-                    /*msgid*/  Types.DWORD,
-                    /*langid*/ Types.DWORD,
-                    /*buf*/    Types.out_wstring,
-                    /*size*/   Types.DWORD,
-                    /*Arguments*/Types.void_t.in_ptr
+                    /*return*/ Type.DWORD,
+                    /*flags*/  Type.DWORD,
+                    /*source*/ Type.void_t.in_ptr,
+                    /*msgid*/  Type.DWORD,
+                    /*langid*/ Type.DWORD,
+                    /*buf*/    Type.out_wstring,
+                    /*size*/   Type.DWORD,
+                    /*Arguments*/Type.void_t.in_ptr
                    );
 
-       WinFile.GetCurrentDirectory =
+       SysFile.GetCurrentDirectory =
          declareFFI("GetCurrentDirectoryW", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_DWORD,
-                    /*length*/ Types.DWORD,
-                    /*buf*/    Types.out_path
+                    /*return*/ Type.zero_or_DWORD,
+                    /*length*/ Type.DWORD,
+                    /*buf*/    Type.out_path
                    );
 
-       WinFile.GetFileInformationByHandle =
+       SysFile.GetFileInformationByHandle =
          declareFFI("GetFileInformationByHandle", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*handle*/ Types.HANDLE,
-                    /*info*/   Types.FILE_INFORMATION.out_ptr);
+                    /*return*/ Type.zero_or_nothing,
+                    /*handle*/ Type.HANDLE,
+                    /*info*/   Type.FILE_INFORMATION.out_ptr);
 
-       WinFile.MoveFileEx =
+       SysFile.MoveFileEx =
          declareFFI("MoveFileExW", ctypes.winapi_abi,
-                    /*return*/   Types.zero_or_nothing,
-                    /*sourcePath*/ Types.path,
-                    /*destPath*/ Types.path,
-                    /*flags*/    Types.DWORD
+                    /*return*/   Type.zero_or_nothing,
+                    /*sourcePath*/ Type.path,
+                    /*destPath*/ Type.path,
+                    /*flags*/    Type.DWORD
                    );
 
-       WinFile.ReadFile =
+       SysFile.ReadFile =
          declareFFI("ReadFile", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*file*/   Types.HANDLE,
-                    /*buffer*/ Types.voidptr_t,
-                    /*nbytes*/ Types.DWORD,
-                    /*nbytes_read*/Types.DWORD.out_ptr,
-                    /*overlapped*/Types.void_t.inout_ptr // FIXME: Implement?
+                    /*return*/ Type.zero_or_nothing,
+                    /*file*/   Type.HANDLE,
+                    /*buffer*/ Type.voidptr_t,
+                    /*nbytes*/ Type.DWORD,
+                    /*nbytes_read*/Type.DWORD.out_ptr,
+                    /*overlapped*/Type.void_t.inout_ptr // FIXME: Implement?
          );
 
-       WinFile.RemoveDirectory =
+       SysFile.RemoveDirectory =
          declareFFI("RemoveDirectoryW", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*path*/   Types.path);
+                    /*return*/ Type.zero_or_nothing,
+                    /*path*/   Type.path);
 
-       WinFile.SetCurrentDirectory =
+       SysFile.SetCurrentDirectory =
          declareFFI("SetCurrentDirectoryW", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*path*/   Types.path
+                    /*return*/ Type.zero_or_nothing,
+                    /*path*/   Type.path
                    );
 
-       WinFile.SetEndOfFile =
+       SysFile.SetEndOfFile =
          declareFFI("SetEndOfFile", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*file*/   Types.HANDLE);
+                    /*return*/ Type.zero_or_nothing,
+                    /*file*/   Type.HANDLE);
 
-       WinFile.SetFilePointer =
+       SysFile.SetFilePointer =
          declareFFI("SetFilePointer", ctypes.winapi_abi,
-                    /*return*/ Types.negative_or_DWORD,
-                    /*file*/   Types.HANDLE,
-                    /*distlow*/Types.long,
-                    /*disthi*/ Types.long.in_ptr,
-                    /*method*/ Types.DWORD);
+                    /*return*/ Type.negative_or_DWORD,
+                    /*file*/   Type.HANDLE,
+                    /*distlow*/Type.long,
+                    /*disthi*/ Type.long.in_ptr,
+                    /*method*/ Type.DWORD);
 
-       WinFile.WriteFile =
+       SysFile.WriteFile =
          declareFFI("WriteFile", ctypes.winapi_abi,
-                    /*return*/ Types.zero_or_nothing,
-                    /*file*/   Types.HANDLE,
-                    /*buffer*/ Types.voidptr_t,
-                    /*nbytes*/ Types.DWORD,
-                    /*nbytes_wr*/Types.DWORD.out_ptr,
-                    /*overlapped*/Types.void_t.inout_ptr // FIXME: Implement?
+                    /*return*/ Type.zero_or_nothing,
+                    /*file*/   Type.HANDLE,
+                    /*buffer*/ Type.voidptr_t,
+                    /*nbytes*/ Type.DWORD,
+                    /*nbytes_wr*/Type.DWORD.out_ptr,
+                    /*overlapped*/Type.void_t.inout_ptr // FIXME: Implement?
          );
 
-        WinFile.FlushFileBuffers =
+        SysFile.FlushFileBuffers =
           declareFFI("FlushFileBuffers", ctypes.winapi_abi,
-                     /*return*/ Types.zero_or_nothing,
-                     /*file*/   Types.HANDLE);
+                     /*return*/ Type.zero_or_nothing,
+                     /*file*/   Type.HANDLE);
      };
-     exports.OS.Win.File._init = init;
+
+     exports.OS.Win = {
+       File: {
+           _init:  init
+       }
+     };
    })(this);
 }
--- a/toolkit/components/osfile/modules/osfile_win_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_win_front.jsm
@@ -14,45 +14,46 @@
     // We do not wish osfile_win_front.jsm to be used directly as a main thread
     // module yet.
     throw new Error("osfile_win_front.jsm cannot be used from the main thread yet");
   }
 
   (function(exports) {
      "use strict";
 
-     exports.OS = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm").OS;
-     let Path = require("resource://gre/modules/osfile/ospath.jsm");
 
       // exports.OS.Win is created by osfile_win_back.jsm
      if (exports.OS && exports.OS.File) {
         return; // Avoid double-initialization
      }
 
+     let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+     let Path = require("resource://gre/modules/osfile/ospath.jsm");
+     let SysAll = require("resource://gre/modules/osfile/osfile_win_allthreads.jsm");
      exports.OS.Win.File._init();
      let Const = exports.OS.Constants.Win;
      let WinFile = exports.OS.Win.File;
-     let LOG = OS.Shared.LOG.bind(OS.Shared, "Win front-end");
+     let Type = WinFile.Type;
 
      // Mutable thread-global data
      // In the Windows implementation, methods |read| and |write|
      // require passing a pointer to an int32 to determine how many
      // bytes have been read/written. In C, this is a benigne operation,
      // but in js-ctypes, this has a cost. Rather than re-allocating a
      // C int32 and a C int32* for each |read|/|write|, we take advantage
      // of the fact that the state is thread-private -- hence that two
      // |read|/|write| operations cannot take place at the same time --
      // and we use the following global mutable values:
      let gBytesRead = new ctypes.int32_t(-1);
      let gBytesReadPtr = gBytesRead.address();
      let gBytesWritten = new ctypes.int32_t(-1);
      let gBytesWrittenPtr = gBytesWritten.address();
 
      // Same story for GetFileInformationByHandle
-     let gFileInfo = new OS.Shared.Type.FILE_INFORMATION.implementation();
+     let gFileInfo = new Type.FILE_INFORMATION.implementation();
      let gFileInfoPtr = gFileInfo.address();
 
      /**
       * Representation of a file.
       *
       * You generally do not need to call this constructor yourself. Rather,
       * to open a file, use function |OS.File.open|.
       *
@@ -383,17 +384,17 @@
       * - {bool} ignoreExisting If |true|, do not fail if the
       * directory already exists.
       */
      File.makeDir = function makeDir(path, options = {}) {
        let security = options.winSecurity || null;
        let result = WinFile.CreateDirectory(path, security);
        if (result ||
            options.ignoreExisting &&
-           ctypes.winLastError == OS.Constants.Win.ERROR_ALREADY_EXISTS) {
+           ctypes.winLastError == Const.ERROR_ALREADY_EXISTS) {
         return;
        }
        throw new File.Error("makeDir");
      };
 
      /**
       * Copy a file to a destination.
       *
@@ -460,17 +461,17 @@
        throw_on_zero("move",
          WinFile.MoveFileEx(sourcePath, destPath, flags)
        );
      };
 
      /**
       * A global value used to receive data during time conversions.
       */
-     let gSystemTime = new OS.Shared.Type.SystemTime.implementation();
+     let gSystemTime = new Type.SystemTime.implementation();
      let gSystemTimePtr = gSystemTime.address();
 
      /**
       * Utility function: convert a FILETIME to a JavaScript Date.
       */
      let FILETIME_to_Date = function FILETIME_to_Date(fileTime) {
        if (fileTime == null) {
          throw new TypeError("Expecting a non-null filetime");
@@ -509,31 +510,31 @@
          this._pattern = path + "\\" + options.winPattern;
        } else {
          this._pattern = path + "\\*";
        }
        this._path = path;
 
        // Pre-open the first item.
        this._first = true;
-       this._findData = new OS.Shared.Type.FindData.implementation();
+       this._findData = new Type.FindData.implementation();
        this._findDataPtr = this._findData.address();
        this._handle = WinFile.FindFirstFile(this._pattern, this._findDataPtr);
        if (this._handle == Const.INVALID_HANDLE_VALUE) {
          let error = ctypes.winLastError;
          this._findData = null;
          this._findDataPtr = null;
          if (error == Const.ERROR_FILE_NOT_FOUND) {
            // Directory is empty, let's behave as if it were closed
-           LOG("Directory is empty");
+           SharedAll.LOG("Directory is empty");
            this._closed = true;
            this._exists = true;
          } else if (error == Const.ERROR_PATH_NOT_FOUND) {
            // Directory does not exist, let's throw if we attempt to walk it
-           LOG("Directory does not exist");
+           SharedAll.LOG("Directory does not exist");
            this._closed = true;
            this._exists = false;
          } else {
            throw new File.Error("DirectoryIterator", error);
          }
        } else {
          this._closed = false;
          this._exists = true;
@@ -645,21 +646,21 @@
 
        if (!parent) {
          throw new TypeError("Empty parent");
        }
        this._parent = parent;
 
        let path = Path.join(this._parent, name);
 
-       exports.OS.Shared.Win.AbstractEntry.call(this, isDir, isSymLink, name,
-                                                winCreationDate, winLastWriteDate,
-                                                winLastAccessDate, path);
+       SysAll.AbstractEntry.call(this, isDir, isSymLink, name,
+         winCreationDate, winLastWriteDate,
+         winLastAccessDate, path);
      };
-     File.DirectoryIterator.Entry.prototype = Object.create(exports.OS.Shared.Win.AbstractEntry.prototype);
+     File.DirectoryIterator.Entry.prototype = Object.create(SysAll.AbstractEntry.prototype);
 
      /**
       * Return a version of an instance of
       * File.DirectoryIterator.Entry that can be sent from a worker
       * thread to the main thread. Note that deserialization is
       * asymmetric and returns an object with a different
       * implementation.
       */
@@ -690,23 +691,23 @@
        let isDir = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
        let isSymLink = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
        
        let winBirthDate = FILETIME_to_Date(stat.ftCreationTime);
        let lastAccessDate = FILETIME_to_Date(stat.ftLastAccessTime);
        let lastWriteDate = FILETIME_to_Date(stat.ftLastWriteTime);
 
        let value = ctypes.UInt64.join(stat.nFileSizeHigh, stat.nFileSizeLow);
-       let size = exports.OS.Shared.Type.uint64_t.importFromC(value);
+       let size = Type.uint64_t.importFromC(value);
 
-       exports.OS.Shared.Win.AbstractInfo.call(this, isDir, isSymLink, size,
-                                               winBirthDate, lastAccessDate,
-                                               lastWriteDate);
+       SysAll.AbstractInfo.call(this, isDir, isSymLink, size,
+         winBirthDate, lastAccessDate,
+         lastWriteDate);
      };
-     File.Info.prototype = Object.create(exports.OS.Shared.Win.AbstractInfo.prototype);
+     File.Info.prototype = Object.create(SysAll.AbstractInfo.prototype);
 
      /**
       * Return a version of an instance of File.Info that can be sent
       * from a worker thread to the main thread. Note that deserialization
       * is asymmetric and returns an object with a different implementation.
       */
      File.Info.toMsg = function toMsg(stat) {
        if (!stat instanceof File.Info) {
@@ -739,63 +740,64 @@
          return file.stat();
        } finally {
          file.close();
        }
      };
      // All of the following is required to ensure that File.stat
      // also works on directories.
      const FILE_STAT_MODE = {
-       read:true
+       read: true
      };
      const FILE_STAT_OPTIONS = {
        // Directories can be opened neither for reading(!) nor for writing
        winAccess: 0,
        // Directories can only be opened with backup semantics(!)
-       winFlags: OS.Constants.Win.FILE_FLAG_BACKUP_SEMANTICS,
-       winDisposition: OS.Constants.Win.OPEN_EXISTING
+       winFlags: Const.FILE_FLAG_BACKUP_SEMANTICS,
+       winDisposition: Const.OPEN_EXISTING
      };
 
      File.read = exports.OS.Shared.AbstractFile.read;
      File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
+     File.openUnique = exports.OS.Shared.AbstractFile.openUnique;
      File.removeDir = exports.OS.Shared.AbstractFile.removeDir;
 
      /**
       * Get the current directory by getCurrentDirectory.
       */
      File.getCurrentDirectory = function getCurrentDirectory() {
-           // This function is more complicated than one could hope.
-           //
-           // This is due to two facts:
-           // - the maximal length of a path under Windows is not completely
-           //  specified (there is a constant MAX_PATH, but it is quite possible
-           //  to create paths that are much larger, see bug 744413);
-           // - if we attempt to call |GetCurrentDirectory| with a buffer that
-           //  is too short, it returns the length of the current directory, but
-           //  this length might be insufficient by the time we can call again
-           //  the function with a larger buffer, in the (unlikely byt possible)
-           //  case in which the process changes directory to a directory with
-           //  a longer name between both calls.
-           let buffer_size = 4096;
-           while (true) {
-             let array = new (ctypes.ArrayType(ctypes.jschar, buffer_size))();
+       // This function is more complicated than one could hope.
+       //
+       // This is due to two facts:
+       // - the maximal length of a path under Windows is not completely
+       //  specified (there is a constant MAX_PATH, but it is quite possible
+       //  to create paths that are much larger, see bug 744413);
+       // - if we attempt to call |GetCurrentDirectory| with a buffer that
+       //  is too short, it returns the length of the current directory, but
+       //  this length might be insufficient by the time we can call again
+       //  the function with a larger buffer, in the (unlikely but possible)
+       //  case in which the process changes directory to a directory with
+       //  a longer name between both calls.
+       //
+       let buffer_size = 4096;
+       while (true) {
+         let array = new (ctypes.ArrayType(ctypes.jschar, buffer_size))();
          let expected_size = throw_on_zero("getCurrentDirectory",
-               WinFile.GetCurrentDirectory(buffer_size, array)
-             );
-             if (expected_size <= buffer_size) {
-               return array.readString();
-             }
-             // At this point, we are in a case in which our buffer was not
-             // large enough to hold the name of the current directory.
-             // Consequently
-
-             // Note that, even in crazy scenarios, the loop will eventually
-             // converge, as the length of the paths cannot increase infinitely.
-             buffer_size = expected_size;
-           }
+           WinFile.GetCurrentDirectory(buffer_size, array)
+         );
+         if (expected_size <= buffer_size) {
+           return array.readString();
+         }
+         // At this point, we are in a case in which our buffer was not
+         // large enough to hold the name of the current directory.
+         // Consequently, we need to increase the size of the buffer.
+         // Note that, even in crazy scenarios, the loop will eventually
+         // converge, as the length of the paths cannot increase infinitely.
+         buffer_size = expected_size + 1 /* to store \0 */;
+       }
      };
 
      /**
       * Set the current directory by setCurrentDirectory.
       */
      File.setCurrentDirectory = function setCurrentDirectory(path) {
        throw_on_zero("setCurrentDirectory",
          WinFile.SetCurrentDirectory(path));
@@ -811,17 +813,17 @@
          get: function() {
            return this.getCurrentDirectory();
          }
        }
      );
 
      // Utility functions, used for error-handling
      function error_or_file(maybe) {
-       if (maybe == exports.OS.Constants.Win.INVALID_HANDLE_VALUE) {
+       if (maybe == Const.INVALID_HANDLE_VALUE) {
          throw new File.Error("open");
        }
        return new File(maybe);
      }
      function throw_on_zero(operation, result) {
        if (result == 0) {
          throw new File.Error(operation);
        }
@@ -836,18 +838,17 @@
      function throw_on_null(operation, result) {
        if (result == null || (result.isNull && result.isNull())) {
          throw new File.Error(operation);
        }
        return result;
      }
 
      File.Win = exports.OS.Win.File;
-     File.Error = exports.OS.Shared.Win.Error;
+     File.Error = SysAll.Error;
      exports.OS.File = File;
+     exports.OS.Shared.Type = Type;
 
-     exports.OS.Path = exports.Path;
-
-     Object.defineProperty(File, "POS_START", { value: OS.Shared.POS_START });
-     Object.defineProperty(File, "POS_CURRENT", { value: OS.Shared.POS_CURRENT });
-     Object.defineProperty(File, "POS_END", { value: OS.Shared.POS_END });
+     Object.defineProperty(File, "POS_START", { value: SysAll.POS_START });
+     Object.defineProperty(File, "POS_CURRENT", { value: SysAll.POS_CURRENT });
+     Object.defineProperty(File, "POS_END", { value: SysAll.POS_END });
    })(this);
 }
--- a/toolkit/components/osfile/osfile.jsm
+++ b/toolkit/components/osfile/osfile.jsm
@@ -11,24 +11,22 @@ if (typeof Components != "undefined") {
   Components.utils.import("resource://gre/modules/osfile/osfile_async_front.jsm", this);
 } else {
   // At this stage, we need to import all sources at once to avoid
   // a unique failure on tbpl + talos that seems caused by a
   // what looks like a nested event loop bug (see bug 794091).
 #ifdef XP_WIN
   importScripts(
     "resource://gre/modules/workers/require.js",
-    "resource://gre/modules/osfile/osfile_win_allthreads.jsm",
     "resource://gre/modules/osfile/osfile_win_back.jsm",
     "resource://gre/modules/osfile/osfile_shared_front.jsm",
     "resource://gre/modules/osfile/osfile_win_front.jsm"
   );
 #else
   importScripts(
     "resource://gre/modules/workers/require.js",
-    "resource://gre/modules/osfile/osfile_unix_allthreads.jsm",
     "resource://gre/modules/osfile/osfile_unix_back.jsm",
     "resource://gre/modules/osfile/osfile_shared_front.jsm",
     "resource://gre/modules/osfile/osfile_unix_front.jsm"
   );
 #endif
   OS.Path = require("resource://gre/modules/osfile/ospath.jsm");
 }
--- a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
+++ b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
@@ -56,16 +56,19 @@ let maketest = function(prefix, test) {
         throw new TypeError("The test did not return a promise");
       }
       utils.info("This was a promise");
       // The test returns a promise
       result = result.then(function test_complete() {
         utils.info("Complete");
       }, function catch_uncaught_errors(err) {
         utils.fail("Uncaught error " + err);
+        if (err && typeof err == "object" && "message" in err) {
+          utils.fail("(" + err.message + ")");
+        }
         if (err && typeof err == "object" && "stack" in err) {
           utils.fail("at " + err.stack);
         }
       });
       return result;
     } catch (x) {
       utils.fail("Error " + x + " at " + x.stack);
       return null;
@@ -919,17 +922,14 @@ let test_duration = maketest("duration",
       tmpPath: tmpPath
     };
     backupDuration = writeAtomicOptions.outExecutionDuration;
 
     yield OS.File.writeAtomic(pathDest, contents, writeAtomicOptions);
     test.ok(copyOptions.outExecutionDuration >= backupDuration, "duration has increased 3");
     OS.File.remove(pathDest);
 
-    OS.Shared.TEST = true;
-
     // Testing an operation that doesn't take arguments at all
     let file = yield OS.File.open(pathSource);
     yield file.stat();
     yield file.close();
-    Services.prefs.setBoolPref("toolkit.osfile.log", false);
   });
 });
--- a/toolkit/components/osfile/tests/mochi/worker_handler.js
+++ b/toolkit/components/osfile/tests/mochi/worker_handler.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function worker_handler(worker) {
   worker.onerror = function(error) {
     error.preventDefault();
-    ok(false, "error "+error);
+    ok(false, "Worker error " + error.message);
   }
   worker.onmessage = function(msg) {
     ok(true, "MAIN: onmessage " + JSON.stringify(msg.data));
     switch (msg.data.kind) {
     case "is":
       SimpleTest.ok(msg.data.outcome, msg.data.description +
          "( "+ msg.data.a + " ==? " + msg.data.b + ")" );
       return;
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
@@ -1,12 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 importScripts('worker_test_osfile_shared.js');
+importScripts("resource://gre/modules/workers/require.js");
+
+let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+SharedAll.Config.DEBUG = true;
 
 function should_throw(f) {
   try {
     f();
   } catch (x) {
     return x;
   }
   return null;
@@ -54,46 +58,46 @@ function test_offsetby() {
   let buf = new ArrayBuffer(LENGTH);
   let view = new Uint8Array(buf);
   let i;
   for (i = 0; i < LENGTH; ++i) {
     view[i] = i;
   }
 
   // Walk through the array with offsetBy by 8 bits
-  let uint8 = OS.Shared.Type.uint8_t.in_ptr.implementation(buf);
+  let uint8 = SharedAll.Type.uint8_t.in_ptr.implementation(buf);
   for (i = 0; i < LENGTH; ++i) {
-    let value = OS.Shared.offsetBy(uint8, i).contents;
+    let value = SharedAll.offsetBy(uint8, i).contents;
     if (value != i%256) {
       is(value, i % 256, "test_offsetby: Walking through array with offsetBy (8 bits)");
       break;
     }
   }
 
   // Walk again by 16 bits
-  let uint16 = OS.Shared.Type.uint16_t.in_ptr.implementation(buf);
+  let uint16 = SharedAll.Type.uint16_t.in_ptr.implementation(buf);
   let view2 = new Uint16Array(buf);
   for (i = 0; i < LENGTH/2; ++i) {
-    let value = OS.Shared.offsetBy(uint16, i).contents;
+    let value = SharedAll.offsetBy(uint16, i).contents;
     if (value != view2[i]) {
       is(value, view2[i], "test_offsetby: Walking through array with offsetBy (16 bits)");
       break;
     }
   }
 
   // Ensure that offsetBy(..., 0) is idempotent
-  let startptr = OS.Shared.offsetBy(uint8, 0);
-  let startptr2 = OS.Shared.offsetBy(startptr, 0);
+  let startptr = SharedAll.offsetBy(uint8, 0);
+  let startptr2 = SharedAll.offsetBy(startptr, 0);
   is(startptr.toString(), startptr2.toString(), "test_offsetby: offsetBy(..., 0) is idmpotent");
 
   // Ensure that offsetBy(ptr, ...) does not work if ptr is a void*
   let ptr = ctypes.voidptr_t(0);
   let exn;
   try {
-    OS.Shared.Utils.offsetBy(ptr, 1);
+    SharedAll.offsetBy(ptr, 1);
   } catch (x) {
     exn = x;
   }
   ok(!!exn, "test_offsetby: rejected offsetBy with void*");
 
   info("test_offsetby: complete");
 }
 
--- a/toolkit/components/osfile/tests/xpcshell/test_path.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_path.js
@@ -140,11 +140,15 @@ function run_test()
   do_check_eq(Unix.normalize("a/b/c/../../../d/e/f"), "d/e/f");
   do_check_fail(function() Unix.normalize("/a/b/c/../../../../d/e/f"));
 
   do_check_eq(Unix.join("/tmp", "foo", "bar"), "/tmp/foo/bar", "join /tmp,foo,bar");
   do_check_eq(Unix.join("/tmp", "/foo", "bar"), "/foo/bar", "join /tmp,/foo,bar");
 
   do_print("Testing the presence of ospath.jsm");
   let Scope = {};
-  Components.utils.import("resource://gre/modules/osfile/ospath.jsm", Scope);
+  try {
+    Components.utils.import("resource://gre/modules/osfile/ospath.jsm", Scope);
+  } catch (ex) {
+    // Can't load ospath
+  }
   do_check_true(!!Scope.basename);
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/osfile/tests/xpcshell/test_unique.js
@@ -0,0 +1,90 @@
+"use strict";
+
+Components.utils.import("resource://gre/modules/osfile.jsm");
+Components.utils.import("resource://gre/modules/Task.jsm");
+
+function run_test() {
+  run_next_test();
+}
+
+function testFiles(filename) {
+  return Task.spawn(function() {
+    const MAX_TRIES = 10;
+    let currentDir = yield OS.File.getCurrentDirectory();
+    let path = OS.Path.join(currentDir, filename);
+    let exists = yield OS.File.exists(path);
+    // Check a file with the same name doesn't exist already
+    do_check_false(exists);
+
+    // Ensure that openUnique() uses the file name if there is no file with that name already.
+    let openedFile = yield OS.File.openUnique(path);
+    do_print("\nCreate new file: " + openedFile.path);
+    yield openedFile.file.close();
+    exists = yield OS.File.exists(openedFile.path);
+    do_check_true(exists);
+    do_check_eq(path, openedFile.path);
+    let fileInfo = yield OS.File.stat(openedFile.path);
+    do_check_true(fileInfo.size == 0);
+
+    // Ensure that openUnique() creates a new file name using a HEX number, as the original name is already taken.
+    openedFile = yield OS.File.openUnique(path);
+    do_print("\nCreate unique HEX file: " + openedFile.path);
+    yield openedFile.file.close();
+    exists = yield OS.File.exists(openedFile.path);
+    do_check_true(exists);
+    let fileInfo = yield OS.File.stat(openedFile.path);
+    do_check_true(fileInfo.size == 0);
+
+    // Ensure that openUnique() generates different file names each time, using the HEX number algorithm
+    let filenames = new Set();
+    for (let i=0; i < MAX_TRIES; i++) {
+      openedFile = yield OS.File.openUnique(path);
+      yield openedFile.file.close();
+      filenames.add(openedFile.path);
+    }
+
+    do_check_eq(filenames.size, MAX_TRIES);
+
+    // Ensure that openUnique() creates a new human readable file name using, as the original name is already taken.
+    openedFile = yield OS.File.openUnique(path, {humanReadable : true});
+    do_print("\nCreate unique Human Readable file: " + openedFile.path);
+    yield openedFile.file.close();
+    exists = yield OS.File.exists(openedFile.path);
+    do_check_true(exists);
+    let fileInfo = yield OS.File.stat(openedFile.path);
+    do_check_true(fileInfo.size == 0);
+
+    // Ensure that openUnique() generates different human readable file names each time
+    filenames = new Set();
+    for (let i=0; i < MAX_TRIES; i++) {
+      openedFile = yield OS.File.openUnique(path, {humanReadable : true});
+      yield openedFile.file.close();
+      filenames.add(openedFile.path);
+    }
+
+    do_check_eq(filenames.size, MAX_TRIES);
+
+    let exn;
+    try {
+      for (let i=0; i < 100; i++) {
+        openedFile = yield OS.File.openUnique(path, {humanReadable : true});
+        yield openedFile.file.close();
+      }
+    } catch (ex) {
+      exn = ex;
+    }
+
+    do_print("Ensure that this raises the correct error");
+    do_check_true(!!exn);
+    do_check_true(exn instanceof OS.File.Error);
+    do_check_true(exn.becauseExists);
+  });
+}
+
+add_task(function test_unique() {
+  OS.Shared.DEBUG = true;
+  // Tests files with extension
+  yield testFiles("dummy_unique_file.txt");
+  // Tests files with no extension
+  yield testFiles("dummy_unique_file_no_ext");
+});
--- a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
@@ -7,8 +7,9 @@ tail =
 [test_osfile_async.js]
 [test_osfile_async_bytes.js]
 [test_profiledir.js]
 [test_logging.js]
 [test_creationDate.js]
 [test_exception.js]
 [test_path_constants.js]
 [test_removeDir.js]
+[test_unique.js]
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -1133,15 +1133,38 @@
           else
             this._findFailedString = null;
 
           if (this._findMode != this.FIND_NORMAL && !this.hidden)
             this._setFindCloseTimeout();
         ]]></body>
       </method>
 
+      <!--
+        - This handler may cancel a request to focus content by returning |false|
+        - explicitly.
+        -->
+      <method name="shouldFocusContent">
+        <body><![CDATA[
+          const fm = Components.classes["@mozilla.org/focus-manager;1"]
+                               .getService(Components.interfaces.nsIFocusManager);
+          if (fm.focusedWindow != window)
+            return false;
+
+          let focusedElement = fm.focusedElement;
+          if (!focusedElement)
+            return false;
+
+          let bindingParent = document.getBindingParent(focusedElement);
+          if (bindingParent != this && bindingParent != this._findField)
+            return false;
+
+          return true;
+        ]]></body>
+      </method>
+
     </implementation>
 
     <handlers>
       <handler event="keypress" keycode="VK_ESCAPE" phase="capturing" action="this.close();" preventdefault="true"/>
     </handlers>
   </binding>
 </bindings>
--- a/toolkit/devtools/Loader.jsm
+++ b/toolkit/devtools/Loader.jsm
@@ -115,16 +115,17 @@ var SrcdirProvider = {
       paths: {
         "": "resource://gre/modules/commonjs/",
         "main": mainURI,
         "devtools": devtoolsURI,
         "devtools/server": serverURI,
         "devtools/toolkit/webconsole": webconsoleURI,
         "devtools/app-actor-front": appActorURI,
         "devtools/styleinspector/css-logic": cssLogicURI,
+        "devtools/css-color": cssColorURI,
         "devtools/touch-events": touchEventsURI,
         "devtools/client": clientURI,
         "escodegen": escodegenURI,
         "estraverse": estraverseURI
       },
       globals: loaderGlobals
     });
 
--- a/toolkit/modules/Finder.jsm
+++ b/toolkit/modules/Finder.jsm
@@ -111,38 +111,56 @@ Finder.prototype = {
 
     fastFind.collapseSelection();
     this.enableSelection();
 
     this._restoreOriginalOutline();
   },
 
   focusContent: function() {
+    // Allow Finder listeners to cancel focusing the content.
+    for (let l of this._listeners) {
+      if (!l.shouldFocusContent())
+        return;
+    }
+
     let fastFind = this._fastFind;
-
+    const fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
     try {
-      // Try to find the best possible match that should receive focus.
+      // Try to find the best possible match that should receive focus and
+      // block scrolling on focus since find already scrolls. Further
+      // scrolling is due to user action, so don't override this.
       if (fastFind.foundLink) {
-        fastFind.foundLink.focus();
+        fm.setFocus(fastFind.foundLink, fm.FLAG_NOSCROLL);
       } else if (fastFind.foundEditable) {
-        fastFind.foundEditable.focus();
+        fm.setFocus(fastFind.foundEditable, fm.FLAG_NOSCROLL);
         fastFind.collapseSelection();
       } else {
         this._getWindow().focus()
       }
     } catch (e) {}
   },
 
   keyPress: function (aEvent) {
     let controller = this._getSelectionController(this._getWindow());
 
     switch (aEvent.keyCode) {
       case Ci.nsIDOMKeyEvent.DOM_VK_RETURN:
-        if (this._fastFind.foundLink) // Todo: Handle ctrl click.
-          this._fastFind.foundLink.click();
+        if (this._fastFind.foundLink) {
+          let view = this._fastFind.foundLink.ownerDocument.defaultView;
+          this._fastFind.foundLink.dispatchEvent(new view.MouseEvent("click", {
+            view: view,
+            cancelable: true,
+            bubbles: true,
+            ctrlKey: aEvent.ctrlKey,
+            altKey: aEvent.altKey,
+            shiftKey: aEvent.shiftKey,
+            metaKey: aEvent.metaKey
+          }));
+        }
         break;
       case Ci.nsIDOMKeyEvent.DOM_VK_TAB:
         let direction = Services.focus.MOVEFOCUS_FORWARD;
         if (aEvent.shiftKey) {
           direction = Services.focus.MOVEFOCUS_BACKWARD;
         }
         Services.focus.moveFocus(this._getWindow(), null, direction, 0);
         break;
--- a/toolkit/modules/RemoteFinder.jsm
+++ b/toolkit/modules/RemoteFinder.jsm
@@ -109,16 +109,22 @@ RemoteFinderListener.prototype = {
   ],
 
   onFindResult: function (aResult, aFindBackwards, aLinkURL) {
     let data = { result: aResult, findBackwards: aFindBackwards,
                  linkURL: aLinkURL, searchString: this._finder.searchString };
     this._global.sendAsyncMessage("Finder:Result", data);
   },
 
+  //XXXmikedeboer-20131016: implement |shouldFocusContent| here to mitigate
+  //                        issues like bug 921338 and bug 921308.
+  shouldFocusContent: function () {
+    return true;
+  },
+
   receiveMessage: function (aMessage) {
     let data = aMessage.data;
 
     switch (aMessage.name) {
       case "Finder:CaseSensitive":
         this._finder.caseSensitive = data.caseSensitive;
         break;
 
--- a/toolkit/mozapps/update/test/chrome/chrome.ini
+++ b/toolkit/mozapps/update/test/chrome/chrome.ini
@@ -5,37 +5,36 @@ support-files =
 
 [test_0011_check_basic.xul]
 [test_0012_check_basic_license.xul]
 [test_0013_check_incompat_basic.xul]
 [test_0014_check_incompat_basic_license.xul]
 [test_0015_check_incompat_basic_addons.xul]
 [test_0016_check_incompat_basic_license_addons.xul]
 [test_0017_check_staging_basic.xul]
+skip-if = os == 'linux'
+reason = Bug 918029 - timeout caused by copying too many files.
 [test_0021_check_billboard.xul]
 [test_0022_check_billboard_license.xul]
 [test_0023_check_incompat_billboard.xul]
 [test_0024_check_incompat_billboard_license.xul]
 [test_0025_check_incompat_billboard_addons.xul]
 [test_0026_check_incompat_billboard_license_addons.xul]
-[test_0027_check_staging_billboard.xul]
 [test_0031_available_basic.xul]
 [test_0032_available_basic_license.xul]
 [test_0033_available_incompat_basic.xul]
 [test_0034_available_incompat_basic_license.xul]
 [test_0035_available_incompat_basic_addons.xul]
 [test_0036_available_incompat_basic_license_addons.xul]
-[test_0037_available_staging_basic.xul]
 [test_0041_available_billboard.xul]
 [test_0042_available_billboard_license.xul]
 [test_0043_available_incompat_billboard.xul]
 [test_0044_available_incompat_billboard_license.xul]
 [test_0045_available_incompat_billboard_addons.xul]
 [test_0046_available_incompat_billboard_license_addons.xul]
-[test_0047_available_staging_billboard.xul]
 [test_0051_check_error_xml_malformed.xul]
 [test_0052_check_no_updates.xul]
 [test_0053_check_billboard_license_noAttr.xul]
 [test_0054_check_billboard_license_404.xul]
 [test_0061_check_verifyFailPartial_noComplete.xul]
 [test_0062_check_verifyFailComplete_noPartial.xul]
 [test_0063_check_verifyFailPartialComplete.xul]
 [test_0064_check_verifyFailPartial_successComplete.xul]
deleted file mode 100644
--- a/toolkit/mozapps/update/test/chrome/test_0027_check_staging_billboard.xul
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0"?>
-<!--
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
--->
-
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
-
-<window title="Update Wizard pages: update check, billboard, download with staging, and finished"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        onload="runTestDefault();">
-<script type="application/javascript"
-        src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-<script type="application/javascript"
-        src="utils.js"/>
-
-<script type="application/javascript">
-<![CDATA[
-
-const TESTS = [ {
-  pageid: PAGEID_CHECKING
-}, {
-  pageid: PAGEID_FOUND_BILLBOARD,
-  extraDelayedCheckFunction: checkRemoteContentState,
-  expectedRemoteContentState: "loading",
-  extraDelayedFinishFunction: addRemoteContentLoadListener
-}, {
-  pageid: PAGEID_FOUND_BILLBOARD,
-  extraStartFunction: waitForRemoteContentLoaded,
-  expectedRemoteContentState: "loaded",
-  buttonClick: "next"
-}, {
-  pageid: PAGEID_DOWNLOADING
-}, {
-  pageid: PAGEID_FINISHED,
-  buttonClick: "extra1"
-} ];
-
-function runTest() {
-  debugDump("entering");
-
-  Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
-
-  let url = URL_UPDATE + "?showBillboard=1&showDetails=1" + getVersionParams();
-  setUpdateURLOverride(url);
-
-  setupTimer(180000); // 180 seconds
-
-  gUP.checkForUpdates();
-}
-
-]]>
-</script>
-
-<body xmlns="http://www.w3.org/1999/xhtml">
-  <p id="display"></p>
-  <div id="content" style="display: none"></div>
-  <pre id="test"></pre>
-</body>
-</window>
deleted file mode 100644
--- a/toolkit/mozapps/update/test/chrome/test_0037_available_staging_basic.xul
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0"?>
-<!--
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
--->
-
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
-
-<window title="Update Wizard pages: basic, download with staging, and finished"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        onload="runTestDefault();">
-<script type="application/javascript"
-        src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-<script type="application/javascript"
-        src="utils.js"/>
-
-<script type="application/javascript">
-<![CDATA[
-
-const TESTS = [ {
-  pageid: PAGEID_FOUND_BASIC,
-  buttonClick: "next"
-}, {
-  pageid: PAGEID_DOWNLOADING
-}, {
-  pageid: PAGEID_FINISHED,
-  buttonClick: "extra1"
-} ];
-
-function runTest() {
-  debugDump("entering");
-
-  Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
-
-  let url = URL_UPDATE + "?showDetails=1&showPrompt=1" + getVersionParams();
-  setUpdateURLOverride(url);
-
-  setupTimer(180000); // 180 seconds
-
-  gAUS.checkForBackgroundUpdates();
-}
-
-]]>
-</script>
-
-<body xmlns="http://www.w3.org/1999/xhtml">
-  <p id="display"></p>
-  <div id="content" style="display: none"></div>
-  <pre id="test"></pre>
-</body>
-</window>
deleted file mode 100644
--- a/toolkit/mozapps/update/test/chrome/test_0047_available_staging_billboard.xul
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0"?>
-<!--
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
--->
-
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
-
-<window title="Update Wizard pages: billboard, download with staging, and finished"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        onload="runTestDefault();">
-<script type="application/javascript"
-        src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-<script type="application/javascript"
-        src="utils.js"/>
-
-<script type="application/javascript">
-<![CDATA[
-
-const TESTS = [ {
-  pageid: PAGEID_FOUND_BILLBOARD,
-  extraDelayedCheckFunction: checkRemoteContentState,
-  expectedRemoteContentState: "loading",
-  extraDelayedFinishFunction: addRemoteContentLoadListener
-}, {
-  pageid: PAGEID_FOUND_BILLBOARD,
-  extraStartFunction: waitForRemoteContentLoaded,
-  expectedRemoteContentState: "loaded",
-  buttonClick: "next"
-}, {
-  pageid: PAGEID_DOWNLOADING,
-}, {
-  pageid: PAGEID_FINISHED,
-  buttonClick: "extra1"
-} ];
-
-function runTest() {
-  debugDump("entering");
-
-  Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
-
-  let url = URL_UPDATE + "?showBillboard=1&showDetails=1&showPrompt=1" +
-            getVersionParams();
-  setUpdateURLOverride(url);
-
-  setupTimer(180000); // 180 seconds
-
-  gAUS.notify(null);
-}
-
-]]>
-</script>
-
-<body xmlns="http://www.w3.org/1999/xhtml">
-  <p id="display"></p>
-  <div id="content" style="display: none"></div>
-  <pre id="test"></pre>
-</body>
-</window>
--- a/widget/gonk/HwcComposer2D.cpp
+++ b/widget/gonk/HwcComposer2D.cpp
@@ -152,21 +152,23 @@ HwcComposer2D::PrepareLayerList(Layer* a
 
     bool fillColor = false;
 
     const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion();
     if (visibleRegion.IsEmpty()) {
         return true;
     }
 
-    float opacity = aLayer->GetEffectiveOpacity();
-    if (opacity < 1) {
+    uint8_t opacity = std::min(0xFF, (int)(aLayer->GetEffectiveOpacity() * 256.0));
+#if ANDROID_VERSION < 18
+    if (opacity < 0xFF) {
         LOGD("%s Layer has planar semitransparency which is unsupported", aLayer->Name());
         return false;
     }
+#endif
 
     nsIntRect clip;
     if (!HwcUtils::CalculateClipRect(aParentTransform * aGLWorldTransform,
                                      aLayer->GetEffectiveClipRect(),
                                      aClip,
                                      &clip))
     {
         LOGD("%s Clip rect is empty. Skip layer", aLayer->Name());
@@ -271,17 +273,17 @@ HwcComposer2D::PrepareLayerList(Layer* a
     hwcLayer.flags = 0;
     hwcLayer.hints = 0;
     hwcLayer.blending = HWC_BLENDING_PREMULT;
 #if ANDROID_VERSION >= 18
     hwcLayer.compositionType = HWC_FRAMEBUFFER;
 
     hwcLayer.acquireFenceFd = -1;
     hwcLayer.releaseFenceFd = -1;
-    hwcLayer.planeAlpha = 0xFF; // Until plane alpha is enabled
+    hwcLayer.planeAlpha = opacity;
 #else
     hwcLayer.compositionType = HwcUtils::HWC_USE_COPYBIT;
 #endif
 
     if (!fillColor) {
         if (state.FormatRBSwapped()) {
             if (!mRBSwapSupport) {
                 LOGD("No R/B swap support in H/W Composer");
@@ -429,16 +431,17 @@ HwcComposer2D::PrepareLayerList(Layer* a
         ColorLayer* colorLayer = aLayer->AsColorLayer();
         if (colorLayer->GetColor().a < 1.0) {
             LOGD("Color layer has semitransparency which is unsupported");
             return false;
         }
         hwcLayer.transform = colorLayer->GetColor().Packed();
     }
 
+    mHwcLayerMap.AppendElement(static_cast<LayerComposite*>(aLayer->ImplData()));
     mList->numHwLayers++;
     return true;
 }
 
 
 #if ANDROID_VERSION >= 18
 bool
 HwcComposer2D::TryHwComposition()
@@ -456,23 +459,39 @@ HwcComposer2D::TryHwComposition()
         if (!ReallocLayerList() || idx >= mMaxLayerCount) {
             LOGE("TryHwComposition failed! Could not add FB layer");
             return false;
         }
     }
 
     Prepare(fbsurface->lastHandle, -1);
 
+    bool fullHwcComposite = true;
     for (int j = 0; j < idx; j++) {
         if (mList->hwLayers[j].compositionType == HWC_FRAMEBUFFER) {
+            // After prepare, if there is an HWC_FRAMEBUFFER layer,
+            // it means full HWC Composition is not possible this time
             LOGD("GPU or Partial HWC Composition");
-            return false;
+            fullHwcComposite = false;
+            break;
         }
     }
 
+    if (!fullHwcComposite) {
+        for (int k=0; k < idx; k++) {
+            if (mList->hwLayers[k].compositionType == HWC_OVERLAY) {
+                // HWC will compose HWC_OVERLAY layers in partial
+                // HWC Composition, so set layer composition flag
+                // on mapped LayerComposite to skip GPU composition
+                mHwcLayerMap[k]->SetLayerComposited(true);
+            }
+        }
+        return false;
+    }
+
     // Full HWC Composition
     Commit();
 
     // No composition on FB layer, so closing releaseFenceFd
     close(mList->hwLayers[idx].releaseFenceFd);
     mList->hwLayers[idx].releaseFenceFd = -1;
     mList->numHwLayers = 0;
     return true;
@@ -549,38 +568,36 @@ HwcComposer2D::Prepare(buffer_handle_t f
 bool
 HwcComposer2D::Commit()
 {
     hwc_display_contents_1_t *displays[HWC_NUM_DISPLAY_TYPES] = { nullptr };
     displays[HWC_DISPLAY_PRIMARY] = mList;
 
     int err = mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
 
-    for (int i = 0; i <= MAX_HWC_LAYERS; i++) {
-        if (mPrevRelFd[i] <= 0) {
-            break;
+    if (!mPrevReleaseFds.IsEmpty()) {
+        // Wait for previous retire Fence to signal.
+        // Denotes contents on display have been replaced.
+        // For buffer-sync, framework should not over-write
+        // prev buffers until we close prev releaseFenceFds
+        sp<Fence> fence = new Fence(mPrevReleaseFds[0]);
+        if (fence->wait(1000) == -ETIME) {
+            LOGE("Wait timed-out for retireFenceFd %d", mPrevReleaseFds[0]);
         }
-        if (!i) {
-            // Wait for previous retire Fence to signal.
-            // Denotes contents on display have been replaced.
-            // For buffer-sync, framework should not over-write
-            // prev buffers until we close prev releaseFenceFds
-            sp<Fence> fence = new Fence(mPrevRelFd[i]);
-            if (fence->wait(1000) == -ETIME) {
-                LOGE("Wait timed-out for retireFenceFd %d", mPrevRelFd[i]);
-            }
+
+        for (int i = 0; i < mPrevReleaseFds.Length(); i++) {
+            close(mPrevReleaseFds[i]);
         }
-        close(mPrevRelFd[i]);
-        mPrevRelFd[i] = -1;
+        mPrevReleaseFds.Clear();
     }
 
-    mPrevRelFd[0] = mList->retireFenceFd;
-    for (uint32_t j = 0; j < (mList->numHwLayers - 1); j++) {
+    mPrevReleaseFds.AppendElement(mList->retireFenceFd);
+    for (uint32_t j=0; j < (mList->numHwLayers - 1); j++) {
         if (mList->hwLayers[j].compositionType == HWC_OVERLAY) {
-            mPrevRelFd[j + 1] = mList->hwLayers[j].releaseFenceFd;
+            mPrevReleaseFds.AppendElement(mList->hwLayers[j].releaseFenceFd);
             mList->hwLayers[j].releaseFenceFd = -1;
         }
     }
 
     mList->retireFenceFd = -1;
     return !err;
 }
 #else
@@ -604,22 +621,24 @@ HwcComposer2D::TryRender(Layer* aRoot,
     if (!aGLWorldTransform.PreservesAxisAlignedRectangles()) {
         LOGD("Render aborted. World transform has non-square angle rotation");
         return false;
     }
 
     MOZ_ASSERT(Initialized());
     if (mList) {
         mList->numHwLayers = 0;
+        mHwcLayerMap.Clear();
     }
 
     // XXX: The clear() below means all rect vectors will be have to be
     // reallocated. We may want to avoid this if possible
     mVisibleRegions.clear();
 
+    MOZ_ASSERT(mHwcLayerMap.IsEmpty());
     if (!PrepareLayerList(aRoot,
                           mScreenRect,
                           gfxMatrix(),
                           aGLWorldTransform))
     {
         LOGD("Render aborted. Nothing was drawn to the screen");
         if (mList) {
            mList->numHwLayers = 0;
--- a/widget/gonk/HwcComposer2D.h
+++ b/widget/gonk/HwcComposer2D.h
@@ -19,18 +19,16 @@
 
 #include "Composer2D.h"
 #include "Layers.h"
 #include <vector>
 #include <list>
 
 #include <hardware/hwcomposer.h>
 
-#define MAX_HWC_LAYERS 15
-
 namespace mozilla {
 
 namespace layers {
 class ContainerLayer;
 class Layer;
 }
 
 //Holds a dynamically allocated vector of rectangles
@@ -78,14 +76,15 @@ private:
     hwc_surface_t           mSur;
     nsIntRect               mScreenRect;
     int                     mMaxLayerCount;
     bool                    mColorFill;
     bool                    mRBSwapSupport;
     //Holds all the dynamically allocated RectVectors needed
     //to render the current frame
     std::list<RectVector>   mVisibleRegions;
-    int                     mPrevRelFd[MAX_HWC_LAYERS + 1];
+    nsTArray<int>           mPrevReleaseFds;
+    nsTArray<layers::LayerComposite*> mHwcLayerMap;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_HwcComposer2D
--- a/widget/windows/winrt/MetroInput.cpp
+++ b/widget/windows/winrt/MetroInput.cpp
@@ -201,16 +201,17 @@ namespace {
 namespace mozilla {
 namespace widget {
 namespace winrt {
 
 MetroInput::MetroInput(MetroWidget* aWidget,
                        UI::Core::ICoreWindow* aWindow)
               : mWidget(aWidget),
                 mChromeHitTestCacheForTouch(false),
+                mCurrentInputLevel(LEVEL_IMPRECISE),
                 mWindow(aWindow)
 {
   LogFunction();
   NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!");
   NS_ASSERTION(aWindow, "Attempted to create MetroInput for null window!");
 
   mTokenPointerPressed.value = 0;
   mTokenPointerReleased.value = 0;
@@ -235,16 +236,61 @@ MetroInput::MetroInput(MetroWidget* aWid
 }
 
 MetroInput::~MetroInput()
 {
   LogFunction();
   UnregisterInputEvents();
 }
 
+
+/**
+ * Tracks the current input level (precise/imprecise) and fires an observer
+ * when the mode changes.
+ */
+void
+MetroInput::UpdateInputLevel(InputPrecisionLevel aInputLevel)
+{
+  // ignore mouse input if we have active touch input.
+  if (aInputLevel == LEVEL_PRECISE && mTouches.Count() > 0) {
+    return;
+  }
+  if (mCurrentInputLevel != aInputLevel) {
+    mCurrentInputLevel = aInputLevel;
+    MetroUtils::FireObserver(mCurrentInputLevel == LEVEL_PRECISE ?
+                               "metro_precise_input" : "metro_imprecise_input");
+  }
+}
+
+/**
+ * Processes an IEdgeGestureEventArgs and returns the input source type
+ * for the event. Also updates input level via UpdateInputLevel.
+ */
+uint16_t
+MetroInput::ProcessInputTypeForGesture(UI::Input::IEdgeGestureEventArgs* aArgs)
+{
+  MOZ_ASSERT(aArgs);
+  UI::Input::EdgeGestureKind kind;
+  aArgs->get_Kind(&kind);
+  switch(kind) {
+    case UI::Input::EdgeGestureKind::EdgeGestureKind_Touch:
+      UpdateInputLevel(LEVEL_PRECISE);
+      return nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+    break;
+    case UI::Input::EdgeGestureKind::EdgeGestureKind_Keyboard:
+      return nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
+    break;
+    case UI::Input::EdgeGestureKind::EdgeGestureKind_Mouse:
+      UpdateInputLevel(LEVEL_IMPRECISE);
+      return nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
+    break;
+  }
+  return nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
+}
+
 /**
  * When the user swipes her/his finger in from the top of the screen,
  * we receive this event.
  *
  * @param sender the CoreDispatcher that fired this event
  * @param aArgs the event-specific args we use when processing this event
  * @returns S_OK
  */
@@ -258,18 +304,17 @@ MetroInput::OnEdgeGestureStarted(UI::Inp
   WidgetSimpleGestureEvent geckoEvent(true,
                                       NS_SIMPLE_GESTURE_EDGE_STARTED,
                                       mWidget.Get(),
                                       0,
                                       0.0);
   mModifierKeyState.Update();
   mModifierKeyState.InitInputEvent(geckoEvent);
   geckoEvent.time = ::GetMessageTime();
-
-  geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+  geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
 
   // Safe
   DispatchEventIgnoreStatus(&geckoEvent);
   return S_OK;
 }
 
 /**
  * This event can be received if the user swipes her/his finger back to
@@ -290,18 +335,17 @@ MetroInput::OnEdgeGestureCanceled(UI::In
   WidgetSimpleGestureEvent geckoEvent(true,
                                       NS_SIMPLE_GESTURE_EDGE_CANCELED,
                                       mWidget.Get(),
                                       0,
                                       0.0);
   mModifierKeyState.Update();
   mModifierKeyState.InitInputEvent(geckoEvent);
   geckoEvent.time = ::GetMessageTime();
-
-  geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+  geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
 
   // Safe
   DispatchEventIgnoreStatus(&geckoEvent);
   return S_OK;
 }
 
 /**
  * This event is received if the user presses ctrl+Z or lifts her/his
@@ -321,25 +365,17 @@ MetroInput::OnEdgeGestureCompleted(UI::I
   WidgetSimpleGestureEvent geckoEvent(true,
                                       NS_SIMPLE_GESTURE_EDGE_COMPLETED,
                                       mWidget.Get(),
                                       0,
                                       0.0);
   mModifierKeyState.Update();
   mModifierKeyState.InitInputEvent(geckoEvent);
   geckoEvent.time = ::GetMessageTime();
-
-  UI::Input::EdgeGestureKind value;
-  aArgs->get_Kind(&value);
-
-  if (value == UI::Input::EdgeGestureKind::EdgeGestureKind_Keyboard) {
-    geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
-  } else {
-    geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
-  }
+  geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
 
   // Safe
   DispatchEventIgnoreStatus(&geckoEvent);
   return S_OK;
 }
 
 /**
  * This helper function is used by our processing of PointerPressed,
@@ -386,16 +422,17 @@ MetroInput::OnPointerNonTouch(UI::Input:
       event->button = WidgetMouseEvent::buttonType::eMiddleButton;
       event->message = NS_MOUSE_BUTTON_UP;
       break;
     case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonReleased:
       event->button = WidgetMouseEvent::buttonType::eRightButton;
       event->message = NS_MOUSE_BUTTON_UP;
       break;
   }
+  UpdateInputLevel(LEVEL_PRECISE);
   InitGeckoMouseEventFromPointerPoint(event, aPoint);
   DispatchAsyncEventIgnoreStatus(event);
 }
 
 void
 MetroInput::InitTouchEventTouchList(WidgetTouchEvent* aEvent)
 {
   MOZ_ASSERT(aEvent);
@@ -425,16 +462,18 @@ MetroInput::OnPointerPressed(UI::Core::I
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     OnPointerNonTouch(currentPoint.Get());
     mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
     return S_OK;
   }
 
   // This is touch input.
+  UpdateInputLevel(LEVEL_IMPRECISE);
+
   // Create the new touch point and add it to our event.
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
   nsRefPtr<Touch> touch = CreateDOMTouch(currentPoint.Get());
   touch->mChanged = true;
   mTouches.Put(pointerId, touch);
 
   WidgetTouchEvent* touchEvent =
@@ -512,16 +551,18 @@ MetroInput::OnPointerMoved(UI::Core::ICo
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     OnPointerNonTouch(currentPoint.Get());
     AddPointerMoveDataToRecognizer(aArgs);
     return S_OK;
   }
 
   // This is touch input.
+  UpdateInputLevel(LEVEL_IMPRECISE);
+
   // Get the touch associated with this touch point.
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
   nsRefPtr<Touch> touch = mTouches.Get(pointerId);
 
   // Some old drivers cause us to receive a PointerMoved event for a touchId
   // after we've already received a PointerReleased event for that touchId.
   // To work around those busted drivers, we simply ignore TouchMoved events
@@ -609,16 +650,18 @@ MetroInput::OnPointerReleased(UI::Core::
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     OnPointerNonTouch(currentPoint.Get());
     mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
     return S_OK;
   }
 
   // This is touch input.
+  UpdateInputLevel(LEVEL_IMPRECISE);
+
   // Get the touch associated with this touch point.
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
   nsRefPtr<Touch> touch = mTouches.Get(pointerId);
 
   // Purge any pending moves for this pointer
   if (touch->mChanged) {
     WidgetTouchEvent* touchEvent =
@@ -743,19 +786,21 @@ MetroInput::OnPointerEntered(UI::Core::I
   device->get_PointerDeviceType(&deviceType);
 
   // We only dispatch mouseenter and mouseexit events for mouse and pen input.
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     WidgetMouseEvent* event =
       new WidgetMouseEvent(true, NS_MOUSE_ENTER, mWidget.Get(),
                            WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
+    UpdateInputLevel(LEVEL_PRECISE);
     InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get());
     DispatchAsyncEventIgnoreStatus(event);
   }
+  UpdateInputLevel(LEVEL_IMPRECISE);
   return S_OK;
 }
 
 // This event is raised when a precise pointer leaves the bounding box of
 // our window.  For touch input, this will be raised before the
 // PointerReleased event.
 HRESULT
 MetroInput::OnPointerExited(UI::Core::ICoreWindow* aSender,
@@ -774,19 +819,21 @@ MetroInput::OnPointerExited(UI::Core::IC
   device->get_PointerDeviceType(&deviceType);
 
   // We only dispatch mouseenter and mouseexit events for mouse and pen input.
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     WidgetMouseEvent* event =
       new WidgetMouseEvent(true, NS_MOUSE_EXIT, mWidget.Get(),
                            WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
+    UpdateInputLevel(LEVEL_PRECISE);
     InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get());
     DispatchAsyncEventIgnoreStatus(event);
   }
+  UpdateInputLevel(LEVEL_IMPRECISE);
   return S_OK;
 }
 
 /**
  * This helper function is called by our processing of "manipulation events".
  * Manipulation events are how Windows sends us information about swipes,
  * magnification gestures, and rotation gestures.
  *
--- a/widget/windows/winrt/MetroInput.h
+++ b/widget/windows/winrt/MetroInput.h
@@ -150,16 +150,24 @@ public:
 
 private:
   Microsoft::WRL::ComPtr<ICoreWindow> mWindow;
   Microsoft::WRL::ComPtr<MetroWidget> mWidget;
   Microsoft::WRL::ComPtr<IGestureRecognizer> mGestureRecognizer;
 
   ModifierKeyState mModifierKeyState;
 
+  // Tracking input level
+  enum InputPrecisionLevel {
+    LEVEL_PRECISE,
+    LEVEL_IMPRECISE
+  };
+  InputPrecisionLevel mCurrentInputLevel;
+  void UpdateInputLevel(InputPrecisionLevel aInputLevel);
+
   // Initialization/Uninitialization helpers
   void RegisterInputEvents();
   void UnregisterInputEvents();
 
   // Hit testing for chrome content
   bool mChromeHitTestCacheForTouch;
   bool HitTestChrome(const LayoutDeviceIntPoint& pt);
 
@@ -169,16 +177,17 @@ private:
   void OnPointerNonTouch(IPointerPoint* aPoint);
   void AddPointerMoveDataToRecognizer(IPointerEventArgs* aArgs);
   void InitGeckoMouseEventFromPointerPoint(WidgetMouseEvent* aEvent,
                                            IPointerPoint* aPoint);
   void ProcessManipulationDelta(ManipulationDelta const& aDelta,
                                 Point const& aPosition,
                                 uint32_t aMagEventType,
                                 uint32_t aRotEventType);
+  uint16_t ProcessInputTypeForGesture(IEdgeGestureEventArgs* aArgs);
 
   // The W3C spec states that "whether preventDefault has been called" should
   // be tracked on a per-touchpoint basis, but it also states that touchstart
   // and touchmove events can contain multiple changed points.  At the time of
   // this writing, W3C touch events are in the process of being abandoned in
   // favor of W3C pointer events, so it is unlikely that this ambiguity will
   // be resolved.  Additionally, nsPresShell tracks "whether preventDefault
   // has been called" on a per-touch-session basis.  We will follow a similar