Merge fx-team to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 16 Apr 2014 22:29:41 -0400
changeset 179345 e71ed4135461b57a9c7f35995e4e1f1dc5f0e8bf
parent 179318 84536adf61090b2cfcff13a4c72f3ca52a806029 (current diff)
parent 179344 b3ea0d746ee49ac5aa7d6839db33951a101c128e (diff)
child 179346 f011f98832e26151590465d8211cdb759acaa05d
child 179388 6196c121c4f4f7241931e7ce1608c8c97c7433d6
child 179400 ccc9cd25ebda0454adaf5b870ba446ebbd51597d
child 179417 f1f58acc81549b859c6846aacfca32c6527253a2
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone31.0a1
Merge fx-team to m-c.
browser/base/content/aboutaccounts/fonts/clearsans-regular.woff
browser/base/content/aboutaccounts/fonts/firasans-light.woff
browser/base/content/aboutaccounts/fonts/firasans-regular.woff
browser/components/customizableui/test/browser_996364_defaultCollapsed.js
browser/themes/linux/devtools/ruleview.css
browser/themes/osx/devtools/ruleview.css
browser/themes/shared/ClearSans-Regular.ttf
browser/themes/windows/devtools/ruleview.css
--- a/b2g/chrome/content/devtools.js
+++ b/b2g/chrome/content/devtools.js
@@ -2,34 +2,34 @@
  * 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/. */
 
 'use strict';
 
 const DEVELOPER_HUD_LOG_PREFIX = 'DeveloperHUD';
 
 XPCOMUtils.defineLazyGetter(this, 'devtools', function() {
-  const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+  const {devtools} = Cu.import('resource://gre/modules/devtools/Loader.jsm', {});
   return devtools;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'DebuggerClient', function() {
   return Cu.import('resource://gre/modules/devtools/dbg-client.jsm', {}).DebuggerClient;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'WebConsoleUtils', function() {
-  return devtools.require("devtools/toolkit/webconsole/utils").Utils;
+  return devtools.require('devtools/toolkit/webconsole/utils').Utils;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'EventLoopLagFront', function() {
-  return devtools.require("devtools/server/actors/eventlooplag").EventLoopLagFront;
+  return devtools.require('devtools/server/actors/eventlooplag').EventLoopLagFront;
 });
 
 XPCOMUtils.defineLazyGetter(this, 'MemoryFront', function() {
-  return devtools.require("devtools/server/actors/memory").MemoryFront;
+  return devtools.require('devtools/server/actors/memory').MemoryFront;
 });
 
 
 /**
  * The Developer HUD is an on-device developer tool that displays widgets,
  * showing visual debug information about apps. Each widget corresponds to a
  * metric as tracked by a metric watcher (e.g. consoleWatcher).
  */
@@ -42,17 +42,17 @@ let developerHUD = {
   _watchers: [],
   _logging: true,
 
   /**
    * This method registers a metric watcher that will watch one or more metrics
    * on app frames that are being tracked. A watcher must implement the
    * `trackTarget(target)` and `untrackTarget(target)` methods, register
    * observed metrics with `target.register(metric)`, and keep them up-to-date
-   * with `target.update(metric, value, message)` when necessary.
+   * with `target.update(metric, message)` when necessary.
    */
   registerWatcher: function dwp_registerWatcher(watcher) {
     this._watchers.unshift(watcher);
   },
 
   init: function dwp_init() {
     if (this._client)
       return;
@@ -205,25 +205,34 @@ Target.prototype = {
   register: function target_register(metric) {
     this.metrics.set(metric, 0);
   },
 
   /**
    * Modify one of a target's metrics, and send out an event to notify relevant
    * parties (e.g. the developer HUD, automated tests, etc).
    */
-  update: function target_update(metric, value = 0, message) {
+  update: function target_update(metric, message) {
+    if (!metric.name) {
+      throw new Error('Missing metric.name');
+    }
+
+    if (!metric.value) {
+      metric.value = 0;
+    }
+
     let metrics = this.metrics;
-    metrics.set(metric, value);
+    if (metrics) {
+      metrics.set(metric.name, metric.value);
+    }
 
     let data = {
       metrics: [], // FIXME(Bug 982066) Remove this field.
       manifest: this.frame.appManifestURL,
       metric: metric,
-      value: value,
       message: message
     };
 
     // FIXME(Bug 982066) Remove this loop.
     if (metrics && metrics.size > 0) {
       for (let name of metrics.keys()) {
         data.metrics.push({name: name, value: metrics.get(name)});
       }
@@ -235,25 +244,27 @@ Target.prototype = {
     this._send(data);
   },
 
   /**
    * Nicer way to call update() when the metric value is a number that needs
    * to be incremented.
    */
   bump: function target_bump(metric, message) {
-    this.update(metric, this.metrics.get(metric) + 1, message);
+    metric.value = (this.metrics.get(metric.name) || 0) + 1;
+    this.update(metric, message);
   },
 
   /**
    * Void a metric value and make sure it isn't displayed on the front-end
    * anymore.
    */
   clear: function target_clear(metric) {
-    this.update(metric, 0);
+    metric.value = 0;
+    this.update(metric);
   },
 
   /**
    * Tear everything down, including the front-end by sending a message without
    * widgets.
    */
   destroy: function target_destroy() {
     delete this.metrics;
@@ -302,17 +313,17 @@ let consoleWatcher = {
       SettingsListener.observe('hud.' + metric, watching[metric], watch => {
         // Watch or unwatch the metric.
         if (watching[metric] = watch) {
           return;
         }
 
         // If unwatched, remove any existing widgets for that metric.
         for (let target of this._targets.values()) {
-          target.clear(metric);
+          target.clear({name: metric});
         }
       });
     }
 
     client.addListener('logMessage', this.consoleListener);
     client.addListener('pageError', this.consoleListener);
     client.addListener('consoleAPICall', this.consoleListener);
     client.addListener('reflowActivity', this.consoleListener);
@@ -340,72 +351,76 @@ let consoleWatcher = {
       listeners: ['LogMessage', 'PageError', 'ConsoleAPI', 'ReflowActivity']
     }, (res) => { });
 
     this._targets.delete(target.actor.consoleActor);
   },
 
   consoleListener: function cw_consoleListener(type, packet) {
     let target = this._targets.get(packet.from);
-    let metric;
+    let metric = {};
     let output = '';
 
     switch (packet.type) {
 
       case 'pageError':
         let pageError = packet.pageError;
 
         if (pageError.warning || pageError.strict) {
-          metric = 'warnings';
+          metric.name = 'warnings';
           output += 'warning (';
         } else {
-          metric = 'errors';
+          metric.name = 'errors';
           output += 'error (';
         }
 
         if (this._security.indexOf(pageError.category) > -1) {
-          metric = 'security';
+          metric.name = 'security';
         }
 
         let {errorMessage, sourceName, category, lineNumber, columnNumber} = pageError;
         output += category + '): "' + (errorMessage.initial || errorMessage) +
           '" in ' + sourceName + ':' + lineNumber + ':' + columnNumber;
         break;
 
       case 'consoleAPICall':
         switch (packet.message.level) {
 
           case 'error':
-            metric = 'errors';
+            metric.name = 'errors';
             output += 'error (console)';
             break;
 
           case 'warn':
-            metric = 'warnings';
+            metric.name = 'warnings';
             output += 'warning (console)';
             break;
 
           default:
             return;
         }
         break;
 
       case 'reflowActivity':
-        metric = 'reflows';
+        metric.name = 'reflows';
 
-        let {start, end, sourceURL} = packet;
+        let {start, end, sourceURL, interruptible} = packet;
+        metric.interruptible = interruptible;
         let duration = Math.round((end - start) * 100) / 100;
         output += 'reflow: ' + duration + 'ms';
         if (sourceURL) {
           output += ' ' + this.formatSourceURL(packet);
         }
         break;
+
+      default:
+        return;
     }
 
-    if (!this._watching[metric]) {
+    if (!this._watching[metric.name]) {
       return;
     }
 
     target.bump(metric, output);
   },
 
   formatSourceURL: function cw_formatSourceURL(packet) {
     // Abbreviate source URL
@@ -441,29 +456,29 @@ let eventLoopLagWatcher = {
 
     // Toggle the state of existing fronts.
     let fronts = this._fronts;
     for (let target of fronts.keys()) {
       if (value) {
         fronts.get(target).start();
       } else {
         fronts.get(target).stop();
-        target.clear('jank');
+        target.clear({name: 'jank'});
       }
     }
   },
 
   trackTarget: function(target) {
     target.register('jank');
 
     let front = new EventLoopLagFront(this._client, target.actor);
     this._fronts.set(target, front);
 
     front.on('event-loop-lag', time => {
-      target.update('jank', time, 'jank: ' + time + 'ms');
+      target.update({name: 'jank', value: time}, 'jank: ' + time + 'ms');
     });
 
     if (this._active) {
       front.start();
     }
   },
 
   untrackTarget: function(target) {
@@ -509,17 +524,17 @@ let memoryWatcher = {
     SettingsListener.observe('hud.appmemory', false, enabled => {
       if (this._active = enabled) {
         for (let target of this._fronts.keys()) {
           this.measure(target);
         }
       } else {
         for (let target of this._fronts.keys()) {
           clearTimeout(this._timers.get(target));
-          target.clear('memory');
+          target.clear({name: 'memory'});
         }
       }
     });
   },
 
   measure: function mw_measure(target) {
 
     // TODO Also track USS (bug #976024).
@@ -545,17 +560,17 @@ let memoryWatcher = {
       if (watch.style) {
         total += parseInt(data.styleSize);
       }
       if (watch.other) {
         total += parseInt(data.otherSize);
       }
       // TODO Also count images size (bug #976007).
 
-      target.update('memory', total);
+      target.update({name: 'memory', value: total});
       let duration = parseInt(data.jsMilliseconds) + parseInt(data.nonJSMilliseconds);
       let timer = setTimeout(() => this.measure(target), 100 * duration);
       this._timers.set(target, timer);
     }, (err) => {
       console.error(err);
     });
   },
 
--- a/browser/base/content/aboutaccounts/fonts.css
+++ b/browser/base/content/aboutaccounts/fonts.css
@@ -1,25 +1,24 @@
 @font-face {
   font-family: 'Fira Sans';
   font-style: normal;
   font-weight: 400;
   src: local('Fira Sans'),
        local('FiraSans'),
-       url('fonts/firasans-regular.woff') format('woff');
+       url('../fonts/FiraSans-Regular.woff') format('woff');
 }
 @font-face {
   font-family: 'Fira Sans';
   font-style: normal;
   font-weight: 300;
   src: local('Fira Sans Light'),
        local('FiraSansLight'),
-       url('fonts/firasans-light.woff') format('woff');
+       url('../fonts/FiraSans-Light.woff') format('woff');
 }
 @font-face {
   font-family: 'Clear Sans';
   font-style: normal;
   font-weight: 400;
   src: local('Clear Sans'),
        local('ClearSans'),
-       url('fonts/clearsans-regular.woff') format('woff');
+       url('../fonts/ClearSans-Regular.woff') format('woff');
 }
-
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -58,16 +58,20 @@ var StarUI = {
   // nsIDOMEventListener
   handleEvent: function SU_handleEvent(aEvent) {
     switch (aEvent.type) {
       case "popuphidden":
         if (aEvent.originalTarget == this.panel) {
           if (!this._element("editBookmarkPanelContent").hidden)
             this.quitEditMode();
 
+          if (this._anchorToolbarButton) {
+            this._anchorToolbarButton.removeAttribute("open");
+            this._anchorToolbarButton = null;
+          }
           this._restoreCommandsState();
           this._itemId = -1;
           if (this._batching) {
             PlacesUtils.transactionManager.endBatch(false);
             this._batching = false;
           }
 
           switch (this._actionOnHide) {
@@ -181,16 +185,31 @@ var StarUI = {
     this._element("editBookmarkPanelRemoveButton").label = label;
 
     // unset the unstarred state, if set
     this._element("editBookmarkPanelStarIcon").removeAttribute("unstarred");
 
     this._itemId = aItemId !== undefined ? aItemId : this._itemId;
     this.beginBatch();
 
+    if (aAnchorElement) {
+      // Set the open=true attribute if the anchor is a
+      // descendent of a toolbarbutton.
+      let parent = aAnchorElement.parentNode;
+      while (parent) {
+        if (parent.localName == "toolbarbutton") {
+          break;
+        }
+        parent = parent.parentNode;
+      }
+      if (parent) {
+        this._anchorToolbarButton = parent;
+        parent.setAttribute("open", "true");
+      }
+    }
     this.panel.openPopup(aAnchorElement, aPosition);
 
     gEditItemOverlay.initPanel(this._itemId,
                                { hiddenRows: ["description", "location",
                                               "loadInSidebar", "keyword"] });
   },
 
   panelShown:
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -232,20 +232,16 @@ let gSyncUI = {
     ));
 
     let notification = new Weave.Notification(
       title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
     Weave.Notifications.replaceTitle(notification);
   },
 
   _getAppName: function () {
-    try {
-      let syncStrings = new StringBundle("chrome://browser/locale/sync.properties");
-      return syncStrings.getFormattedString("sync.defaultAccountApplication", [brandName]);
-    } catch (ex) {}
     let brand = new StringBundle("chrome://branding/locale/brand.properties");
     return brand.get("brandShortName");
   },
 
   onEOLNotice: function (data) {
     let code = data.code;
     let kind = (code == "hard-eol") ? "error" : "warning";
     let url = data.url || gSyncUI.DEFAULT_EOL_URL;
rename from browser/base/content/aboutaccounts/fonts/clearsans-regular.woff
rename to browser/base/content/fonts/ClearSans-Regular.woff
rename from browser/base/content/aboutaccounts/fonts/firasans-light.woff
rename to browser/base/content/fonts/FiraSans-Light.woff
rename from browser/base/content/aboutaccounts/fonts/firasans-regular.woff
rename to browser/base/content/fonts/FiraSans-Regular.woff
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -51,19 +51,16 @@ browser.jar:
         content/browser/abouthealthreport/abouthealth.css     (content/abouthealthreport/abouthealth.css)
 #endif
         content/browser/aboutaccounts/aboutaccounts.xhtml                     (content/aboutaccounts/aboutaccounts.xhtml)
         content/browser/aboutaccounts/aboutaccounts.js                        (content/aboutaccounts/aboutaccounts.js)
         content/browser/aboutaccounts/aboutaccounts.css                       (content/aboutaccounts/aboutaccounts.css)
         content/browser/aboutaccounts/main.css                                (content/aboutaccounts/main.css)
         content/browser/aboutaccounts/normalize.css                           (content/aboutaccounts/normalize.css)
         content/browser/aboutaccounts/fonts.css                               (content/aboutaccounts/fonts.css)
-        content/browser/aboutaccounts/fonts/clearsans-regular.woff            (content/aboutaccounts/fonts/clearsans-regular.woff)
-        content/browser/aboutaccounts/fonts/firasans-light.woff               (content/aboutaccounts/fonts/firasans-light.woff)
-        content/browser/aboutaccounts/fonts/firasans-regular.woff             (content/aboutaccounts/fonts/firasans-regular.woff)
         content/browser/aboutaccounts/images/fox.png                          (content/aboutaccounts/images/fox.png)
         content/browser/aboutaccounts/images/graphic_sync_intro.png           (content/aboutaccounts/images/graphic_sync_intro.png)
         content/browser/aboutaccounts/images/graphic_sync_intro@2x.png        (content/aboutaccounts/images/graphic_sync_intro@2x.png)
 
         content/browser/certerror/aboutCertError.xhtml     (content/aboutcerterror/aboutCertError.xhtml)
         content/browser/certerror/aboutCertError.css       (content/aboutcerterror/aboutCertError.css)
 
         content/browser/aboutRobots-icon.png          (content/aboutRobots-icon.png)
@@ -72,16 +69,19 @@ browser.jar:
         content/browser/aboutTabCrashed.js            (content/aboutTabCrashed.js)
         content/browser/aboutTabCrashed.xhtml         (content/aboutTabCrashed.xhtml)
 *       content/browser/browser.css                   (content/browser.css)
 *       content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
 *       content/browser/chatWindow.xul                (content/chatWindow.xul)
         content/browser/content.js                    (content/content.js)
+        content/browser/fonts/ClearSans-Regular.woff  (content/fonts/ClearSans-Regular.woff)
+        content/browser/fonts/FiraSans-Regular.woff   (content/fonts/FiraSans-Regular.woff)
+        content/browser/fonts/FiraSans-Light.woff     (content/fonts/FiraSans-Light.woff)
         content/browser/newtab/newTab.xul             (content/newtab/newTab.xul)
 *       content/browser/newtab/newTab.js              (content/newtab/newTab.js)
         content/browser/newtab/newTab.css             (content/newtab/newTab.css)
         content/browser/newtab/preloaderContent.js    (content/newtab/preloaderContent.js)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/pageInfo.xml         (content/pageinfo/pageInfo.xml)
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -292,21 +292,22 @@ let CustomizableUIInternal = {
 
   registerArea: function(aName, aProperties, aInternalCaller) {
     if (typeof aName != "string" || !/^[a-z0-9-_]{1,}$/i.test(aName)) {
       throw new Error("Invalid area name");
     }
 
     let areaIsKnown = gAreas.has(aName);
     let props = areaIsKnown ? gAreas.get(aName) : new Map();
-    if (areaIsKnown && aProperties["type"] &&
-        props.get("type") != aProperties["type"]) {
-      throw new Error("An area cannot change types");
-    }
+    const kImmutableProperties = new Set(["type", "legacy", "overflowable"]);
     for (let key in aProperties) {
+      if (areaIsKnown && kImmutableProperties.has(key) &&
+          props.get(key) != aProperties[key]) {
+        throw new Error("An area cannot change the property for '" + key + "'");
+      }
       //XXXgijs for special items, we need to make sure they have an appropriate ID
       // so we aren't perpetually in a non-default state:
       if (key == "defaultPlacements" && Array.isArray(aProperties[key])) {
         props.set(key, aProperties[key].map(x => this.isSpecialWidget(x) ? this.ensureSpecialWidgetId(x) : x ));
       } else {
         props.set(key, aProperties[key]);
       }
     }
@@ -3453,18 +3454,18 @@ function WidgetSingleWrapper(aWidget, aN
   this.node = aNode;
   this.provider = CustomizableUI.PROVIDER_API;
 
   const kGlobalProps = ["id", "type"];
   for (let prop of kGlobalProps) {
     this[prop] = aWidget[prop];
   }
 
-  const nodeProps = ["label", "tooltiptext"];
-  for (let prop of nodeProps) {
+  const kNodeProps = ["label", "tooltiptext"];
+  for (let prop of kNodeProps) {
     let propertyName = prop;
     // Look at the node for these, instead of the widget data, to ensure the
     // wrapper always reflects this live instance.
     this.__defineGetter__(propertyName,
                           function() aNode.getAttribute(propertyName));
   }
 
   this.__defineGetter__("disabled", function() aNode.disabled);
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -100,11 +100,11 @@ skip-if = os == "linux"
 [browser_985815_propagate_setToolbarVisibility.js]
 [browser_981305_separator_insertion.js]
 [browser_987177_destroyWidget_xul.js]
 [browser_987177_xul_wrapper_updating.js]
 [browser_987492_window_api.js]
 [browser_992747_toggle_noncustomizable_toolbar.js]
 [browser_993322_widget_notoolbar.js]
 [browser_995164_registerArea_during_customize_mode.js]
-[browser_996364_defaultCollapsed.js]
+[browser_996364_registerArea_different_properties.js]
 [browser_bootstrapped_custom_toolbar.js]
 [browser_panel_toggle.js]
--- a/browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
+++ b/browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
@@ -53,43 +53,62 @@ add_task(function*() {
   ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
   is(CustomizableUI.getPlacementOfWidget("sync-button").area, TOOLBARID, "Button's back on toolbar");
   ok(toolbar.querySelector("#sync-button"), "Sync button really is on toolbar.");
 
   let otherWin = yield openAndLoadWindow({}, true);
   let otherTB = otherWin.document.createElementNS(kNSXUL, "toolbar");
   otherTB.id = TOOLBARID;
   otherTB.setAttribute("customizable", "true");
+  let wasInformedCorrectlyOfAreaAppearing = false;
+  let listener = {
+    onAreaNodeRegistered: function(aArea, aNode) {
+      if (aNode == otherTB) {
+        wasInformedCorrectlyOfAreaAppearing = true;
+      }
+    }
+  };
+  CustomizableUI.addListener(listener);
   otherWin.gNavToolbox.appendChild(otherTB);
+  ok(wasInformedCorrectlyOfAreaAppearing, "Should have been told area was registered.");
+  CustomizableUI.removeListener(listener);
+
   ok(otherTB.querySelector("#sync-button"), "Sync button is on other toolbar, too.");
 
   simulateItemDrag(syncButton, gNavToolbox.palette);
   ok(!CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved to the palette");
   ok(gNavToolbox.palette.querySelector("#sync-button"), "Sync button really is in palette.");
   ok(!otherTB.querySelector("#sync-button"), "Sync button is in palette in other window, too.");
 
   simulateItemDrag(syncButton, toolbar);
   ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
   is(CustomizableUI.getPlacementOfWidget("sync-button").area, TOOLBARID, "Button's back on toolbar");
   ok(toolbar.querySelector("#sync-button"), "Sync button really is on toolbar.");
   ok(otherTB.querySelector("#sync-button"), "Sync button is on other toolbar, too.");
 
   let wasInformedCorrectlyOfAreaDisappearing = false;
-  let listener = {
+  let windowClosed = null;
+  listener = {
     onAreaNodeUnregistered: function(aArea, aNode, aReason) {
       if (aArea == TOOLBARID) {
         is(aNode, otherTB, "Should be informed about other toolbar");
         is(aReason, CustomizableUI.REASON_WINDOW_CLOSED, "Reason should be correct.");
         wasInformedCorrectlyOfAreaDisappearing = (aReason === CustomizableUI.REASON_WINDOW_CLOSED);
       }
-    }
+    },
+    onWindowClosed: function(aWindow) {
+      if (aWindow == otherWin) {
+        windowClosed = aWindow;
+      }
+    },
   };
   CustomizableUI.addListener(listener);
   yield promiseWindowClosed(otherWin);
 
+  is(windowClosed, otherWin, "Window should have sent onWindowClosed notification.");
   ok(wasInformedCorrectlyOfAreaDisappearing, "Should be told about window closing.");
   CustomizableUI.removeListener(listener);
   // Closing the other window should not be counted against this window's customize mode:
   is(syncButton.parentNode.localName, "toolbarpaletteitem", "Sync button's parent node should still be a wrapper.");
   isnot(gCustomizeMode.areas.indexOf(toolbar), -1, "Toolbar should still be a customizable area for this customize mode instance.");
 
   yield gCustomizeMode.reset();
 
rename from browser/components/customizableui/test/browser_996364_defaultCollapsed.js
rename to browser/components/customizableui/test/browser_996364_registerArea_different_properties.js
--- a/browser/components/customizableui/test/browser_996364_defaultCollapsed.js
+++ b/browser/components/customizableui/test/browser_996364_registerArea_different_properties.js
@@ -15,42 +15,98 @@ add_task(function() {
   }
 
   CustomizableUI.unregisterArea("area-996364", true);
 });
 
 add_task(function() {
   let exceptionThrown = false;
   try {
-    CustomizableUI.registerArea("area-996364-2", {"type": CustomizableUI.TYPE_TOOLBAR, "defaultCollapsed": "false"});
+    CustomizableUI.registerArea("area-996364-2", {type: CustomizableUI.TYPE_TOOLBAR, defaultCollapsed: "false"});
   } catch (ex) {
     exceptionThrown = true;
   }
   ok(exceptionThrown, "defaultCollapsed is not allowed as an external property");
 
   // No need to unregister the area because registration fails.
 });
 
 add_task(function() {
   let exceptionThrown;
   try {
-    CustomizableUI.registerArea("area-996364-3", {"type": CustomizableUI.TYPE_TOOLBAR});
-    CustomizableUI.registerArea("area-996364-3", {"type": CustomizableUI.TYPE_MENU_PANEL});
+    CustomizableUI.registerArea("area-996364-3", {type: CustomizableUI.TYPE_TOOLBAR});
+    CustomizableUI.registerArea("area-996364-3", {type: CustomizableUI.TYPE_MENU_PANEL});
   } catch (ex) {
     exceptionThrown = ex;
   }
   ok(exceptionThrown, "Exception expected, an area cannot change types: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
 
   CustomizableUI.unregisterArea("area-996364-3", true);
 });
 
 add_task(function() {
   let exceptionThrown;
   try {
-    CustomizableUI.registerArea("area-996364-4", {"type": CustomizableUI.TYPE_MENU_PANEL});
-    CustomizableUI.registerArea("area-996364-4", {"type": CustomizableUI.TYPE_TOOLBAR});
+    CustomizableUI.registerArea("area-996364-4", {type: CustomizableUI.TYPE_MENU_PANEL});
+    CustomizableUI.registerArea("area-996364-4", {type: CustomizableUI.TYPE_TOOLBAR});
   } catch (ex) {
     exceptionThrown = ex;
   }
   ok(exceptionThrown, "Exception expected, an area cannot change types: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
 
   CustomizableUI.unregisterArea("area-996364-4", true);
 });
+
+add_task(function() {
+  let exceptionThrown;
+  try {
+    CustomizableUI.registerArea("area-996899-1", { anchor: "PanelUI-menu-button",
+                                                   type: CustomizableUI.TYPE_MENU_PANEL,
+                                                   defaultPlacements: [] });
+    CustomizableUI.registerArea("area-996899-1", { anchor: "home-button",
+                                                   type: CustomizableUI.TYPE_MENU_PANEL,
+                                                   defaultPlacements: [] });
+  } catch (ex) {
+    exceptionThrown = ex;
+  }
+  ok(!exceptionThrown, "Changing anchors shouldn't throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+  CustomizableUI.unregisterArea("area-996899-1", true);
+});
+
+add_task(function() {
+  let exceptionThrown;
+  try {
+    CustomizableUI.registerArea("area-996899-2", { anchor: "PanelUI-menu-button",
+                                                   type: CustomizableUI.TYPE_MENU_PANEL,
+                                                   defaultPlacements: [] });
+    CustomizableUI.registerArea("area-996899-2", { anchor: "PanelUI-menu-button",
+                                                   type: CustomizableUI.TYPE_MENU_PANEL,
+                                                   defaultPlacements: ["feed-button"] });
+  } catch (ex) {
+    exceptionThrown = ex;
+  }
+  ok(!exceptionThrown, "Changing defaultPlacements shouldn't throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+  CustomizableUI.unregisterArea("area-996899-2", true);
+});
+
+add_task(function() {
+  let exceptionThrown;
+  try {
+    CustomizableUI.registerArea("area-996899-3", { legacy: true });
+    CustomizableUI.registerArea("area-996899-3", { legacy: false });
+  } catch (ex) {
+    exceptionThrown = ex;
+  }
+  ok(exceptionThrown, "Changing 'legacy' should throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+  CustomizableUI.unregisterArea("area-996899-3", true);
+});
+
+add_task(function() {
+  let exceptionThrown;
+  try {
+    CustomizableUI.registerArea("area-996899-4", { overflowable: true });
+    CustomizableUI.registerArea("area-996899-4", { overflowable: false });
+  } catch (ex) {
+    exceptionThrown = ex;
+  }
+  ok(exceptionThrown, "Changing 'overflowable' should throw an exception: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+  CustomizableUI.unregisterArea("area-996899-4", true);
+});
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -673,18 +673,16 @@ let SessionStoreInternal = {
             this.restoreTabContent(tab);
           }
         }
         break;
       default:
         debug("received unknown message '" + aMessage.name + "'");
         break;
     }
-
-    this._clearRestoringWindows();
   },
 
   /**
    * Record telemetry measurements stored in an object.
    * @param telemetry
    *        {histogramID: value, ...} An object mapping histogramIDs to the
    *        value to be recorded for that ID,
    */
@@ -3395,16 +3393,25 @@ let SessionStoreInternal = {
            !!this._closedWindows[normalWindowIndex].isPopup)
       normalWindowIndex++;
     if (normalWindowIndex >= this._max_windows_undo)
       spliceTo = normalWindowIndex + 1;
 #endif
     this._closedWindows.splice(spliceTo, this._closedWindows.length);
   },
 
+  /**
+   * Clears the set of windows that are "resurrected" before writing to disk to
+   * make closing windows one after the other until shutdown work as expected.
+   *
+   * This function should only be called when we are sure that there has been
+   * a user action that indicates the browser is actively being used and all
+   * windows that have been closed before are not part of a series of closing
+   * windows.
+   */
   _clearRestoringWindows: function ssi_clearRestoringWindows() {
     for (let i = 0; i < this._closedWindows.length; i++) {
       delete this._closedWindows[i]._shouldRestore;
     }
   },
 
   /**
    * Reset state to prepare for a new session state to be restored.
--- a/browser/components/sessionstore/test/browser_819510_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_819510_perwindowpb.js
@@ -110,35 +110,37 @@ function test_3() {
             waitForTabLoad(aWindow, "http://www.example.com/", function() {
 
               let curState = JSON.parse(ss.getBrowserState());
               is(curState.windows.length, 4, "Browser has opened 4 windows");
               is(curState.windows[2].isPrivate, true, "Window 2 is private");
               is(curState.selectedWindow, 4, "Last window opened is the one selected");
 
               waitForWindowClose(normalWindow, function() {
-                // Load another tab before checking the written state so that
+                // Pin and unpin a tab before checking the written state so that
                 // the list of restoring windows gets cleared. Otherwise the
                 // window we just closed would be marked as not closed.
-                waitForTabLoad(aWindow, "http://www.example.com/", function() {
-                  forceWriteState(function(state) {
-                    is(state.windows.length, 2,
-                       "sessionstore state: 2 windows in data being written to disk");
-                    is(state.selectedWindow, 2,
-                       "Selected window is updated to match one of the saved windows");
-                    state.windows.forEach(function(win) {
-                      is(!win.isPrivate, true, "Saved window is not private");
-                    });
-                    is(state._closedWindows.length, 1,
-                       "sessionstore state: 1 closed window in data being written to disk");
-                    state._closedWindows.forEach(function(win) {
-                      is(!win.isPrivate, true, "Closed window is not private");
-                    });
-                    runNextTest();
+                let tab = aWindow.gBrowser.tabs[0];
+                aWindow.gBrowser.pinTab(tab);
+                aWindow.gBrowser.unpinTab(tab);
+
+                forceWriteState(function(state) {
+                  is(state.windows.length, 2,
+                     "sessionstore state: 2 windows in data being written to disk");
+                  is(state.selectedWindow, 2,
+                     "Selected window is updated to match one of the saved windows");
+                  state.windows.forEach(function(win) {
+                    is(!win.isPrivate, true, "Saved window is not private");
                   });
+                  is(state._closedWindows.length, 1,
+                     "sessionstore state: 1 closed window in data being written to disk");
+                  state._closedWindows.forEach(function(win) {
+                    is(!win.isPrivate, true, "Closed window is not private");
+                  });
+                  runNextTest();
                 });
               });
             });
           });
         });
       });
     });
   });
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/chrome.manifest
@@ -0,0 +1,1 @@
+content browser_dbg_addon4 .
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/test.xul
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="text/javascript" src="testxul.js"/>
+  <label value="test.xul"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/test2.xul
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="text/javascript" src="testxul2.js"/>
+  <label value="test2.xul"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/testxul.js
@@ -0,0 +1,4 @@
+// Define something in here or the script may get collected
+window.addEventListener("unload", function() {
+  window.foo = "bar";
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon4/testxul2.js
@@ -0,0 +1,4 @@
+// Define something in here or the script may get collected
+window.addEventListener("unload", function() {
+  window.foo = "bar";
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/chrome.manifest
@@ -0,0 +1,1 @@
+content browser_dbg_addon5 .
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/test.xul
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="text/javascript" src="testxul.js"/>
+  <label value="test.xul"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/test2.xul
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="text/javascript" src="testxul2.js"/>
+  <label value="test2.xul"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/testxul.js
@@ -0,0 +1,4 @@
+// Define something in here or the script may get collected
+window.addEventListener("unload", function() {
+  window.foo = "bar";
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/addon-source/browser_dbg_addon5/testxul2.js
@@ -0,0 +1,4 @@
+// Define something in here or the script may get collected
+window.addEventListener("unload", function() {
+  window.foo = "bar";
+});
index a654b7e011b029859b2be10071d13ecb4e9e78eb..31c5331b69dee6d15183647bb598f90195ff1c2a
GIT binary patch
literal 2918
zc$^FHW@Zs#U}E54@a?a132>Y;EtQdhA)J|kfrmkcAt^t<q`0Igu|O}YI5dQlfq7H8
zaWKPto8Zz4ZU#n{Z$PC$-3$y@Cpr2n8}hWi|E#^naw~^w_Yuw`iJ^QYTiY*n8G8vE
z^PW1Qe4A19NWHr3mDHn$xcZ)?J$?GKy6g?(pEJ=POmZyCq+2xv3|!ueFl#jC35cc6
zUDRIuD)!7W#)jTk1xahuOQhak2)-XI=6vU7qDXsSw)}*Xax>+{_;N1)SQS{&GDGy|
zvC1jJGmbicIN90L+OBTVEcul=^^DeX-{Rm+*F7X%WV0pJ@*dv!TEvmp>8IiUaHG=R
zeTJ^dLc-Z+tuiHBxvJOfkX`&%b4p86_=)n3LNlUY-hP=`>ail==+^)9cY7>zjy2G%
z^b*%=Z~2pT)31DU#rqkjoA%6oU+kB5F=eKH;Ju38-qmvjuY^W+yXo%UFuCk(iT;V}
ziFadn3uwr1H~r{cvYCBH)VbBpTQ`W+P3C^^;5t{FQeBbS|8E<+7X996^LKB9??s32
zKbv_&+E&jg6WMM5Ilj;6)xJX?ZNE+S_Ii9_9%BGXR4ImKelP%re-aY|12-_LGV_W{
z5_59&ic-?xF{L&GBc@6xoy}`D5NLUC)9RWxwS)64D~rn1MRHnSFNN)OQ?uv{{jX51
zxy3*JIHUTKwfXb@rj;Kr+V_gpeSV1avJ^qRXN5bonicAA+9*X&T*lGQ;M-Psz4Y_D
zx^-U|^)HzmEo_>sa_Os`QM{0cyr(fYbBWct8G(i$xxSvYP+#XLblltL=t*1dw-zm*
zculR(N^Qti2wExDwc)Cfbd2O=Hjd{L6(+r1$@tpJ_teT05wT%WrERZrV`aB3SvNIi
z-9e{kRT+Kf*L>$)=so-9)nmKaRRp)xY_6T~R`TXvD>nBpU%KXn-Yv?SbK3D<kR~ge
zNv78Y-O>ZDpF{*|7ks#2GUqMFkC~UlB~0aoraY48x%NL*D)8Ww-vUo%-gwPuS|a~-
zw{eBwvDOdkdu4v6H%VI@|0X>3AU|rL7vFpOq8k{B3xJpd80aOb#U;S3k&7?HO-~)<
zYH|>9d-&JpXur4ky<2L!T&1BJHy3qq%$6)s*lTo;sr#O-VAQ<B?uur2InMrl5qp>8
z4nuP9<*5<Oi5|&;Ht+eDPb`SusTaf`HL=ch$=<a`_O5xh?P*@ejq6v=-#9bv-Zf+0
z#jJ`dOTVPByj*#L|G3G{sn7qK-JcrqSSaPpLDPSeS1$Yg^VlnPRL?x#Y9$Es3@6kx
zMr8O#=(ecWHVvhuS!lj71p4OiUx}K#9Df*IuQ89B(`vA!P-Fi0cJmWEukP~}X>eN-
zFF5nTmLJoN?`*rn%h^8bm*nl6mu|BzOWmY!WXY77JqguX-G>Wj-(U6FPP<I>+76eQ
zd>2jsf_<|L**7&|%`Wdc!@V~F9mvRt6g3s4IbhH1o^Klr!f2lH?LR2gWWeL{zQ$F(
zTzaon!~Gz`OI{f>)uKMUUR(5W+Rd~3a&Dcr+FThpZ9)>edUcEUeUrE0b{%;>k2<gX
z@cGWiHvh6u`jLs3uNs~bQEIxFyO3{6*ipw6_9+7SD`#|dUTO@F%~~@h>G>DFGv}7s
zJaW1H#zph-iz#Z0miE2UoXndP{N(I9|F(UG%cd%}yZ+C&y>t4lmc+@!;i7AQe^YGO
z&$C<m>U$ac%<}pjueR>^{(3e)s-GWN+<U(b=nrN_<QOFE=}SOQ14{sqr@y{nu=9S}
zzeOl&S*O^)<=amkjZQQFoq9R$`pmQ4n_5JyEp95u{9cx6|H-i=S;&9MvGiT#20!Mc
z?NaEQlh+rxOhB+FkGpii7A-y{>jf=8rgyEJp&?swb?GrL%`(6LjW=&jai5bhd#*;R
z%-l;ZTdoA!rfzb~>DqJi>&Xk|$F>+f+3@21+V<+5v8>9KKUOR%{huSx^n-Wf?!emf
zA19an{=*u!`RLxn`+JZIzv}2_m+4!|e-#7$(h9^}P>%z>4XdRzSJ(!tt-$cP?rBH9
zCIbQ13$^|qFN><U@j2XidCvPvnU}hz@iHEP<UQ6pG5hx<9++#yop^EPnhib)jtku`
zuxYCwIhKD|#%{(5fvsU(ZYSUBcz)0Jc)4-njmhH53$OhQUDftP|HNvS<f-e9UuXP3
zA+)f$zV(^=_t);_3`p^X=4@_+vyEWx)&aU%YtXox3+QeJa60>@-!NSk$Od74pu3YZ
zit=+)^>P#QGSh%wz?a&R^Ycnl^GXzwit@{gQ;XtLlG5W7Q&RHtOceCE0=yZS<d|_a
zKO`7H0LW!n(g<RqHbz(>MHHHK=q94plE@~m22v<>CUFL$)&R%`DgYggs1JxU4YdeH
zHtj9cG$Ue-!z=@ljoZz_fK(!~LZXX+X{e<OvT5sC8Ia2w!p33dS!Cl@urZ+IU{**D
z#^*eY%!F)SJUatw)?$TZEvyFOO6AA~igPevrg>bxW@Tdl$0jGkWuVEm93UP5@Gl)6
index 2c18f6da191746c1a7c658087fbce264a410e5a6..16991f7a06aeaf2bb32a045d7c925f1d41372d9e
GIT binary patch
literal 2933
zc$~$Sc{o)2AIA@47)!QmD$M;w%D#@|+Vk8&*SHz{&?U<lV~eqjtt8pEvSfsiY(t_&
z_Fa~&C4<P;Ey<d23nBMt`8C6@ZqM(Z-#pLvnRA}!ocDRZ&iniMd=3T;Vc-Gi0T|$B
z7=R+N|Dl4X0{~M90I&fFz{b_plj!Mzb(3}?8vmOafMjPY8PR2^8hQD$0(1=Tz)Qiq
z0ige~roNCITj;{FM4?hC>~LowYeZ84V#<jl7j91yw&Np3>XbpmBeqT`=XXYh!V&G=
z9i7W7bAyo8l=5eegC287SqucMBgVuSG4!JXk{!xg9IyJSlYfT*{QZA6n?0I3GM1TF
zmnRukaj!{~J$pc1AVCeOA&D3yFB)gRfyqfMU0N0uk&O<2mhclVD~G-tqx=k0N3!vW
z)Jd}K?RaE_@&HnA<o?~+mz*Qq>6mNxaaw2naZ#;@ga@vA^&(l|?@qd_Xb&5SGPm4{
zTd+ASTi#nvq0Gc#GNVp!Y52tJg;!Y{E|H{9u`{pr7pK3rTO5;1f}T+sf0fpi)sD2v
zs#)aYH&qeJzgfKZl9@MFWd7>3W!&wis%mdRj5^`;^Q0*|4!6=Y({LxO)Q0fBhQ{0Q
z8q~&w-iLV{Tx$h)=Di<XYF6OuWhVZ7HdCc}QSX4sdIY}DKlFv)oJbO>HB$|Op^3^s
z5Pn_*eE-q(0Kf`H<wziUVx66(J#6j%laxgAuB5y!cM|T)a%!!tu}I8`L&HlfI75#e
z?$gR8JS{Mvu!ihCk9wDrx%r+?L@p<GPL=q&t2UdbGl6F+n76aktofcJpG>EyxaB}>
zn-|HVndyS^g9?{nM?aN*yIA|>HAEio!{X*13mXl1oKe=?p_rbv<@;&v@g_4e(hQ4`
z;v=<9de{0B28({pKPg|WP{U{siPu%U6TzRoWSL0B2g2fyXRXHk;a0Fr4r))M6eFuj
zyPO?L$2oONtE>Wp=ic_x`#AT!%|s^+t%UgiJn@C+h(t`bQ?VK{YUE8bud@=Ohaz!y
zpoFXIY-L_>SudaDe#0bdzR5te7-n>YwYUD)0}(^=zWJvc%Nc?y7c0ZeDi|H4Vh|=<
zp$9PS{eBsh)eXj2w#yVu{d5BwD;pA^v^0^ye7*td9bnGuzas<U*vqMC@TFb{7Yxk6
z6HoL6E5+q|0Vs4dvIHID(f)H&wIvq4Z|teC6pNR!=ulP!ES1+&pcXksA3dhVWp=Ge
zNATz<tb23HViYz4;4V=TPeQQAaR#aj9C`6>=9SWh0AKuht(@AT=Gwx^vX_L2!6$u>
z2D`3|4aiGnLj@1z{$<NBeWQ)z!I8?uSDQ!26HmV2w(V+E_>gdeyuQ{t!$b@7qEwmd
z3k>sT807yz!#xbwEfYOxb9D!e97xl&x&PcKYy+4rR5VKqmCbP%%~%OnY^&_AL-SnF
z&avW3c~-KTBtKF%vWGeR>U-Ydq26IA8DA{WoI^^vXzeE+-Q=D+Ua+h#;SC?CK&7xh
zR@nSVL#C!VXA+Eh9chX#1`X2DQAOkH<@^z*Iz!FKGD~eI%<r*{+(EKzsD*P{B0l`J
z$`{5B<$84;QiRRk%ocepUK#4HbAH;XeD93Gl{gzF5x)@h_>p;2^$3FA^T@tcy%l!G
z42qt8b3CPAu7l@b&|{ZOc9KbprY#eR)AdGjRAg_UsfAM^$>!Bx>|GDZs?Slwb11PF
zQzYT6+?W}$ggrJ!6WzD;!|LS7iGtx;Tdry&o%7=R+nY?`MeFYbFVwSDOY|=wG#q^b
zDrQP6R%TN<Xqlh=GPY0#azN;)6QqXTUb~@tLFkYL)otjvQ-C`9Wo!wz898$AhrIHR
z7IQnrbv(uLNlJHgaR`sfFGE77*2#_<OPZcIZvC8A`zjyV)ik>*ftWNxi~*UG>mq^G
z>w1Ye`$3iKA*)GIH<Cqv_U+GY)fMwTu@yKpMAAufNKF^TBhq_OC4B~J_+m}xsG6a-
z?LCUECCG_8Qwv4mew7wbp);%bSzcSt2k2M#+^sh7f3%q3y}ki8xz|#Q9j~GL3hrwj
zj7lo;c~1mcLczuI863p^GPH#9)r=7NyU3+FHQ9q?IiWrN`im6!A#L_!BhwGjecrkv
zV)A4*PF#(O<f-}^Y(qMd75g})@Q$9fW~O!zql8Fvt7{WNJ-Lmu)Feu~eO~gnmD9(k
z@5T=%>=ViySTimNn~-U{iNYn`dhmp9E6&(GI3RRVXJuB$2l#5+eTQw<ueOn2eD4K4
z=kDI7|0mxppl{&gK1(>RD)TdVyuCO;-#7;kR~Njr3zpz$2O@lLx8Ym~o_K<%fQ^T%
z4-xNSWou(^g|)SHB`63;vtZD4^#6i<>uY2`u>Av1ft<kYBie5h=%*I7<8eC`X@@4Y
z;!W_6W`zEjAngR87L)-^f1L+ErbgR>sns?<t08|>Zg&r)mg`^us5&C_Q(XU`M%!Je
z)#{)Cb+7qnx!pBOEmzJ6&{Qz=Qw4uVZ+9_K>k*g$+R}o4Dy{!2_^n+|Ehr2FcJKLb
TQ9~Jlk7+Uk6j1phZ2RnQ9pEq>
--- a/browser/devtools/debugger/test/browser_dbg_addon-modules-unpacked.js
+++ b/browser/devtools/debugger/test/browser_dbg_addon-modules-unpacked.js
@@ -3,46 +3,59 @@
 
 // Make sure the add-on actor can see loaded JS Modules from an add-on
 
 const ADDON_URL = EXAMPLE_URL + "addon5.xpi";
 
 function test() {
   Task.spawn(function () {
     let addon = yield addAddon(ADDON_URL);
+    let tab1 = yield addTab("chrome://browser_dbg_addon5/content/test.xul");
+
     let addonDebugger = yield initAddonDebugger(ADDON_URL);
 
     is(addonDebugger.title, "Debugger - Test unpacked add-on with JS Modules", "Saw the right toolbox title.");
 
     // Check the inital list of sources is correct
     let groups = yield addonDebugger.getSourceGroups();
     is(groups[0].name, "browser_dbg_addon5@tests.mozilla.org", "Add-on code should be the first group");
-    is(groups.length, 1, "Should be only one group.");
+    is(groups[1].name, "chrome://global", "XUL code should be the second group");
+    is(groups.length, 2, "Should be only two groups.");
 
     let sources = groups[0].sources;
-    is(sources.length, 2, "Should be two sources");
-    ok(sources[0].url.endsWith("/browser_dbg_addon5@tests.mozilla.org/bootstrap.js"), "correct url for bootstrap code")
-    is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
-    is(sources[1].url, "resource://browser_dbg_addon5/test.jsm", "correct url for addon code")
-    is(sources[1].label, "test.jsm", "correct label for addon code")
-
-    // Load a new module and check it appears in the list of sources
-    Cu.import("resource://browser_dbg_addon5/test2.jsm", {});
-
-    groups = yield addonDebugger.getSourceGroups();
-    is(groups[0].name, "browser_dbg_addon5@tests.mozilla.org", "Add-on code should be the first group");
-    is(groups.length, 1, "Should be only one group.");
-
-    sources = groups[0].sources;
     is(sources.length, 3, "Should be three sources");
     ok(sources[0].url.endsWith("/browser_dbg_addon5@tests.mozilla.org/bootstrap.js"), "correct url for bootstrap code")
     is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
     is(sources[1].url, "resource://browser_dbg_addon5/test.jsm", "correct url for addon code")
     is(sources[1].label, "test.jsm", "correct label for addon code")
-    is(sources[2].url, "resource://browser_dbg_addon5/test2.jsm", "correct url for addon code")
-    is(sources[2].label, "test2.jsm", "correct label for addon code")
+    is(sources[2].url, "chrome://browser_dbg_addon5/content/testxul.js", "correct url for addon tab code")
+    is(sources[2].label, "testxul.js", "correct label for addon tab code")
+
+    // Load a new module and tab and check they appear in the list of sources
+    Cu.import("resource://browser_dbg_addon5/test2.jsm", {});
+    let tab2 = yield addTab("chrome://browser_dbg_addon5/content/test2.xul");
+
+    groups = yield addonDebugger.getSourceGroups();
+    is(groups[0].name, "browser_dbg_addon5@tests.mozilla.org", "Add-on code should be the first group");
+    is(groups[1].name, "chrome://global", "XUL code should be the second group");
+    is(groups.length, 2, "Should be only two groups.");
+
+    sources = groups[0].sources;
+    is(sources.length, 5, "Should be five sources");
+    ok(sources[0].url.endsWith("/browser_dbg_addon5@tests.mozilla.org/bootstrap.js"), "correct url for bootstrap code")
+    is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
+    is(sources[1].url, "resource://browser_dbg_addon5/test.jsm", "correct url for addon code")
+    is(sources[1].label, "test.jsm", "correct label for addon code")
+    is(sources[2].url, "chrome://browser_dbg_addon5/content/testxul.js", "correct url for addon tab code")
+    is(sources[2].label, "testxul.js", "correct label for addon tab code")
+    is(sources[3].url, "resource://browser_dbg_addon5/test2.jsm", "correct url for addon code")
+    is(sources[3].label, "test2.jsm", "correct label for addon code")
+    is(sources[4].url, "chrome://browser_dbg_addon5/content/testxul2.js", "correct url for addon tab code")
+    is(sources[4].label, "testxul2.js", "correct label for addon tab code")
 
     Cu.unload("resource://browser_dbg_addon5/test2.jsm");
     yield addonDebugger.destroy();
+    yield removeTab(tab1);
+    yield removeTab(tab2);
     yield removeAddon(addon);
     finish();
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_addon-modules.js
+++ b/browser/devtools/debugger/test/browser_dbg_addon-modules.js
@@ -3,46 +3,59 @@
 
 // Make sure the add-on actor can see loaded JS Modules from an add-on
 
 const ADDON_URL = EXAMPLE_URL + "addon4.xpi";
 
 function test() {
   Task.spawn(function () {
     let addon = yield addAddon(ADDON_URL);
+    let tab1 = yield addTab("chrome://browser_dbg_addon4/content/test.xul");
+
     let addonDebugger = yield initAddonDebugger(ADDON_URL);
 
     is(addonDebugger.title, "Debugger - Test add-on with JS Modules", "Saw the right toolbox title.");
 
     // Check the inital list of sources is correct
     let groups = yield addonDebugger.getSourceGroups();
     is(groups[0].name, "browser_dbg_addon4@tests.mozilla.org", "Add-on code should be the first group");
-    is(groups.length, 1, "Should be only one group.");
+    is(groups[1].name, "chrome://global", "XUL code should be the second group");
+    is(groups.length, 2, "Should be only two groups.");
 
     let sources = groups[0].sources;
-    is(sources.length, 2, "Should be two sources");
-    ok(sources[0].url.endsWith("/browser_dbg_addon4@tests.mozilla.org.xpi!/bootstrap.js"), "correct url for bootstrap code")
-    is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
-    is(sources[1].url, "resource://browser_dbg_addon4/test.jsm", "correct url for addon code")
-    is(sources[1].label, "test.jsm", "correct label for addon code")
-
-    // Load a new module and check it appears in the list of sources
-    Cu.import("resource://browser_dbg_addon4/test2.jsm", {});
-
-    groups = yield addonDebugger.getSourceGroups();
-    is(groups[0].name, "browser_dbg_addon4@tests.mozilla.org", "Add-on code should be the first group");
-    is(groups.length, 1, "Should be only one group.");
-
-    sources = groups[0].sources;
     is(sources.length, 3, "Should be three sources");
     ok(sources[0].url.endsWith("/browser_dbg_addon4@tests.mozilla.org.xpi!/bootstrap.js"), "correct url for bootstrap code")
     is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
     is(sources[1].url, "resource://browser_dbg_addon4/test.jsm", "correct url for addon code")
     is(sources[1].label, "test.jsm", "correct label for addon code")
-    is(sources[2].url, "resource://browser_dbg_addon4/test2.jsm", "correct url for addon code")
-    is(sources[2].label, "test2.jsm", "correct label for addon code")
+    is(sources[2].url, "chrome://browser_dbg_addon4/content/testxul.js", "correct url for addon tab code")
+    is(sources[2].label, "testxul.js", "correct label for addon tab code")
+
+    // Load a new module and tab and check they appear in the list of sources
+    Cu.import("resource://browser_dbg_addon4/test2.jsm", {});
+    let tab2 = yield addTab("chrome://browser_dbg_addon4/content/test2.xul");
+
+    groups = yield addonDebugger.getSourceGroups();
+    is(groups[0].name, "browser_dbg_addon4@tests.mozilla.org", "Add-on code should be the first group");
+    is(groups[1].name, "chrome://global", "XUL code should be the second group");
+    is(groups.length, 2, "Should be only two groups.");
+
+    sources = groups[0].sources;
+    is(sources.length, 5, "Should be five sources");
+    ok(sources[0].url.endsWith("/browser_dbg_addon4@tests.mozilla.org.xpi!/bootstrap.js"), "correct url for bootstrap code")
+    is(sources[0].label, "bootstrap.js", "correct label for bootstrap code")
+    is(sources[1].url, "resource://browser_dbg_addon4/test.jsm", "correct url for addon code")
+    is(sources[1].label, "test.jsm", "correct label for addon code")
+    is(sources[2].url, "chrome://browser_dbg_addon4/content/testxul.js", "correct url for addon tab code")
+    is(sources[2].label, "testxul.js", "correct label for addon tab code")
+    is(sources[3].url, "resource://browser_dbg_addon4/test2.jsm", "correct url for addon code")
+    is(sources[3].label, "test2.jsm", "correct label for addon code")
+    is(sources[4].url, "chrome://browser_dbg_addon4/content/testxul2.js", "correct url for addon tab code")
+    is(sources[4].label, "testxul2.js", "correct label for addon tab code")
 
     Cu.unload("resource://browser_dbg_addon4/test2.jsm");
     yield addonDebugger.destroy();
+    yield removeTab(tab1);
+    yield removeTab(tab2);
     yield removeAddon(addon);
     finish();
   });
 }
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -113,17 +113,21 @@ function addAddon(aUrl) {
 
   let deferred = promise.defer();
 
   AddonManager.getInstallForURL(aUrl, aInstaller => {
     aInstaller.install();
     let listener = {
       onInstallEnded: function(aAddon, aAddonInstall) {
         aInstaller.removeListener(listener);
-        deferred.resolve(aAddonInstall);
+
+        // Wait for add-on's startup scripts to execute. See bug 997408
+        executeSoon(function() {
+          deferred.resolve(aAddonInstall);
+        });
       }
     };
     aInstaller.addListener(listener);
   }, "application/x-xpinstall");
 
   return deferred.promise;
 }
 
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -608,16 +608,17 @@ toolbarbutton[sdk-button="true"][cui-are
   width: 32px;
 }
 
 #nav-bar #PanelUI-menu-button {
   -moz-padding-start: 7px;
   -moz-padding-end: 5px;
 }
 
+:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]) > .toolbarbutton-menubutton-button[open] + .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-badge-container > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-icon {
   background-color: hsla(0,0%,100%,.3);
   background-image: linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.2));
   border: 1px solid rgb(154,154,154);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
@@ -626,33 +627,34 @@ toolbarbutton[sdk-button="true"][cui-are
 }
 
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   margin-top: 4px;
   margin-bottom: 4px;
 }
 
-:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover:active > .toolbarbutton-icon,
+:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):-moz-any(:hover:active, [open="true"]) > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open="true"] > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon {
   background-color: rgba(154,154,154,.5);
   background-image: linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.4));
   border: 1px solid rgb(154,154,154);
   box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset,
               0 0 1px hsla(0,0%,0%,.3) inset;
   transition-duration: 10ms;
 }
 
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[checked]:not(:active):hover > .toolbarbutton-icon {
   background-color: rgba(90%,90%,90%,.4);
   transition: background-color 150ms;
 }
 
+:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button[open],
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:hover:active,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:hover:active {
   padding: 3px;
 }
 
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:hover):not(:active):not([open]) > .toolbarbutton-menubutton-dropmarker::before {
   content: "";
   display: -moz-box;
deleted file mode 100644
--- a/browser/themes/linux/devtools/ruleview.css
+++ /dev/null
@@ -1,150 +0,0 @@
-/* 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/. */
-
-.ruleview {
-  height: 100%;
-}
-
-.ruleview-rule-source {
-  -moz-padding-start: 5px;
-  cursor: pointer;
-  text-align: right;
-  float: right;
-  -moz-user-select: none;
-}
-
-.ruleview-header {
-  border-top-width: 1px;
-  border-bottom-width: 1px;
-  border-top-style: solid;
-  border-bottom-style: solid;
-  padding: 1px 4px;
-  margin-top: 4px;
-  -moz-user-select: none;
-  word-wrap: break-word;
-}
-
-.ruleview-rule-source:hover {
-  text-decoration: underline;
-}
-
-.ruleview-rule,
-#noResults {
-  padding: 2px 4px;
-}
-
-#noResults {
-  font: message-box;
-  color: GrayText;
-}
-
-.ruleview-rule + .ruleview-rule {
-  border-top-width: 1px;
-  border-top-style: dotted;
-}
-
-.ruleview-warning {
-  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
-  -moz-margin-start: 5px;
-  display: inline-block;
-  vertical-align: top;
-  width: 13px;
-  height: 12px;
-}
-
-.ruleview-ruleopen {
-  -moz-padding-end: 5px;
-}
-
-.ruleview-ruleclose {
-  cursor: text;
-  padding-right: 20px;
-}
-
-.ruleview-propertylist {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-}
-
-.ruleview-rule:not(:hover) .ruleview-enableproperty {
-  visibility: hidden;
-}
-
-.ruleview-expander {
-  display: inline-block;
-}
-
-.ruleview-newproperty {
-  /* (enable checkbox width: 12px) + (expander width: 15px) */
-  -moz-margin-start: 27px;
-}
-
-.ruleview-namecontainer,
-.ruleview-propertycontainer,
-.ruleview-propertyname,
-.ruleview-propertyvalue {
-  text-decoration: inherit;
-}
-
-.ruleview-computedlist {
-  list-style: none;
-  padding: 0;
-}
-
-.ruleview-computed {
-  -moz-margin-start: 35px;
-}
-
-.ruleview-colorswatch {
-  border-radius: 50%;
-  width: 1em;
-  height: 1em;
-  vertical-align: text-top;
-  -moz-margin-end: 5px;
-}
-
-.ruleview-overridden {
-  text-decoration: line-through;
-}
-
-.theme-light .ruleview-overridden {
-  -moz-text-decoration-color: #667380; /*  Content (Text) - Dark Grey */
-}
-
-.styleinspector-propertyeditor {
-  border: 1px solid #CCC;
-  padding: 0;
-}
-
-.ruleview-property {
-  border-left: 2px solid transparent;
-  clear: right;
-}
-
-.ruleview-property  > * {
-  vertical-align: middle;
-}
-
-.ruleview-property[dirty] {
-  border-left-color: #68E268;
-}
-
-.ruleview-namecontainer > .ruleview-propertyname,
-.ruleview-propertycontainer > .ruleview-propertyvalue {
-  border-bottom: 1px dashed transparent;
-}
-
-.ruleview-namecontainer:hover > .ruleview-propertyname,
-.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
-  border-bottom-color: hsl(0,0%,50%);
-}
-
-.ruleview-selector {
-  word-wrap: break-word;
-}
-
-.ruleview-selector-separator, .ruleview-selector-unmatched {
-  color: #888;
-}
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -100,17 +100,16 @@ browser.jar:
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
   skin/classic/browser/feeds/videoFeedIcon.png        (feeds/feedIcon.png)
   skin/classic/browser/feeds/videoFeedIcon16.png      (feeds/feedIcon16.png)
   skin/classic/browser/feeds/audioFeedIcon.png        (feeds/feedIcon.png)
   skin/classic/browser/feeds/audioFeedIcon16.png      (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css            (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css         (feeds/subscribe-ui.css)
-  skin/classic/browser/fonts/ClearSans-Regular.ttf    (../shared/ClearSans-Regular.ttf)
   skin/classic/browser/newtab/newTab.css              (newtab/newTab.css)
   skin/classic/browser/newtab/controls.png            (../shared/newtab/controls.png)
   skin/classic/browser/places/bookmarksMenu.png       (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png    (places/bookmarksToolbar.png)
   skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
   skin/classic/browser/places/bookmarks-notification-finish.png  (places/bookmarks-notification-finish.png)
   skin/classic/browser/places/bookmarks-menu-arrow.png           (places/bookmarks-menu-arrow.png)
   skin/classic/browser/places/calendar.png            (places/calendar.png)
@@ -196,17 +195,17 @@ browser.jar:
   skin/classic/browser/devtools/command-scratchpad@2x.png     (../shared/devtools/images/command-scratchpad@2x.png)
   skin/classic/browser/devtools/command-tilt.png              (../shared/devtools/images/command-tilt.png)
   skin/classic/browser/devtools/command-tilt@2x.png           (../shared/devtools/images/command-tilt@2x.png)
   skin/classic/browser/devtools/command-pick.png              (../shared/devtools/images/command-pick.png)
   skin/classic/browser/devtools/command-pick@2x.png           (../shared/devtools/images/command-pick@2x.png)
   skin/classic/browser/devtools/command-console.png           (../shared/devtools/images/command-console.png)
   skin/classic/browser/devtools/command-console@2x.png        (../shared/devtools/images/command-console@2x.png)
   skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
-  skin/classic/browser/devtools/ruleview.css          (devtools/ruleview.css)
+* skin/classic/browser/devtools/ruleview.css          (../shared/devtools/ruleview.css)
 * skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
   skin/classic/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
   skin/classic/browser/devtools/webconsole.png                  (devtools/webconsole.png)
   skin/classic/browser/devtools/commandline.css              (devtools/commandline.css)
   skin/classic/browser/devtools/markup-view.css       (../shared/devtools/markup-view.css)
   skin/classic/browser/devtools/editor-error.png       (devtools/editor-error.png)
   skin/classic/browser/devtools/editor-breakpoint.png  (devtools/editor-breakpoint.png)
   skin/classic/browser/devtools/editor-debug-location.png (devtools/editor-debug-location.png)
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -507,31 +507,33 @@ toolbar .toolbarbutton-1 > .toolbarbutto
   transition-duration: 250ms;
 }
 
 toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],#back-button,#forward-button)) {
   padding: 0 4px;
 }
 
 toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],[disabled],[open],#back-button,#forward-button)):hover,
+toolbar .toolbarbutton-1[type="menu-button"]:not([disabled]) > .toolbarbutton-menubutton-button[open] + .toolbarbutton-menubutton-dropmarker,
 toolbar .toolbarbutton-1[type="menu-button"]:not([disabled]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-button,
 toolbar .toolbarbutton-1[type="menu-button"]:not([disabled]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker,
 toolbar .toolbaritem-combined-buttons:hover > .toolbarbutton-combined {
   border-color: hsla(0,0%,0%,.2);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.5),
               0 1px 0 hsla(0,0%,100%,.5) inset;
 }
 
 toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],[disabled],[open],#back-button,#forward-button)):hover,
 toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open]))[buttonover] > .toolbarbutton-menubutton-button,
 toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open],[buttonover])):hover > .toolbarbutton-menubutton-dropmarker {
   background: hsla(0,0%,100%,.1) linear-gradient(hsla(0,0%,100%,.3), hsla(0,0%,100%,.1)) padding-box;
 }
 
 toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],[disabled],#back-button,#forward-button)):-moz-any(:hover:active,[open],[checked]),
+toolbar .toolbarbutton-1[type="menu-button"]:not([disabled]) > .toolbarbutton-menubutton-button[open],
 toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open]))[buttonover]:active > .toolbarbutton-menubutton-button,
 toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open],[buttonover])):hover:active > .toolbarbutton-menubutton-dropmarker,
 toolbar .toolbarbutton-1[type="menu-button"][open]:not([disabled]) > .toolbarbutton-menubutton-dropmarker {
   background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), hsla(0,0%,0%,0)) border-box;
   border-color: hsla(0,0%,0%,.3);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.5),
               0 1px 0 hsla(0,0%,0%,.05) inset,
               0 1px 1px hsla(0,0%,0%,.2) inset;
deleted file mode 100644
--- a/browser/themes/osx/devtools/ruleview.css
+++ /dev/null
@@ -1,154 +0,0 @@
-/* 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/. */
-
-.ruleview {
-  height: 100%;
-}
-
-.ruleview-rule-source {
-  -moz-padding-start: 5px;
-  cursor: pointer;
-  text-align: right;
-  float: right;
-  -moz-user-select: none;
-}
-
-.ruleview-header {
-  border-top-width: 1px;
-  border-bottom-width: 1px;
-  border-top-style: solid;
-  border-bottom-style: solid;
-  padding: 1px 4px;
-  -moz-user-select: none;
-  word-wrap: break-word;
-}
-
-.ruleview-rule-pseudo-element {
-  padding-left:20px;
-  border-left: solid 10px;
-}
-
-.ruleview-rule-source:hover {
-  text-decoration: underline;
-}
-
-.ruleview-rule,
-#noResults {
-  padding: 2px 4px;
-}
-
-#noResults {
-  font: message-box;
-  color: GrayText;
-}
-
-.ruleview-rule + .ruleview-rule {
-  border-top-width: 1px;
-  border-top-style: dotted;
-}
-
-.ruleview-warning {
-  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
-  -moz-margin-start: 5px;
-  display: inline-block;
-  vertical-align: top;
-  width: 13px;
-  height: 12px;
-}
-
-.ruleview-ruleopen {
-  -moz-padding-end: 5px;
-}
-
-.ruleview-ruleclose {
-  cursor: text;
-  padding-right: 20px;
-}
-
-.ruleview-propertylist {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-}
-
-.ruleview-rule:not(:hover) .ruleview-enableproperty {
-  visibility: hidden;
-}
-
-.ruleview-expander {
-  display: inline-block;
-}
-
-.ruleview-newproperty {
-  /* (enable checkbox width: 12px) + (expander width: 15px) */
-  -moz-margin-start: 27px;
-}
-
-.ruleview-namecontainer,
-.ruleview-propertycontainer,
-.ruleview-propertyname,
-.ruleview-propertyvalue {
-  text-decoration: inherit;
-}
-
-.ruleview-computedlist {
-  list-style: none;
-  padding: 0;
-}
-
-.ruleview-computed {
-  -moz-margin-start: 35px;
-}
-
-.ruleview-colorswatch {
-  border-radius: 50%;
-  width: 1em;
-  height: 1em;
-  vertical-align: text-top;
-  -moz-margin-end: 5px;
-}
-
-.ruleview-overridden {
-  text-decoration: line-through;
-}
-
-.theme-light .ruleview-overridden {
-  -moz-text-decoration-color: #667380; /*  Content (Text) - Dark Grey */
-}
-
-.styleinspector-propertyeditor {
-  border: 1px solid #CCC;
-  padding: 0;
-}
-
-.ruleview-property {
-  border-left: 2px solid transparent;
-  clear: right;
-}
-
-.ruleview-property  > * {
-  vertical-align: middle;
-}
-
-.ruleview-property[dirty] {
-  border-left-color: #68E268;
-}
-
-.ruleview-namecontainer > .ruleview-propertyname,
-.ruleview-propertycontainer > .ruleview-propertyvalue {
-  border-bottom: 1px dashed transparent;
-}
-
-.ruleview-namecontainer:hover > .ruleview-propertyname,
-.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
-  border-bottom-color: hsl(0,0%,50%);
-}
-
-.ruleview-selector {
-  word-wrap: break-word;
-}
-
-.ruleview-selector-separator, .ruleview-selector-unmatched {
-  color: #888;
-}
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -164,17 +164,16 @@ browser.jar:
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css               (feeds/subscribe-ui.css)
   skin/classic/browser/feeds/feedIcon.png                   (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                 (feeds/feedIcon16.png)
   skin/classic/browser/feeds/videoFeedIcon.png              (feeds/feedIcon.png)
   skin/classic/browser/feeds/videoFeedIcon16.png            (feeds/feedIcon16.png)
   skin/classic/browser/feeds/audioFeedIcon.png              (feeds/feedIcon.png)
   skin/classic/browser/feeds/audioFeedIcon16.png            (feeds/feedIcon16.png)
-  skin/classic/browser/fonts/ClearSans-Regular.ttf          (../shared/ClearSans-Regular.ttf)
   skin/classic/browser/newtab/newTab.css                    (newtab/newTab.css)
   skin/classic/browser/newtab/controls.png                  (../shared/newtab/controls.png)
   skin/classic/browser/newtab/controls@2x.png               (newtab/controls@2x.png)
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
   skin/classic/browser/places/allBookmarks.png              (places/allBookmarks.png)
 * skin/classic/browser/places/places.css                    (places/places.css)
@@ -317,17 +316,17 @@ browser.jar:
   skin/classic/browser/devtools/command-scratchpad@2x.png     (../shared/devtools/images/command-scratchpad@2x.png)
   skin/classic/browser/devtools/command-tilt.png              (../shared/devtools/images/command-tilt.png)
   skin/classic/browser/devtools/command-tilt@2x.png           (../shared/devtools/images/command-tilt@2x.png)
   skin/classic/browser/devtools/command-pick.png              (../shared/devtools/images/command-pick.png)
   skin/classic/browser/devtools/command-pick@2x.png           (../shared/devtools/images/command-pick@2x.png)
   skin/classic/browser/devtools/command-console.png           (../shared/devtools/images/command-console.png)
   skin/classic/browser/devtools/command-console@2x.png        (../shared/devtools/images/command-console@2x.png)
   skin/classic/browser/devtools/alerticon-warning.png       (devtools/alerticon-warning.png)
-  skin/classic/browser/devtools/ruleview.css                (devtools/ruleview.css)
+* skin/classic/browser/devtools/ruleview.css                (../shared/devtools/ruleview.css)
   skin/classic/browser/devtools/commandline.css             (devtools/commandline.css)
   skin/classic/browser/devtools/markup-view.css             (../shared/devtools/markup-view.css)
   skin/classic/browser/devtools/editor-error.png             (devtools/editor-error.png)
   skin/classic/browser/devtools/editor-breakpoint.png        (devtools/editor-breakpoint.png)
   skin/classic/browser/devtools/editor-debug-location.png    (devtools/editor-debug-location.png)
 * skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
   skin/classic/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
   skin/classic/browser/devtools/webconsole.png                  (devtools/webconsole.png)
deleted file mode 100644
index fe686f8d2a1343973005977cf21b00b8a388d56a..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/devtools/ruleview.css
@@ -0,0 +1,161 @@
+/* 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/. */
+
+.ruleview {
+  height: 100%;
+}
+
+.ruleview-rule-source {
+  -moz-padding-start: 5px;
+  cursor: pointer;
+  text-align: right;
+  float: right;
+  -moz-user-select: none;
+}
+
+.ruleview-header {
+  border-top-width: 1px;
+  border-bottom-width: 1px;
+  border-top-style: solid;
+  border-bottom-style: solid;
+  padding: 1px 4px;
+  -moz-user-select: none;
+  word-wrap: break-word;
+%ifndef XP_MACOSX
+  margin-top: 4px;
+%endif
+}
+
+.ruleview-rule-pseudo-element {
+  padding-left:20px;
+  border-left: solid 10px;
+}
+
+.ruleview-rule-source:hover {
+  text-decoration: underline;
+}
+
+.ruleview-rule,
+#noResults {
+  padding: 2px 4px;
+}
+
+#noResults {
+  font: message-box;
+  color: GrayText;
+}
+
+.ruleview-rule + .ruleview-rule {
+  border-top-width: 1px;
+  border-top-style: dotted;
+}
+
+.ruleview-warning {
+  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
+  -moz-margin-start: 5px;
+  display: inline-block;
+  vertical-align: top;
+  width: 13px;
+  height: 12px;
+}
+
+.ruleview-ruleopen {
+  -moz-padding-end: 5px;
+}
+
+.ruleview-ruleclose {
+  cursor: text;
+  padding-right: 20px;
+}
+
+.ruleview-propertylist {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.ruleview-rule:not(:hover) .ruleview-enableproperty {
+  visibility: hidden;
+}
+
+.ruleview-expander {
+  display: inline-block;
+}
+
+.ruleview-newproperty {
+  /* (enable checkbox width: 12px) + (expander width: 15px) */
+  -moz-margin-start: 27px;
+}
+
+.ruleview-namecontainer,
+.ruleview-propertycontainer,
+.ruleview-propertyname,
+.ruleview-propertyvalue {
+  text-decoration: inherit;
+}
+
+.ruleview-computedlist {
+  list-style: none;
+  padding: 0;
+}
+
+.ruleview-computed {
+  -moz-margin-start: 35px;
+}
+
+.ruleview-colorswatch {
+  border-radius: 50%;
+  width: 1em;
+  height: 1em;
+  vertical-align: text-top;
+  -moz-margin-end: 5px;
+}
+
+.ruleview-overridden {
+  text-decoration: line-through;
+}
+
+.theme-light .ruleview-overridden {
+  -moz-text-decoration-color: #667380; /*  Content (Text) - Dark Grey */
+}
+
+.styleinspector-propertyeditor {
+  border: 1px solid #CCC;
+  padding: 0;
+}
+
+.ruleview-property {
+  border-left: 3px solid transparent;
+  clear: right;
+}
+
+.ruleview-property  > * {
+  vertical-align: middle;
+}
+
+.theme-dark .ruleview-property[dirty] {
+  border-left-color: #70bf53; /* Green */
+}
+
+.theme-light .ruleview-property[dirty] {
+  border-left-color: #2cbb0f; /* Green */
+}
+
+.ruleview-namecontainer > .ruleview-propertyname,
+.ruleview-propertycontainer > .ruleview-propertyvalue {
+  border-bottom: 1px dashed transparent;
+}
+
+.ruleview-namecontainer:hover > .ruleview-propertyname,
+.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
+  border-bottom-color: hsl(0,0%,50%);
+}
+
+.ruleview-selector {
+  word-wrap: break-word;
+}
+
+.ruleview-selector-separator, .ruleview-selector-unmatched {
+  color: #888;
+}
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -2,17 +2,17 @@
 /* - 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/. */
 %endif
 @namespace html "http://www.w3.org/1999/xhtml";
 
 @font-face {
   font-family: "Clear Sans";
-  src: url("chrome://browser/skin/fonts/ClearSans-Regular.ttf");
+  src: url("chrome://browser/content/fonts/ClearSans-Regular.woff") format('woff');
 }
 
 page {
   -moz-appearance: none;
   background-image: linear-gradient(#FFFFFF, #EDEDED 100px);
 }
 
 caption {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -725,16 +725,17 @@ toolbarbutton[sdk-button="true"][cui-are
   background-repeat: no-repeat;
   background-size: 1px 16px;
 }
 
 @conditionalForwardWithUrlbar@ > .toolbarbutton-1:-moz-any([disabled],:not([open]):not([disabled]):not(:active)) > .toolbarbutton-icon {
   border-color: hsla(210,4%,10%,.1);
 }
 
+#nav-bar .toolbarbutton-1:not([disabled=true]) > .toolbarbutton-menubutton-button[open] + .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text,
 #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-container,
 @conditionalForwardWithUrlbar@ > #forward-button:not([open]):not(:active):not([disabled]):hover > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([buttonover]):not([open]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon {
   background-color: hsla(210,4%,10%,.08);
@@ -794,17 +795,17 @@ toolbarbutton[sdk-button="true"][cui-are
     background-color: hsla(210,48%,96%,.75);
     box-shadow: 0 0 1px hsla(210,54%,20%,.03),
                 0 0 2px hsla(210,54%,20%,.1);
   }
 %ifdef WINDOWS_AERO
 }
 %endif
 
-#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover:active > .toolbarbutton-icon,
+#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):-moz-any(:hover:active, [open]) > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container {
   background-color: hsla(210,4%,10%,.12);
   border-top-color: hsla(210,4%,10%,.2);
   box-shadow: 0 1px 0 0 hsla(210,4%,10%,.1) inset;
   transition-duration: 10ms;
deleted file mode 100644
--- a/browser/themes/windows/devtools/ruleview.css
+++ /dev/null
@@ -1,150 +0,0 @@
-/* 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/. */
-
-.ruleview {
-  height: 100%;
-}
-
-.ruleview-rule-source {
-  -moz-padding-start: 5px;
-  cursor: pointer;
-  text-align: right;
-  float: right;
-  -moz-user-select: none;
-}
-
-.ruleview-header {
-  border-top-width: 1px;
-  border-bottom-width: 1px;
-  border-top-style: solid;
-  border-bottom-style: solid;
-  padding: 1px 4px;
-  margin-top: 4px;
-  -moz-user-select: none;
-  word-wrap: break-word;
-}
-
-.ruleview-rule-source:hover {
-  text-decoration: underline;
-}
-
-.ruleview-rule,
-#noResults {
-  padding: 2px 4px;
-}
-
-#noResults {
-  font: message-box;
-  color: GrayText;
-}
-
-.ruleview-rule + .ruleview-rule {
-  border-top-width: 1px;
-  border-top-style: dotted;
-}
-
-.ruleview-warning {
-  background: url("chrome://browser/skin/devtools/alerticon-warning.png");
-  -moz-margin-start: 5px;
-  display: inline-block;
-  vertical-align: top;
-  width: 13px;
-  height: 12px;
-}
-
-.ruleview-ruleopen {
-  -moz-padding-end: 5px;
-}
-
-.ruleview-ruleclose {
-  cursor: text;
-  padding-right: 20px;
-}
-
-.ruleview-propertylist {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-}
-
-.ruleview-rule:not(:hover) .ruleview-enableproperty {
-  visibility: hidden;
-}
-
-.ruleview-expander {
-  display: inline-block;
-}
-
-.ruleview-newproperty {
-  /* (enable checkbox width: 12px) + (expander width: 15px) */
-  -moz-margin-start: 27px;
-}
-
-.ruleview-namecontainer,
-.ruleview-propertycontainer,
-.ruleview-propertyname,
-.ruleview-propertyvalue {
-  text-decoration: inherit;
-}
-
-.ruleview-computedlist {
-  list-style: none;
-  padding: 0;
-}
-
-.ruleview-computed {
-  -moz-margin-start: 35px;
-}
-
-.ruleview-colorswatch {
-  border-radius: 50%;
-  width: 1em;
-  height: 1em;
-  vertical-align: text-top;
-  -moz-margin-end: 5px;
-}
-
-.ruleview-overridden {
-  text-decoration: line-through;
-}
-
-.theme-light .ruleview-overridden {
-  -moz-text-decoration-color: #667380; /*  Content (Text) - Dark Grey */
-}
-
-.styleinspector-propertyeditor {
-  border: 1px solid #CCC;
-  padding: 0;
-}
-
-.ruleview-property {
-  border-left: 2px solid transparent;
-  clear: right;
-}
-
-.ruleview-property  > * {
-  vertical-align: middle;
-}
-
-.ruleview-property[dirty] {
-  border-left-color: #68E268;
-}
-
-.ruleview-namecontainer > .ruleview-propertyname,
-.ruleview-propertycontainer > .ruleview-propertyvalue {
-  border-bottom: 1px dashed transparent;
-}
-
-.ruleview-namecontainer:hover > .ruleview-propertyname,
-.ruleview-propertycontainer:hover > .ruleview-propertyvalue {
-  border-bottom-color: hsl(0,0%,50%);
-}
-
-.ruleview-selector {
-  word-wrap: break-word;
-}
-
-.ruleview-selector-separator, .ruleview-selector-unmatched {
-  color: #888;
-}
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -121,17 +121,16 @@ browser.jar:
         skin/classic/browser/feeds/feedIcon.png                      (feeds/feedIcon.png)
         skin/classic/browser/feeds/feedIcon16.png                    (feeds/feedIcon16.png)
         skin/classic/browser/feeds/audioFeedIcon.png                 (feeds/feedIcon.png)
         skin/classic/browser/feeds/audioFeedIcon16.png               (feeds/feedIcon16.png)
         skin/classic/browser/feeds/videoFeedIcon.png                 (feeds/feedIcon.png)
         skin/classic/browser/feeds/videoFeedIcon16.png               (feeds/feedIcon16.png)
         skin/classic/browser/feeds/subscribe.css                     (feeds/subscribe.css)
         skin/classic/browser/feeds/subscribe-ui.css                  (feeds/subscribe-ui.css)
-        skin/classic/browser/fonts/ClearSans-Regular.ttf             (../shared/ClearSans-Regular.ttf)
         skin/classic/browser/newtab/newTab.css                       (newtab/newTab.css)
         skin/classic/browser/newtab/controls.png                     (../shared/newtab/controls.png)
         skin/classic/browser/places/places.css                       (places/places.css)
 *       skin/classic/browser/places/organizer.css                    (places/organizer.css)
         skin/classic/browser/places/bookmark.png                     (places/bookmark.png)
         skin/classic/browser/places/query.png                        (places/query.png)
         skin/classic/browser/places/bookmarksMenu.png                (places/bookmarksMenu.png)
         skin/classic/browser/places/bookmarksToolbar.png             (places/bookmarksToolbar.png)
@@ -221,17 +220,17 @@ browser.jar:
 *       skin/classic/browser/devtools/dark-theme.css                (../shared/devtools/dark-theme.css)
 *       skin/classic/browser/devtools/light-theme.css               (../shared/devtools/light-theme.css)
         skin/classic/browser/devtools/filters.svg                   (../shared/devtools/filters.svg)
         skin/classic/browser/devtools/controls.png                  (../shared/devtools/images/controls.png)
         skin/classic/browser/devtools/controls@2x.png               (../shared/devtools/images/controls@2x.png)
 *       skin/classic/browser/devtools/widgets.css                   (devtools/widgets.css)
         skin/classic/browser/devtools/commandline-icon.png          (devtools/commandline-icon.png)
         skin/classic/browser/devtools/alerticon-warning.png         (devtools/alerticon-warning.png)
-        skin/classic/browser/devtools/ruleview.css                  (devtools/ruleview.css)
+*       skin/classic/browser/devtools/ruleview.css                  (../shared/devtools/ruleview.css)
         skin/classic/browser/devtools/commandline.css               (devtools/commandline.css)
         skin/classic/browser/devtools/command-paintflashing.png     (../shared/devtools/images/command-paintflashing.png)
         skin/classic/browser/devtools/command-paintflashing@2x.png  (../shared/devtools/images/command-paintflashing@2x.png)
         skin/classic/browser/devtools/command-responsivemode.png    (../shared/devtools/images/command-responsivemode.png)
         skin/classic/browser/devtools/command-responsivemode@2x.png (../shared/devtools/images/command-responsivemode@2x.png)
         skin/classic/browser/devtools/command-scratchpad.png        (../shared/devtools/images/command-scratchpad.png)
         skin/classic/browser/devtools/command-scratchpad@2x.png     (../shared/devtools/images/command-scratchpad@2x.png)
         skin/classic/browser/devtools/command-tilt.png              (../shared/devtools/images/command-tilt.png)
@@ -486,17 +485,16 @@ browser.jar:
         skin/classic/aero/browser/feeds/feedIcon.png                 (feeds/feedIcon-aero.png)
         skin/classic/aero/browser/feeds/feedIcon16.png               (feeds/feedIcon16-aero.png)
         skin/classic/aero/browser/feeds/audioFeedIcon.png            (feeds/feedIcon-aero.png)
         skin/classic/aero/browser/feeds/audioFeedIcon16.png          (feeds/feedIcon16-aero.png)
         skin/classic/aero/browser/feeds/videoFeedIcon.png            (feeds/feedIcon-aero.png)
         skin/classic/aero/browser/feeds/videoFeedIcon16.png          (feeds/feedIcon16-aero.png)
         skin/classic/aero/browser/feeds/subscribe.css                (feeds/subscribe.css)
         skin/classic/aero/browser/feeds/subscribe-ui.css             (feeds/subscribe-ui.css)
-        skin/classic/aero/browser/fonts/ClearSans-Regular.ttf        (../shared/ClearSans-Regular.ttf)
         skin/classic/aero/browser/newtab/newTab.css                  (newtab/newTab.css)
         skin/classic/aero/browser/newtab/controls.png                (../shared/newtab/controls.png)
 *       skin/classic/aero/browser/places/places.css                  (places/places-aero.css)
 *       skin/classic/aero/browser/places/organizer.css               (places/organizer-aero.css)
         skin/classic/aero/browser/places/bookmark.png                (places/bookmark-aero.png)
         skin/classic/aero/browser/places/query.png                   (places/query-aero.png)
         skin/classic/aero/browser/places/bookmarksMenu.png           (places/bookmarksMenu-aero.png)
         skin/classic/aero/browser/places/bookmarksToolbar.png        (places/bookmarksToolbar-aero.png)
@@ -597,17 +595,17 @@ browser.jar:
         skin/classic/aero/browser/devtools/command-scratchpad@2x.png (../shared/devtools/images/command-scratchpad@2x.png)
         skin/classic/aero/browser/devtools/command-tilt.png          (../shared/devtools/images/command-tilt.png)
         skin/classic/aero/browser/devtools/command-tilt@2x.png       (../shared/devtools/images/command-tilt@2x.png)
         skin/classic/aero/browser/devtools/command-pick.png          (../shared/devtools/images/command-pick.png)
         skin/classic/aero/browser/devtools/command-pick@2x.png       (../shared/devtools/images/command-pick@2x.png)
         skin/classic/aero/browser/devtools/command-console.png       (../shared/devtools/images/command-console.png)
         skin/classic/aero/browser/devtools/command-console@2x.png    (../shared/devtools/images/command-console@2x.png)
         skin/classic/aero/browser/devtools/alerticon-warning.png     (devtools/alerticon-warning.png)
-        skin/classic/aero/browser/devtools/ruleview.css              (devtools/ruleview.css)
+*       skin/classic/aero/browser/devtools/ruleview.css              (../shared/devtools/ruleview.css)
         skin/classic/aero/browser/devtools/commandline.css           (devtools/commandline.css)
         skin/classic/aero/browser/devtools/markup-view.css           (../shared/devtools/markup-view.css)
         skin/classic/aero/browser/devtools/editor-error.png           (devtools/editor-error.png)
         skin/classic/aero/browser/devtools/editor-breakpoint.png      (devtools/editor-breakpoint.png)
         skin/classic/aero/browser/devtools/editor-debug-location.png  (devtools/editor-debug-location.png)
 *       skin/classic/aero/browser/devtools/webconsole.css                  (devtools/webconsole.css)
         skin/classic/aero/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
         skin/classic/aero/browser/devtools/webconsole.png                  (devtools/webconsole.png)
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -486,75 +486,17 @@ abstract public class BrowserApp extends
 
         mBrowserSearchContainer = findViewById(R.id.search_container);
         mBrowserSearch = (BrowserSearch) getSupportFragmentManager().findFragmentByTag(BROWSER_SEARCH_TAG);
         if (mBrowserSearch == null) {
             mBrowserSearch = BrowserSearch.newInstance();
             mBrowserSearch.setUserVisibleHint(false);
         }
 
-        mBrowserToolbar.setOnActivateListener(new BrowserToolbar.OnActivateListener() {
-            public void onActivate() {
-                enterEditingMode();
-            }
-        });
-
-        mBrowserToolbar.setOnCommitListener(new BrowserToolbar.OnCommitListener() {
-            public void onCommit() {
-                commitEditingMode();
-            }
-        });
-
-        mBrowserToolbar.setOnDismissListener(new BrowserToolbar.OnDismissListener() {
-            public void onDismiss() {
-                mBrowserToolbar.cancelEdit();
-            }
-        });
-
-        mBrowserToolbar.setOnFilterListener(new BrowserToolbar.OnFilterListener() {
-            public void onFilter(String searchText, AutocompleteHandler handler) {
-                filterEditingMode(searchText, handler);
-            }
-        });
-
-        mBrowserToolbar.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                if (isHomePagerVisible()) {
-                    mHomePager.onToolbarFocusChange(hasFocus);
-                }
-            }
-        });
-
-        mBrowserToolbar.setOnStartEditingListener(new BrowserToolbar.OnStartEditingListener() {
-            public void onStartEditing() {
-                // Temporarily disable doorhanger notifications.
-                mDoorHangerPopup.disable();
-            }
-        });
-
-        mBrowserToolbar.setOnStopEditingListener(new BrowserToolbar.OnStopEditingListener() {
-            public void onStopEditing() {
-                selectTargetTabForEditingMode();
-
-                // Since the underlying LayerView is set visible in hideHomePager, we would
-                // ordinarily want to call it first. However, hideBrowserSearch changes the
-                // visibility of the HomePager and hideHomePager will take no action if the
-                // HomePager is hidden, so we want to call hideBrowserSearch to restore the
-                // HomePager visibility first.
-                hideBrowserSearch();
-                hideHomePager();
-
-                // Re-enable doorhanger notifications. They may trigger on the selected tab above.
-                mDoorHangerPopup.enable();
-            }
-        });
-
-        // Intercept key events for gamepad shortcuts
-        mBrowserToolbar.setOnKeyListener(this);
+        setBrowserToolbarListeners();
 
         mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page);
         mMediaCastingBar = (MediaCastingBar) findViewById(R.id.media_casting);
 
         registerEventListener("CharEncoding:Data");
         registerEventListener("CharEncoding:State");
         registerEventListener("Feedback:LastUrl");
         registerEventListener("Feedback:OpenPlayStore");
@@ -634,16 +576,78 @@ abstract public class BrowserApp extends
 
     @Override
     public void onPause() {
         super.onPause();
         // Register for Prompt:ShowTop so we can foreground this activity even if it's hidden.
         registerEventListener("Prompt:ShowTop");
     }
 
+    private void setBrowserToolbarListeners() {
+        mBrowserToolbar.setOnActivateListener(new BrowserToolbar.OnActivateListener() {
+            public void onActivate() {
+                enterEditingMode();
+            }
+        });
+
+        mBrowserToolbar.setOnCommitListener(new BrowserToolbar.OnCommitListener() {
+            public void onCommit() {
+                commitEditingMode();
+            }
+        });
+
+        mBrowserToolbar.setOnDismissListener(new BrowserToolbar.OnDismissListener() {
+            public void onDismiss() {
+                mBrowserToolbar.cancelEdit();
+            }
+        });
+
+        mBrowserToolbar.setOnFilterListener(new BrowserToolbar.OnFilterListener() {
+            public void onFilter(String searchText, AutocompleteHandler handler) {
+                filterEditingMode(searchText, handler);
+            }
+        });
+
+        mBrowserToolbar.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (isHomePagerVisible()) {
+                    mHomePager.onToolbarFocusChange(hasFocus);
+                }
+            }
+        });
+
+        mBrowserToolbar.setOnStartEditingListener(new BrowserToolbar.OnStartEditingListener() {
+            public void onStartEditing() {
+                // Temporarily disable doorhanger notifications.
+                mDoorHangerPopup.disable();
+            }
+        });
+
+        mBrowserToolbar.setOnStopEditingListener(new BrowserToolbar.OnStopEditingListener() {
+            public void onStopEditing() {
+                selectTargetTabForEditingMode();
+
+                // Since the underlying LayerView is set visible in hideHomePager, we would
+                // ordinarily want to call it first. However, hideBrowserSearch changes the
+                // visibility of the HomePager and hideHomePager will take no action if the
+                // HomePager is hidden, so we want to call hideBrowserSearch to restore the
+                // HomePager visibility first.
+                hideBrowserSearch();
+                hideHomePager();
+
+                // Re-enable doorhanger notifications. They may trigger on the selected tab above.
+                mDoorHangerPopup.enable();
+            }
+        });
+
+        // Intercept key events for gamepad shortcuts
+        mBrowserToolbar.setOnKeyListener(this);
+    }
+
     private void showBookmarkDialog() {
         final Tab tab = Tabs.getInstance().getSelectedTab();
         final Prompt ps = new Prompt(this, new Prompt.PromptCallback() {
             @Override
             public void onPromptFinished(String result) {
                 int itemId = -1;
                 try {
                   itemId = new JSONObject(result).getInt("button");
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -3,26 +3,22 @@
  * 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/. */
 
 package org.mozilla.gecko;
 
 import java.io.BufferedReader;
 import java.io.Closeable;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.net.Proxy;
-import java.net.URL;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Queue;
@@ -105,17 +101,16 @@ import android.view.ContextThemeWrapper;
 import android.view.HapticFeedbackConstants;
 import android.view.Surface;
 import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.MimeTypeMap;
 import android.widget.AbsoluteLayout;
-import android.widget.Toast;
 
 public class GeckoAppShell
 {
     private static final String LOGTAG = "GeckoAppShell";
     private static final boolean LOGGING = false;
 
     // We have static members only.
     private GeckoAppShell() { }
--- a/mobile/android/base/WebappImpl.java
+++ b/mobile/android/base/WebappImpl.java
@@ -18,23 +18,23 @@ import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Animation;
 import android.widget.ImageView;
 import android.view.Display;
 
-import java.net.URL;
 import java.io.File;
+import java.net.URI;
 
 public class WebappImpl extends GeckoApp {
     private static final String LOGTAG = "GeckoWebappImpl";
 
-    private URL mOrigin;
+    private URI mOrigin;
     private TextView mTitlebarText = null;
     private View mTitlebar = null;
 
     private View mSplashscreen;
 
     protected int getIndex() { return 0; }
 
     @Override
@@ -64,28 +64,28 @@ public class WebappImpl extends GeckoApp
         if (!action.startsWith(ACTION_WEBAPP_PREFIX)) {
             Log.e(LOGTAG, "Webapp launch, but intent action is " + action + "!");
             return;
         }
 
         // Try to use the origin stored in the WebappAllocator first
         String origin = WebappAllocator.getInstance(this).getAppForIndex(getIndex());
         try {
-            mOrigin = new URL(origin);
-        } catch (java.net.MalformedURLException ex) {
+            mOrigin = new URI(origin);
+        } catch (java.net.URISyntaxException ex) {
             // If we can't parse the this is an app protocol, just settle for not having an origin
             if (!origin.startsWith("app://")) {
                 return;
             }
 
             // If that failed fall back to the origin stored in the shortcut
             Log.i(LOGTAG, "Webapp is not registered with allocator");
             try {
-                mOrigin = new URL(getIntent().getData().toString());
-            } catch (java.net.MalformedURLException ex2) {
+                mOrigin = new URI(getIntent().getData().toString());
+            } catch (java.net.URISyntaxException ex2) {
                 Log.e(LOGTAG, "Unable to parse intent url: ", ex);
             }
         }
     }
 
     @Override
     protected void loadStartupTab(String uri) {
         String action = getIntent().getAction();
@@ -159,38 +159,38 @@ public class WebappImpl extends GeckoApp
 
     @Override
     public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
         switch(msg) {
             case SELECTED:
             case LOCATION_CHANGE:
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     final String urlString = tab.getURL();
-                    final URL url;
+                    final URI uri;
 
                     try {
-                        url = new URL(urlString);
-                    } catch (java.net.MalformedURLException ex) {
+                        uri = new URI(urlString);
+                    } catch (java.net.URISyntaxException ex) {
                         mTitlebarText.setText(urlString);
 
                         // If we can't parse the url, and its an app protocol hide
                         // the titlebar and return, otherwise show the titlebar
                         // and the full url
                         if (!urlString.startsWith("app://")) {
                             mTitlebar.setVisibility(View.VISIBLE);
                         } else {
                             mTitlebar.setVisibility(View.GONE);
                         }
                         return;
                     }
 
-                    if (mOrigin != null && mOrigin.getHost().equals(url.getHost())) {
+                    if (mOrigin != null && mOrigin.getHost().equals(uri.getHost())) {
                         mTitlebar.setVisibility(View.GONE);
                     } else {
-                        mTitlebarText.setText(url.getProtocol() + "://" + url.getHost());
+                        mTitlebarText.setText(uri.getScheme() + "://" + uri.getHost());
                         mTitlebar.setVisibility(View.VISIBLE);
                     }
                 }
                 break;
             case LOADED:
                 if (mSplashscreen != null && mSplashscreen.getVisibility() == View.VISIBLE) {
                     Animation fadeout = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
                     fadeout.setAnimationListener(new Animation.AnimationListener() {
--- a/mobile/android/base/background/nativecode/NativeCrypto.java
+++ b/mobile/android/base/background/nativecode/NativeCrypto.java
@@ -1,22 +1,40 @@
 /* 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/. */
 
 package org.mozilla.gecko.background.nativecode;
 
+import java.security.GeneralSecurityException;
+
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 
-import java.security.GeneralSecurityException;
+import android.util.Log;
 
 @RobocopTarget
 public class NativeCrypto {
   static {
-    System.loadLibrary("mozglue");
+    try {
+      System.loadLibrary("mozglue");
+    } catch (UnsatisfiedLinkError e) {
+      Log.wtf("NativeCrypto", "Couldn't load mozglue. Trying /data/app-lib path.");
+      try {
+        System.load("/data/app-lib/" + AppConstants.ANDROID_PACKAGE_NAME + "/libmozglue.so");
+      } catch (Throwable ee) {
+          try {
+            Log.wtf("NativeCrypto", "Couldn't load mozglue: " + ee + ". Trying /data/data path.");
+            System.load("/data/data/" + AppConstants.ANDROID_PACKAGE_NAME + "/lib/libmozglue.so");
+          } catch (UnsatisfiedLinkError eee) {
+              Log.wtf("NativeCrypto", "Failed every attempt to load mozglue. Giving up.");
+              throw new RuntimeException("Unable to load mozglue", eee);
+          }
+      }
+    }
   }
 
   /**
    * Wrapper to perform PBKDF2-HMAC-SHA-256 in native code.
    */
   public native static byte[] pbkdf2SHA256(byte[] password, byte[] salt, int c, int dkLen)
       throws GeneralSecurityException;
 
--- a/mobile/android/base/db/PasswordsProvider.java
+++ b/mobile/android/base/db/PasswordsProvider.java
@@ -1,27 +1,27 @@
 /* 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/. */
 
 package org.mozilla.gecko.db;
 
-import java.lang.IllegalArgumentException;
 import java.util.HashMap;
+
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.NSSBridge;
-import org.mozilla.gecko.db.DBUtils;
+import org.mozilla.gecko.db.BrowserContract.DeletedPasswords;
 import org.mozilla.gecko.db.BrowserContract.Passwords;
-import org.mozilla.gecko.db.BrowserContract.DeletedPasswords;
-import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.sqlite.MatrixBlobCursor;
 import org.mozilla.gecko.sqlite.SQLiteBridge;
 import org.mozilla.gecko.sync.Utils;
+
 import android.content.ContentValues;
 import android.content.Intent;
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.net.Uri;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -71,17 +71,20 @@ public class PasswordsProvider extends S
         PASSWORDS_PROJECTION_MAP.put(Passwords.TIMES_USED, Passwords.TIMES_USED);
 
         URI_MATCHER.addURI(BrowserContract.PASSWORDS_AUTHORITY, "deleted-passwords", DELETED_PASSWORDS);
 
         DELETED_PASSWORDS_PROJECTION_MAP = new HashMap<String, String>();
         DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.ID, DeletedPasswords.ID);
         DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.GUID, DeletedPasswords.GUID);
         DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.TIME_DELETED, DeletedPasswords.TIME_DELETED);
-        System.loadLibrary("mozglue");
+
+        // We don't use .loadMozGlue because we're in a different process,
+        // and we just want to reuse code rather than use the loader lock etc.
+        GeckoLoader.doLoadLibrary("mozglue");
     }
 
     public PasswordsProvider() {
         super(LOG_TAG);
     }
 
     @Override
     protected String getDBName(){
--- a/mobile/android/base/mozglue/GeckoLoader.java.in
+++ b/mobile/android/base/mozglue/GeckoLoader.java.in
@@ -1,8 +1,9 @@
+#filter substitution
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.mozglue;
 
 import android.content.Context;
@@ -15,16 +16,19 @@ import java.io.File;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.text.NumberFormat;
 import java.util.Locale;
 
 public final class GeckoLoader {
     private static final String LOGTAG = "GeckoLoader";
 
+    // This matches AppConstants, but we're built earlier.
+    private static final String ANDROID_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@";
+
     private static volatile Intent sIntent;
     private static File sCacheFile;
     private static File sGREDir;
 
     private static final Object sLibLoadingLock = new Object();
     // Must hold sLibLoadingLock while accessing the following boolean variables.
     private static boolean sSQLiteLibsLoaded;
     private static boolean sNSSLibsLoaded;
@@ -254,25 +258,44 @@ public final class GeckoLoader {
             sNSSLibsLoaded = true;
         }
 
         loadMozGlue();
         loadLibsSetup(context);
         loadNSSLibsNative(apkName, false);
     }
 
+    public static void doLoadLibrary(final String lib) {
+        try {
+            System.loadLibrary(lib);
+        } catch (UnsatisfiedLinkError e) {
+            Log.wtf(LOGTAG, "Couldn't load " + lib + ". Trying /data/app-lib path.");
+            try {
+                System.load("/data/app-lib/" + ANDROID_PACKAGE_NAME + "/lib" + lib + ".so");
+            } catch (Throwable ee) {
+                try {
+                    Log.wtf(LOGTAG, "Couldn't load " + lib + ": " + ee + ". Trying /data/data path.");
+                    System.load("/data/data/" + ANDROID_PACKAGE_NAME + "/lib/lib" + lib + ".so");
+                } catch (Throwable eee) {
+                    Log.wtf(LOGTAG, "Failed every attempt to load " + lib + ". Giving up.");
+                    throw new RuntimeException("Unable to load " + lib, eee);
+                }
+            }
+        }
+    }
+
     public static void loadMozGlue() {
         synchronized (sLibLoadingLock) {
             if (sMozGlueLoaded) {
                 return;
             }
             sMozGlueLoaded = true;
         }
 
-        System.loadLibrary("mozglue");
+        doLoadLibrary("mozglue");
     }
 
     public static void loadGeckoLibs(Context context, String apkName) {
         loadLibsSetup(context);
         loadGeckoLibsNative(apkName);
     }
 
     private static void setupLocaleEnvironment() {
--- a/mobile/android/base/util/GeckoJarReader.java
+++ b/mobile/android/base/util/GeckoJarReader.java
@@ -11,17 +11,18 @@ import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.util.Log;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.net.URL;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.Stack;
 
 /* Reads out of a multiple level deep jar file such as
  *  jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png
  */
 public final class GeckoJarReader {
     private static final String LOGTAG = "GeckoJarReader";
 
@@ -42,16 +43,18 @@ public final class GeckoJarReader {
             // Load the initial jar file as a zip
             zip = getZipFile(jarUrls.pop());
             inputStream = getStream(zip, jarUrls, url);
             if (inputStream != null) {
                 bitmap = new BitmapDrawable(resources, inputStream);
             }
         } catch (IOException ex) {
             Log.e(LOGTAG, "Exception ", ex);
+        } catch (URISyntaxException ex) {
+            Log.e(LOGTAG, "Exception ", ex);
         } finally {
             if (inputStream != null) {
                 try {
                     inputStream.close();
                 } catch(IOException ex) {
                     Log.e(LOGTAG, "Error closing stream", ex);
                 }
             }
@@ -73,34 +76,36 @@ public final class GeckoJarReader {
             zip = getZipFile(jarUrls.pop());
             InputStream input = getStream(zip, jarUrls, url);
             if (input != null) {
                 reader = new BufferedReader(new InputStreamReader(input));
                 text = reader.readLine();
             }
         } catch (IOException ex) {
             Log.e(LOGTAG, "Exception ", ex);
+        } catch (URISyntaxException ex) {
+            Log.e(LOGTAG, "Exception ", ex);
         } finally {
             if (reader != null) {
                 try {
                     reader.close();
                 } catch(IOException ex) {
                     Log.e(LOGTAG, "Error closing reader", ex);
                 }
             }
             if (zip != null) {
                 zip.close();
             }
         }
 
         return text;
     }
 
-    private static NativeZip getZipFile(String url) throws IOException {
-        URL fileUrl = new URL(url);
+    private static NativeZip getZipFile(String url) throws IOException, URISyntaxException {
+        URI fileUrl = new URI(url);
         return new NativeZip(fileUrl.getPath());
     }
 
     @RobocopTarget
     public static InputStream getStream(String url) {
         Stack<String> jarUrls = parseUrl(url);
         try {
             NativeZip zip = getZipFile(jarUrls.pop());
--- a/mobile/android/base/util/JSONUtils.java
+++ b/mobile/android/base/util/JSONUtils.java
@@ -1,51 +1,22 @@
 /* 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/. */
 
 package org.mozilla.gecko.util;
 
+import java.util.UUID;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 
-import android.util.Log;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.UUID;
-
 public final class JSONUtils {
-    private static final String LOGTAG = "JSONUtils";
-
     private JSONUtils() {}
 
-    public static URL getURL(String name, JSONObject json) {
-        String url = json.optString(name, null);
-        if (url == null) {
-            return null;
-        }
-
-        try {
-            return new URL(url);
-        } catch (MalformedURLException e) {
-            Log.e(LOGTAG, "", new IllegalStateException(name + "=" + url, e));
-            return null;
-        }
-    }
-
-    public static void putURL(String name, URL url, JSONObject json) {
-        String urlString = url.toString();
-        try {
-            json.put(name, urlString);
-        } catch (JSONException e) {
-            throw new IllegalArgumentException(name + "=" + urlString, e);
-        }
-    }
-
     public static UUID getUUID(String name, JSONObject json) {
         String uuid = json.optString(name, null);
         return (uuid != null) ? UUID.fromString(uuid) : null;
     }
 
     public static void putUUID(String name, UUID uuid, JSONObject json) {
         String uuidString = uuid.toString();
         try {
--- a/mobile/android/base/webapp/InstallListener.java
+++ b/mobile/android/base/webapp/InstallListener.java
@@ -2,18 +2,16 @@
  * 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/. */
 
 package org.mozilla.gecko.webapp;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.GeckoThread;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
--- a/mobile/android/base/webapp/WebappImpl.java
+++ b/mobile/android/base/webapp/WebappImpl.java
@@ -2,55 +2,48 @@
  * 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/. */
 
 package org.mozilla.gecko.webapp;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
+import java.net.URI;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoThread;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
-import org.mozilla.gecko.webapp.ApkResources;
-import org.mozilla.gecko.webapp.InstallHelper;
 import org.mozilla.gecko.webapp.InstallHelper.InstallCallback;
 
-import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.Bitmap;
 import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.Display;
 import android.view.View;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 public class WebappImpl extends GeckoApp implements InstallCallback {
     private static final String LOGTAG = "GeckoWebappImpl";
 
-    private URL mOrigin;
+    private URI mOrigin;
     private TextView mTitlebarText = null;
     private View mTitlebar = null;
 
     private View mSplashscreen;
 
     private boolean mIsApk = true;
     private ApkResources mApkResources;
     private String mManifestUrl;
@@ -267,38 +260,38 @@ public class WebappImpl extends GeckoApp
                     // Don't show the titlebar for about:blank, which we load
                     // into the initial tab we create while waiting for the app
                     // to load.
                     if (urlString != null && urlString.equals("about:blank")) {
                         mTitlebar.setVisibility(View.GONE);
                         return;
                     }
 
-                    final URL url;
+                    final URI uri;
 
                     try {
-                        url = new URL(urlString);
-                    } catch (java.net.MalformedURLException ex) {
+                        uri = new URI(urlString);
+                    } catch (java.net.URISyntaxException ex) {
                         mTitlebarText.setText(urlString);
 
                         // If we can't parse the url, and its an app protocol hide
                         // the titlebar and return, otherwise show the titlebar
                         // and the full url
                         if (urlString != null && !urlString.startsWith("app://")) {
                             mTitlebar.setVisibility(View.VISIBLE);
                         } else {
                             mTitlebar.setVisibility(View.GONE);
                         }
                         return;
                     }
 
-                    if (mOrigin != null && mOrigin.getHost().equals(url.getHost())) {
+                    if (mOrigin != null && mOrigin.getHost().equals(uri.getHost())) {
                         mTitlebar.setVisibility(View.GONE);
                     } else {
-                        mTitlebarText.setText(url.getProtocol() + "://" + url.getHost());
+                        mTitlebarText.setText(uri.getScheme() + "://" + uri.getHost());
                         mTitlebar.setVisibility(View.VISIBLE);
                     }
                 }
                 break;
             case LOADED:
                 hideSplash();
                 break;
             case START:
@@ -345,31 +338,31 @@ public class WebappImpl extends GeckoApp
 
     @Override
     public void installErrored(InstallHelper installHelper, Exception exception) {
         Log.e(LOGTAG, "Install errored", exception);
     }
 
     private void setOrigin(String origin) {
         try {
-            mOrigin = new URL(origin);
-        } catch (java.net.MalformedURLException ex) {
+            mOrigin = new URI(origin);
+        } catch (java.net.URISyntaxException ex) {
             // If this isn't an app: URL, just settle for not having an origin.
             if (!origin.startsWith("app://")) {
                 return;
             }
 
             // If that failed fall back to the origin stored in the shortcut.
             if (!mIsApk) {
                 Log.i(LOGTAG, "Origin is app: URL; falling back to intent URL");
                 Uri data = getIntent().getData();
                 if (data != null) {
                     try {
-                        mOrigin = new URL(data.toString());
-                    } catch (java.net.MalformedURLException ex2) {
+                        mOrigin = new URI(data.toString());
+                    } catch (java.net.URISyntaxException ex2) {
                         Log.e(LOGTAG, "Unable to parse intent URL: ", ex);
                     }
                 }
             }
         }
     }
 
     public void launchWebapp(String origin) {
--- a/python/mozbuild/mozbuild/backend/android_eclipse.py
+++ b/python/mozbuild/mozbuild/backend/android_eclipse.py
@@ -233,16 +233,17 @@ class AndroidEclipseBackend(CommonBacken
         defines['IDE_PROJECT_LIBRARY_REFERENCES'] = '\n'.join(
             'android.library.reference.%s=%s' % (i + 1, ref)
             for i, ref in enumerate(sorted(data.included_projects)))
         if data.filtered_resources:
             filteredResources = self._Element_for_filtered_resources(data.filtered_resources)
             defines['IDE_PROJECT_FILTERED_RESOURCES'] = pretty_print(filteredResources).strip()
         else:
             defines['IDE_PROJECT_FILTERED_RESOURCES'] = ''
+        defines['ANDROID_TARGET_SDK'] = self.environment.substs['ANDROID_TARGET_SDK']
 
         copier = FileCopier()
         finder = FileFinder(template_directory)
         for input_filename, f in itertools.chain(finder.find('**'), finder.find('.**')):
             if input_filename == 'AndroidManifest.xml' and not data.is_library:
                 # Main projects supply their own manifests.
                 continue
             copier.add(input_filename, PreprocessedFile(
--- a/python/mozbuild/mozbuild/backend/templates/android_eclipse/project.properties
+++ b/python/mozbuild/mozbuild/backend/templates/android_eclipse/project.properties
@@ -4,11 +4,11 @@
 #
 # This file must be checked in Version Control Systems.
 #
 # To customize properties used by the Ant build system edit
 # "ant.properties", and override values to adapt the script to your
 # project structure.
 
 # Project target.
-target=android-16
+target=android-@ANDROID_TARGET_SDK@
 @IDE_PROJECT_LIBRARY_SETTING@
 @IDE_PROJECT_LIBRARY_REFERENCES@
--- a/python/mozbuild/mozbuild/test/backend/common.py
+++ b/python/mozbuild/mozbuild/test/backend/common.py
@@ -24,16 +24,23 @@ log_manager = LoggingManager()
 log_manager.add_terminal_logging()
 
 
 test_data_path = mozpath.abspath(mozpath.dirname(__file__))
 test_data_path = mozpath.join(test_data_path, 'data')
 
 
 CONFIGS = DefaultOnReadDict({
+    'android_eclipse': {
+        'defines': [],
+        'non_global_defines': [],
+        'substs': [
+            ('ANDROID_TARGET_SDK', '16'),
+        ],
+    },
     'stub0': {
         'defines': [
             ('MOZ_TRUE_1', '1'),
             ('MOZ_TRUE_2', '1'),
         ],
         'non_global_defines': [
             ('MOZ_NONGLOBAL_1', '1'),
             ('MOZ_NONGLOBAL_2', '1'),
--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -174,18 +174,18 @@ function summarizeObject(obj) {
     for (let k of Object.keys(obj)) {
       result[k] = summarizeObject(obj[k]);
     }
     return result;
   }
   return obj;
 }
 
-let worker = null;
 let Scheduler = {
+
   /**
    * |true| once we have sent at least one message to the worker.
    * This field is unaffected by resetting the worker.
    */
   launched: false,
 
   /**
    * |true| once shutdown has begun i.e. we should reject any
@@ -233,16 +233,35 @@ let Scheduler = {
   },
 
   /**
    * A timer used to automatically shut down the worker after some time.
    */
   resetTimer: null,
 
   /**
+   * The worker to which to send requests.
+   *
+   * If the worker has never been created or has been reset, this is a
+   * fresh worker, initialized with osfile_async_worker.js.
+   *
+   * @type {PromiseWorker}
+   */
+  get worker() {
+    if (!this._worker) {
+      // Either the worker has never been created or it has been reset
+      this._worker = new PromiseWorker(
+	"resource://gre/modules/osfile/osfile_async_worker.js", LOG);
+    }
+    return this._worker;
+  },
+
+  _worker: null,
+
+  /**
    * Prepare to kill the OS.File worker after a few seconds.
    */
   restartTimer: function(arg) {
     let delay;
     try {
       delay = Services.prefs.getIntPref("osfile.reset_worker_delay");
     } catch(e) {
       // Don't auto-shutdown if we don't have a delay preference set.
@@ -268,34 +287,41 @@ let Scheduler = {
    *   would not cause leaks. Otherwise, assume that the worker will be shutdown
    *   through some other mean.
    */
   kill: function({shutdown, reset}) {
     return Task.spawn(function*() {
 
       yield this.queue;
 
-      if (!this.launched || this.shutdown || !worker) {
+      // Enter critical section: no yield in this block
+      // (we want to make sure that we remain the only
+      // request in the queue).
+
+      if (!this.launched || this.shutdown || !this._worker) {
         // Nothing to kill
         this.shutdown = this.shutdown || shutdown;
-        worker = null;
+        this._worker = null;
         return null;
       }
 
       // Deactivate the queue, to ensure that no message is sent
       // to an obsolete worker (we reactivate it in the |finally|).
       let deferred = Promise.defer();
       this.queue = deferred.promise;
 
+
+      // Exit critical section
+
       let message = ["Meta_shutdown", [reset]];
 
       try {
         Scheduler.latestReceived = [];
         Scheduler.latestSent = [Date.now(), ...message];
-        let promise = worker.post(...message);
+        let promise = this._worker.post(...message);
 
         // Wait for result
         let resources;
         try {
           resources = (yield promise).ok;
 
           Scheduler.latestReceived = [Date.now(), message];
         } catch (ex) {
@@ -324,17 +350,17 @@ let Scheduler = {
               openedDirectoryIterators.join("\n");
           }
 
           LOG("WARNING: File descriptors leaks detected.\n" + msg);
         }
 
         // Make sure that we do not leave an invalid |worker| around.
         if (killed || shutdown) {
-          worker = null;
+          this._worker = null;
         }
 
         this.shutdown = shutdown;
 
         return resources;
 
       } finally {
         // Resume accepting messages. If we have set |shutdown| to |true|,
@@ -371,55 +397,56 @@ let Scheduler = {
    * must be clonable.
    * @return {Promise} A promise conveying the result/error caused by
    * calling |method| with arguments |args|.
    */
   post: function post(method, ...args) {
     if (this.shutdown) {
       LOG("OS.File is not available anymore. The following request has been rejected.",
         method, args);
-      return Promise.reject(new Error("OS.File has been shut down."));
-    }
-    if (!worker) {
-      // Either the worker has never been created or it has been reset
-      worker = new PromiseWorker(
-        "resource://gre/modules/osfile/osfile_async_worker.js", LOG);
+      return Promise.reject(new Error("OS.File has been shut down. Rejecting post to " + method));
     }
     let firstLaunch = !this.launched;
     this.launched = true;
 
     if (firstLaunch && SharedAll.Config.DEBUG) {
       // If we have delayed sending SET_DEBUG, do it now.
-      worker.post("SET_DEBUG", [true]);
+      this.worker.post("SET_DEBUG", [true]);
       Scheduler.Debugging.messagesSent++;
     }
 
     // By convention, the last argument of any message may be an |options| object.
     let options;
     let methodArgs = args[0];
     if (methodArgs) {
       options = methodArgs[methodArgs.length - 1];
     }
     Scheduler.Debugging.messagesQueued++;
-    return this.push(() => Task.spawn(function*() {
+    return this.push(Task.async(function*() {
+      if (this.shutdown) {
+	LOG("OS.File is not available anymore. The following request has been rejected.",
+	  method, args);
+	throw new Error("OS.File has been shut down. Rejecting request to " + method);
+      }
+
       // Update debugging information. As |args| may be quite
       // expensive, we only keep a shortened version of it.
       Scheduler.Debugging.latestReceived = null;
       Scheduler.Debugging.latestSent = [Date.now(), method, summarizeObject(methodArgs)];
 
       // Don't kill the worker just yet
       Scheduler.restartTimer();
 
 
       let data;
       let reply;
       let isError = false;
       try {
         try {
-          data = yield worker.post(method, ...args);
+          data = yield this.worker.post(method, ...args);
         } finally {
           Scheduler.Debugging.messagesReceived++;
         }
         reply = data;
       } catch (error) {
         reply = error;
         isError = true;
         if (error instanceof PromiseWorker.WorkerError) {
@@ -469,25 +496,26 @@ let Scheduler = {
       let durationMs = Math.max(0, data.durationMs);
       // Accumulate (or initialize) outExecutionDuration
       if (typeof options.outExecutionDuration == "number") {
         options.outExecutionDuration += durationMs;
       } else {
         options.outExecutionDuration = durationMs;
       }
       return data.ok;
-    }));
+    }.bind(this)));
   },
 
   /**
    * Post Telemetry statistics.
    *
    * This is only useful on first launch.
    */
   _updateTelemetry: function() {
+    let worker = this.worker;
     let workerTimeStamps = worker.workerTimeStamps;
     if (!workerTimeStamps) {
       // If the first call to OS.File results in an uncaught errors,
       // the timestamps are absent. As this case is a developer error,
       // let's not waste time attempting to extract telemetry from it.
       return;
     }
     let HISTOGRAM_LAUNCH = Services.telemetry.getHistogramById("OSFILE_WORKER_LAUNCH_MS");
@@ -1477,17 +1505,17 @@ AsyncShutdown.profileBeforeChange.addBlo
     } else {
       return Scheduler.queue;
     }
   },
   function getDetails() {
     let result = {
       launched: Scheduler.launched,
       shutdown: Scheduler.shutdown,
-      worker: !!worker,
+      worker: !!Scheduler._worker,
       pendingReset: !!Scheduler.resetTimer,
       latestSent: Scheduler.Debugging.latestSent,
       latestReceived: Scheduler.Debugging.latestReceived,
       messagesSent: Scheduler.Debugging.messagesSent,
       messagesReceived: Scheduler.Debugging.messagesReceived,
       messagesQueued: Scheduler.Debugging.messagesQueued,
       DEBUG: SharedAll.Config.DEBUG
     };
--- a/toolkit/components/osfile/modules/osfile_async_worker.js
+++ b/toolkit/components/osfile/modules/osfile_async_worker.js
@@ -76,17 +76,17 @@ const EXCEPTION_NAMES = {
    let durationMs;
    try {
      let method = data.fun;
      LOG("Calling method", method);
      result = Agent[method].apply(Agent, data.args);
      LOG("Method", method, "succeeded");
    } catch (ex) {
      exn = ex;
-     LOG("Error while calling agent method", exn, exn.stack || "");
+     LOG("Error while calling agent method", exn, exn.moduleStack || exn.stack || "");
    }
 
    if (start) {
      // Record duration
      durationMs = Date.now() - start;
      LOG("Method took", durationMs, "ms");
    }
 
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -151,58 +151,73 @@ this.PlacesUtils = {
 
   /**
    * Determines whether or not a ResultNode is a Bookmark folder.
    * @param   aNode
    *          A result node
    * @returns true if the node is a Bookmark folder, false otherwise
    */
   nodeIsFolder: function PU_nodeIsFolder(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     return (aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER ||
             aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT);
   },
 
   /**
    * Determines whether or not a ResultNode represents a bookmarked URI.
    * @param   aNode
    *          A result node
    * @returns true if the node represents a bookmarked URI, false otherwise
    */
   nodeIsBookmark: function PU_nodeIsBookmark(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI &&
            aNode.itemId != -1;
   },
 
   /**
    * Determines whether or not a ResultNode is a Bookmark separator.
    * @param   aNode
    *          A result node
    * @returns true if the node is a Bookmark separator, false otherwise
    */
   nodeIsSeparator: function PU_nodeIsSeparator(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR;
   },
 
   /**
    * Determines whether or not a ResultNode is a URL item.
    * @param   aNode
    *          A result node
    * @returns true if the node is a URL item, false otherwise
    */
   nodeIsURI: function PU_nodeIsURI(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI;
   },
 
   /**
    * Determines whether or not a ResultNode is a Query item.
    * @param   aNode
    *          A result node
    * @returns true if the node is a Query item, false otherwise
    */
   nodeIsQuery: function PU_nodeIsQuery(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY;
   },
 
   /**
    * Generator for a node's ancestors.
    * @param aNode
    *        A result node
    */
@@ -352,16 +367,19 @@ this.PlacesUtils = {
   /**
    * Determines if a node is read only (children cannot be inserted, sometimes
    * they cannot be removed depending on the circumstance)
    * @param   aNode
    *          A result node
    * @returns true if the node is readonly, false otherwise
    */
   nodeIsReadOnly: function PU_nodeIsReadOnly(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     let itemId = aNode.itemId;
     if (itemId != -1) {
       return this._readOnly.indexOf(itemId) != -1;
     }
 
     if (this.nodeIsQuery(aNode) &&
         asQuery(aNode).queryOptions.resultType !=
         Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_CONTENTS)
@@ -371,59 +389,71 @@ this.PlacesUtils = {
 
   /**
    * Determines whether or not a ResultNode is a host container.
    * @param   aNode
    *          A result node
    * @returns true if the node is a host container, false otherwise
    */
   nodeIsHost: function PU_nodeIsHost(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY &&
            aNode.parent &&
            asQuery(aNode.parent).queryOptions.resultType ==
              Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY;
   },
 
   /**
    * Determines whether or not a ResultNode is a day container.
    * @param   node
    *          A NavHistoryResultNode
    * @returns true if the node is a day container, false otherwise
    */
   nodeIsDay: function PU_nodeIsDay(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     var resultType;
     return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY &&
            aNode.parent &&
            ((resultType = asQuery(aNode.parent).queryOptions.resultType) ==
                Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY ||
              resultType == Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY);
   },
 
   /**
    * Determines whether or not a result-node is a tag container.
    * @param   aNode
    *          A result-node
    * @returns true if the node is a tag container, false otherwise
    */
   nodeIsTagQuery: function PU_nodeIsTagQuery(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY &&
            asQuery(aNode).queryOptions.resultType ==
              Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_CONTENTS;
   },
 
   /**
    * Determines whether or not a ResultNode is a container.
    * @param   aNode
    *          A result node
    * @returns true if the node is a container item, false otherwise
    */
   containerTypes: [Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER,
                    Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT,
                    Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY],
   nodeIsContainer: function PU_nodeIsContainer(aNode) {
+    if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
+      throw new Error("Invalid Places node");
+    }
     return this.containerTypes.indexOf(aNode.type) != -1;
   },
 
   /**
    * Determines whether or not a ResultNode is an history related container.
    * @param   node
    *          A result node
    * @returns true if the node is an history related container, false otherwise
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/unit/test_PlacesUtils_nodeIsXXX_invalidArg.js
@@ -0,0 +1,24 @@
+
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ let nodeIsMethods = [
+  "nodeIsFolder",
+  "nodeIsBookmark",
+  "nodeIsSeparator",
+  "nodeIsURI",
+  "nodeIsQuery",
+  "nodeIsReadOnly",
+  "nodeIsHost",
+  "nodeIsDay",
+  "nodeIsTagQuery",
+  "nodeIsContainer",
+  "nodeIsHistoryContainer",
+  "nodeIsQuery"
+ ];
+ for (let methodName of nodeIsMethods) {
+  Assert.throws(() => PlacesUtils[methodName](true), /Invalid Places node/);
+ }
+}
+
--- a/toolkit/components/places/tests/unit/xpcshell.ini
+++ b/toolkit/components/places/tests/unit/xpcshell.ini
@@ -112,16 +112,17 @@ fail-if = os == "android"
 skip-if = true
 [test_null_interfaces.js]
 [test_onItemChanged_tags.js]
 [test_pageGuid_bookmarkGuid.js]
 [test_frecency_observers.js]
 [test_placeURIs.js]
 [test_PlacesUtils_asyncGetBookmarkIds.js]
 [test_PlacesUtils_lazyobservers.js]
+[test_PlacesUtils_nodeIsXXX_invalidArg.js]
 [test_placesTxn.js]
 [test_preventive_maintenance.js]
 # Bug 676989: test hangs consistently on Android
 skip-if = os == "android"
 [test_preventive_maintenance_checkAndFixDatabase.js]
 # Bug 676989: test hangs consistently on Android
 skip-if = os == "android"
 [test_preventive_maintenance_runTasks.js]
--- a/toolkit/devtools/server/actors/highlighter.js
+++ b/toolkit/devtools/server/actors/highlighter.js
@@ -66,17 +66,21 @@ let HighlighterActor = protocol.ActorCla
 
   get conn() this._inspector && this._inspector.conn,
 
   /**
    * Can the host support the box model highlighter which requires a parent
    * XUL node to attach itself.
    */
   _supportsBoxModelHighlighter: function() {
-    return this._tabActor.browser && !!this._tabActor.browser.parentNode;
+    // Note that <browser>s on Fennec also have a XUL parentNode but the box
+    // model highlighter doesn't display correctly on Fennec (bug 993190)
+    return this._tabActor.browser &&
+           !!this._tabActor.browser.parentNode &&
+           Services.appinfo.ID !== "{aa3c5121-dab2-40e2-81ca-7ea25febc110}";
   },
 
   destroy: function() {
     protocol.Actor.prototype.destroy.call(this);
     if (this._boxModelHighlighter) {
       this._boxModelHighlighter.off("ready", this._highlighterReady);
       this._boxModelHighlighter.off("hide", this._highlighterHidden);
       this._boxModelHighlighter.destroy();
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -19,18 +19,19 @@ let addonManager = null;
 
 /**
  * This is a wrapper around amIAddonManager.mapURIToAddonID which always returns
  * false on B2G to avoid loading the add-on manager there and reports any
  * exceptions rather than throwing so that the caller doesn't have to worry
  * about them.
  */
 function mapURIToAddonID(uri, id) {
-  if (Services.appinfo.ID == B2G_ID)
+  if (Services.appinfo.ID == B2G_ID) {
     return false;
+  }
 
   if (!addonManager) {
     addonManager = Cc["@mozilla.org/addons/integration;1"].
                    getService(Ci.amIAddonManager);
   }
 
   try {
     return addonManager.mapURIToAddonID(uri, id);
@@ -4772,29 +4773,56 @@ function AddonThreadActor(aConnect, aHoo
 AddonThreadActor.prototype = Object.create(ThreadActor.prototype);
 
 update(AddonThreadActor.prototype, {
   constructor: AddonThreadActor,
 
   // A constant prefix that will be used to form the actor ID by the server.
   actorPrefix: "addonThread",
 
+  onAttach: function(aRequest) {
+    if (!this.attached) {
+      Services.obs.addObserver(this, "document-element-inserted", false);
+    }
+    return ThreadActor.prototype.onAttach.call(this, aRequest);
+  },
+
+  disconnect: function() {
+    if (this.attached) {
+      Services.obs.removeObserver(this, "document-element-inserted");
+    }
+    return ThreadActor.prototype.disconnect.call(this);
+  },
+
+  /**
+   * Called when a new DOM document element is created. Check if the DOM was
+   * laoded from an add-on and if so make the window a debuggee.
+   */
+  observe: function(aSubject, aTopic, aData) {
+    let id = {};
+    if (mapURIToAddonID(aSubject.documentURIObject, id) && id.value === this.addonID) {
+      this.dbg.addDebuggee(aSubject.defaultView);
+    }
+  },
+
   /**
    * Override the eligibility check for scripts and sources to make
    * sure every script and source with a URL is stored when debugging
    * add-ons.
    */
   _allowSource: function(aSourceURL) {
     // Hide eval scripts
-    if (!aSourceURL)
+    if (!aSourceURL) {
       return false;
+    }
 
     // XPIProvider.jsm evals some code in every add-on's bootstrap.js. Hide it
-    if (aSourceURL == "resource://gre/modules/addons/XPIProvider.jsm")
+    if (aSourceURL == "resource://gre/modules/addons/XPIProvider.jsm") {
       return false;
+    }
 
     return true;
   },
 
   /**
    * An object that will be used by ThreadActors to tailor their
    * behaviour depending on the debugging context being required (chrome,
    * addon or content). The methods that this object provides must
@@ -4831,44 +4859,72 @@ update(AddonThreadActor.prototype, {
   },
 
   /**
    * Checks if the provided global belongs to the debugged add-on.
    *
    * @param aGlobal Debugger.Object
    */
   _checkGlobal: function ADA_checkGlobal(aGlobal) {
+    let obj = null;
+    try {
+      obj = aGlobal.unsafeDereference();
+    }
+    catch (e) {
+      // Because of bug 991399 we sometimes get bad objects here. If we can't
+      // dereference them then they won't be useful to us
+      return false;
+    }
+
     try {
       // This will fail for non-Sandbox objects, hence the try-catch block.
-      let metadata = Cu.getSandboxMetadata(aGlobal.unsafeDereference());
-      if (metadata)
+      let metadata = Cu.getSandboxMetadata(obj);
+      if (metadata) {
         return metadata.addonID === this.addonID;
+      }
     } catch (e) {
     }
 
+    if (obj instanceof Ci.nsIDOMWindow) {
+      let id = {};
+      if (mapURIToAddonID(obj.document.documentURIObject, id)) {
+        return id.value === this.addonID;
+      }
+      return false;
+    }
+
     // Check the global for a __URI__ property and then try to map that to an
     // add-on
     let uridescriptor = aGlobal.getOwnPropertyDescriptor("__URI__");
-    if (uridescriptor && "value" in uridescriptor) {
+    if (uridescriptor && "value" in uridescriptor && uridescriptor.value) {
+      let uri;
       try {
-        let uri = Services.io.newURI(uridescriptor.value, null, null);
-        let id = {};
-        if (mapURIToAddonID(uri, id)) {
-          return id.value === this.addonID;
-        }
+        uri = Services.io.newURI(uridescriptor.value, null, null);
       }
       catch (e) {
-        DevToolsUtils.reportException("AddonThreadActor.prototype._checkGlobal", e);
+        DevToolsUtils.reportException("AddonThreadActor.prototype._checkGlobal",
+                                      new Error("Invalid URI: " + uridescriptor.value));
+        return false;
+      }
+
+      let id = {};
+      if (mapURIToAddonID(uri, id)) {
+        return id.value === this.addonID;
       }
     }
 
     return false;
   }
 });
 
+AddonThreadActor.prototype.requestTypes = Object.create(ThreadActor.prototype.requestTypes);
+update(AddonThreadActor.prototype.requestTypes, {
+  "attach": AddonThreadActor.prototype.onAttach
+});
+
 /**
  * Manages the sources for a thread. Handles source maps, locations in the
  * sources, etc for ThreadActors.
  */
 function ThreadSources(aThreadActor, aUseSourceMaps, aAllowPredicate,
                        aOnNewSource) {
   this._thread = aThreadActor;
   this._useSourceMaps = aUseSourceMaps;
--- a/toolkit/modules/ShortcutUtils.jsm
+++ b/toolkit/modules/ShortcutUtils.jsm
@@ -29,26 +29,26 @@ let ShortcutUtils = {
     * @param boolean aNoCloverLeaf
     *        Pass true to use a descriptive string instead of the cloverleaf symbol. (OS X only)
     * @return string
     *         A prettified and properly separated modifier keys string.
     */
   prettifyShortcut: function(aElemKey, aNoCloverLeaf) {
     let elemString = "";
     let elemMod = aElemKey.getAttribute("modifiers");
+    let haveCloverLeaf = false;
 
     if (elemMod.match("accel")) {
       if (Services.appinfo.OS == "Darwin") {
         // XXX bug 779642 Use "Cmd-" literal vs. cloverleaf meta-key until
         // Orion adds variable height lines.
         if (aNoCloverLeaf) {
           elemString += "Cmd-";
         } else {
-          elemString += PlatformKeys.GetStringFromName("VK_META") +
-            PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+          haveCloverLeaf = true;
         }
       } else {
         elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
           PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
       }
     }
     if (elemMod.match("access")) {
       if (Services.appinfo.OS == "Darwin") {
@@ -75,16 +75,21 @@ let ShortcutUtils = {
       elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
         PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
     }
     if (elemMod.match("meta")) {
       elemString += PlatformKeys.GetStringFromName("VK_META") +
         PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
     }
 
+    if (haveCloverLeaf) {
+      elemString += PlatformKeys.GetStringFromName("VK_META") +
+        PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+    }
+
     let key;
     let keyCode = aElemKey.getAttribute("keycode");
     if (keyCode) {
       try {
         // Some keys might not exist in the locale file, which will throw:
         key = Keys.GetStringFromName(keyCode.toUpperCase());
       } catch (ex) {
         Cu.reportError("Error finding " + keyCode + ": " + ex);
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -13,16 +13,20 @@ const Cr = Components.results;
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
                                   "resource://gre/modules/UpdateChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OS",
+                                  "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org"
 const KEY_PROFILEDIR                  = "ProfD";
 const KEY_APPDIR                      = "XCurProcD";
 const FILE_BLOCKLIST                  = "blocklist.xml";
 const PREF_BLOCKLIST_LASTUPDATETIME   = "app.update.lastUpdateTime.blocklist-background-update-timer";
 const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
 const PREF_BLOCKLIST_ITEM_URL         = "extensions.blocklist.itemURL";
@@ -562,47 +566,47 @@ Blocklist.prototype = {
     request.send(null);
 
     // When the blocklist loads we need to compare it to the current copy so
     // make sure we have loaded it.
     if (!this._addonEntries)
       this._loadBlocklist();
   },
 
-  onXMLLoad: function Blocklist_onXMLLoad(aEvent) {
-    var request = aEvent.target;
+  onXMLLoad: Task.async(function* (aEvent) {
+    let request = aEvent.target;
     try {
       gCertUtils.checkCert(request.channel);
     }
     catch (e) {
       LOG("Blocklist::onXMLLoad: " + e);
       return;
     }
-    var responseXML = request.responseXML;
+    let responseXML = request.responseXML;
     if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
         (request.status != 200 && request.status != 0)) {
       LOG("Blocklist::onXMLLoad: there was an error during load");
       return;
     }
-    var blocklistFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
-    if (blocklistFile.exists())
-      blocklistFile.remove(false);
-    var fos = FileUtils.openSafeFileOutputStream(blocklistFile);
-    fos.write(request.responseText, request.responseText.length);
-    FileUtils.closeSafeFileOutputStream(fos);
 
     var oldAddonEntries = this._addonEntries;
     var oldPluginEntries = this._pluginEntries;
     this._addonEntries = [];
     this._pluginEntries = [];
-    this._loadBlocklistFromFile(FileUtils.getFile(KEY_PROFILEDIR,
-                                                  [FILE_BLOCKLIST]));
+
+    this._loadBlocklistFromString(request.responseText);
+    this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
 
-    this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
-  },
+    try {
+      let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
+      yield OS.File.writeAtomic(path, request.responseText, {tmpPath: path + ".tmp"});
+    } catch (e) {
+      LOG("Blocklist::onXMLLoad: " + e);
+    }
+  }),
 
   onXMLError: function Blocklist_onXMLError(aEvent) {
     try {
       var request = aEvent.target;
       // the following may throw (e.g. a local file or timeout)
       var status = request.status;
     }
     catch (e) {
@@ -699,27 +703,56 @@ Blocklist.prototype = {
 
   _loadBlocklistFromFile: function Blocklist_loadBlocklistFromFile(file) {
     if (!gBlocklistEnabled) {
       LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
       return;
     }
 
     if (!file.exists()) {
-      LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist");
+      LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist " + file.path);
       return;
     }
 
-    var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
-                               .createInstance(Components.interfaces.nsIFileInputStream);
-    fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+    let text = "";
+    let fstream = null;
+    let cstream = null;
+
+    try {
+      fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
+                          .createInstance(Components.interfaces.nsIFileInputStream);
+      cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
+                          .createInstance(Components.interfaces.nsIConverterInputStream);
+
+      fstream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+      cstream.init(fstream, "UTF-8", 0, 0);
+
+      let (str = {}) {
+        let read = 0;
+
+        do {
+          read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
+          text += str.value;
+        } while (read != 0);
+      }
+    } catch (e) {
+      LOG("Blocklist::_loadBlocklistFromFile: Failed to load XML file " + e);
+    } finally {
+      cstream.close();
+      fstream.close();
+    }
+
+    text && this._loadBlocklistFromString(text);
+  },
+
+  _loadBlocklistFromString : function Blocklist_loadBlocklistFromString(text) {
     try {
       var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
                    createInstance(Ci.nsIDOMParser);
-      var doc = parser.parseFromStream(fileStream, "UTF-8", file.fileSize, "text/xml");
+      var doc = parser.parseFromString(text, "text/xml");
       if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
         LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
             "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
             "Received: " + doc.documentElement.namespaceURI);
         return;
       }
 
       var childNodes = doc.documentElement.childNodes;
@@ -741,17 +774,16 @@ Blocklist.prototype = {
                                        null);
         }
       }
     }
     catch (e) {
       LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
       return;
     }
-    fileStream.close();
   },
 
   _processItemNodes: function Blocklist_processItemNodes(itemNodes, prefix, handler) {
     var result = [];
     var itemName = prefix + "Item";
     for (var i = 0; i < itemNodes.length; ++i) {
       var blocklistElement = itemNodes.item(i);
       if (!(blocklistElement instanceof Ci.nsIDOMElement) ||