merge fx-team to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 03 Aug 2015 13:53:35 +0200
changeset 287392 20081c51fb3fbaaad25b6b50da738a0efb0f1eeb
parent 287365 3cd0d76851a3f093ead4b3c6ea20b133ec1c7000 (current diff)
parent 287391 88ecc638d778ef1a1e147fb025a0df03c48d31da (diff)
child 287478 b9f166a815b279ef4ed22717cf61536a346431cc
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge fx-team to mozilla-central a=merge
browser/themes/linux/devtools/canvasdebugger.css
browser/themes/linux/devtools/debugger.css
browser/themes/linux/devtools/performance.css
browser/themes/linux/devtools/scratchpad.css
browser/themes/linux/devtools/shadereditor.css
browser/themes/linux/devtools/webaudioeditor.css
browser/themes/osx/devtools/canvasdebugger.css
browser/themes/osx/devtools/debugger.css
browser/themes/osx/devtools/performance.css
browser/themes/osx/devtools/scratchpad.css
browser/themes/osx/devtools/shadereditor.css
browser/themes/osx/devtools/webaudioeditor.css
browser/themes/shared/devtools/canvasdebugger.inc.css
browser/themes/shared/devtools/debugger.inc.css
browser/themes/shared/devtools/performance.inc.css
browser/themes/shared/devtools/scratchpad.inc.css
browser/themes/shared/devtools/shadereditor.inc.css
browser/themes/shared/devtools/webaudioeditor.inc.css
browser/themes/windows/devtools/canvasdebugger.css
browser/themes/windows/devtools/debugger.css
browser/themes/windows/devtools/performance.css
browser/themes/windows/devtools/scratchpad.css
browser/themes/windows/devtools/shadereditor.css
browser/themes/windows/devtools/webaudioeditor.css
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -66,18 +66,18 @@ pref("extensions.hotfix.id", "firefox-ho
 pref("extensions.hotfix.cert.checkAttributes", true);
 pref("extensions.hotfix.certs.1.sha1Fingerprint", "91:53:98:0C:C1:86:DF:47:8F:35:22:9E:11:C9:A7:31:04:49:A1:AA");
 pref("extensions.hotfix.certs.2.sha1Fingerprint", "39:E7:2B:7A:5B:CF:37:78:F9:5D:4A:E0:53:2D:2F:3D:68:53:C5:60");
 
 // Disable add-ons that are not installed by the user in all scopes by default.
 // See the SCOPE constants in AddonManager.jsm for values to use here.
 pref("extensions.autoDisableScopes", 15);
 
-// Don't require signed add-ons by default
-pref("xpinstall.signatures.required", false);
+// Require signed add-ons by default
+pref("xpinstall.signatures.required", true);
 pref("xpinstall.signatures.devInfoURL", "https://wiki.mozilla.org/Addons/Extension_Signing");
 
 // Dictionary download preference
 pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/dictionaries/");
 
 // At startup, should we check to see if the installation
 // date is older than some threshold
 pref("app.update.checkInstallTime", true);
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -962,18 +962,18 @@ file, You can obtain one at http://mozil
 
         // Drag only if the entire value is selected and it's a valid URI.
         var isFullSelection = this.selectionStart == 0 &&
                               this.selectionEnd == this.textLength;
         if (!isFullSelection ||
             this.getAttribute("pageproxystate") != "valid")
           return;
 
-        var urlString = content.location.href;
-        var title = content.document.title || urlString;
+        var urlString = gBrowser.selectedBrowser.currentURI.spec;
+        var title = gBrowser.selectedBrowser.contentTitle || urlString;
         var htmlString = "<a href=\"" + urlString + "\">" + urlString + "</a>";
 
         var dt = event.dataTransfer;
         dt.setData("text/x-moz-url", urlString + "\n" + title);
         dt.setData("text/unicode", urlString);
         dt.setData("text/html", htmlString);
 
         dt.effectAllowed = "copyLink";
--- a/browser/components/loop/test/desktop-local/conversationViews_test.js
+++ b/browser/components/loop/test/desktop-local/conversationViews_test.js
@@ -97,18 +97,23 @@ describe("loop.conversationViews", funct
     loop.shared.mixins.setRootObject(fakeWindow);
 
     conversationStore = new loop.store.ConversationStore(dispatcher, {
       client: {},
       mozLoop: fakeMozLoop,
       sdkDriver: {}
     });
 
+    var textChatStore = new loop.store.TextChatStore(dispatcher, {
+      sdkDriver: {}
+    });
+
     loop.store.StoreMixin.register({
-      conversationStore: conversationStore
+      conversationStore: conversationStore,
+      textChatStore: textChatStore
     });
   });
 
   afterEach(function() {
     loop.shared.mixins.setRootObject(window);
     view = undefined;
     delete navigator.mozLoop;
     sandbox.restore();
--- a/browser/components/loop/test/shared/mixins_test.js
+++ b/browser/components/loop/test/shared/mixins_test.js
@@ -222,19 +222,19 @@ describe("loop.shared.mixins", function(
       };
 
       sharedMixins.setRootObject(rootObject);
 
       view = TestUtils.renderIntoDocument(React.createElement(TestComp));
 
       sandbox.stub(view, "getDOMNode").returns({
         querySelector: function(classSelector) {
-          if (classSelector.includes("local")) {
+          if (classSelector.indexOf("local") > -1) {
             return localElement;
-          } else if (classSelector.includes("screen")) {
+          } else if (classSelector.indexOf("screen") > -1) {
             return screenShareElement;
           }
           return remoteElement;
         }
       });
     });
 
     afterEach(function() {
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -84,43 +84,43 @@
 </hbox>
 
 <!-- Tracking -->
 <groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" align="start">
   <caption><label>&tracking.label;</label></caption>
   <vbox>
     <hbox align="center">
       <checkbox id="privacyDoNotTrackCheckbox"
-                label="&dntTrackingNotOkay3.label;"
-                accesskey="&dntTrackingNotOkay3.accesskey;"
+                label="&dntTrackingNotOkay4.label;"
+                accesskey="&dntTrackingNotOkay4.accesskey;"
                 preference="privacy.donottrackheader.enabled"/>
       <label id="doNotTrackInfo"
              class="text-link"
              href="https://www.mozilla.org/dnt">
         &doNotTrackInfo.label;
       </label>
     </hbox>
   </vbox>
   <vbox id="trackingprotectionbox" hidden="true">
     <hbox align="center">
       <checkbox id="trackingProtection"
                 preference="privacy.trackingprotection.enabled"
-                accesskey="&trackingProtection3.accesskey;"
-                label="&trackingProtection3.label;" />
+                accesskey="&trackingProtection4.accesskey;"
+                label="&trackingProtection4.label;" />
       <label id="trackingProtectionLearnMore"
              class="text-link"
              value="&trackingProtectionLearnMore.label;"/>
     </hbox>
   </vbox>
   <vbox id="trackingprotectionpbmbox">
     <hbox align="center">
       <checkbox id="trackingProtectionPBM"
                 preference="privacy.trackingprotection.pbmode.enabled"
-                accesskey="&trackingProtectionPBM3.accesskey;"
-                label="&trackingProtectionPBM3.label;" />
+                accesskey="&trackingProtectionPBM4.accesskey;"
+                label="&trackingProtectionPBM4.label;" />
       <label id="trackingProtectionPBMLearnMore"
              class="text-link"
              value="&trackingProtectionPBMLearnMore.label;"/>
     </hbox>
   </vbox>
 </groupbox>
 
 <!-- History -->
--- a/browser/components/preferences/privacy.xul
+++ b/browser/components/preferences/privacy.xul
@@ -91,33 +91,33 @@
 
     <!-- Tracking -->
     <groupbox id="trackingGroup" align="start">
       <caption label="&tracking.label;"/>
       <vbox id="trackingprotectionbox" hidden="true">
         <hbox align="center">
           <checkbox id="trackingProtection"
                     preference="privacy.trackingprotection.enabled"
-                    accesskey="&trackingProtection3.accesskey;"
-                    label="&trackingProtection3.label;" />
+                    accesskey="&trackingProtection4.accesskey;"
+                    label="&trackingProtection4.label;" />
           <image id="trackingProtectionImage"
                  src="chrome://browser/skin/bad-content-blocked-16.png"/>
         </hbox>
         <hbox align="center"
               class="indent">
           <label id="trackingProtectionLearnMore"
                  class="text-link"
                  value="&trackingProtectionLearnMore.label;"/>
         </hbox>
       </vbox>
       <vbox>
         <hbox align="center">
           <checkbox id="privacyDoNotTrackCheckbox"
-                    label="&dntTrackingNotOkay3.label;"
-                    accesskey="&dntTrackingNotOkay3.accesskey;"
+                    label="&dntTrackingNotOkay4.label;"
+                    accesskey="&dntTrackingNotOkay4.accesskey;"
                     preference="privacy.donottrackheader.enabled"/>
         </hbox>
         <hbox align="center"
               class="indent">
           <label id="doNotTrackInfo"
                  class="text-link"
                  href="https://www.mozilla.org/dnt">
             &doNotTrackInfo.label;
--- a/browser/devtools/performance/modules/widgets/marker-details.js
+++ b/browser/devtools/performance/modules/widgets/marker-details.js
@@ -97,17 +97,17 @@ MarkerDetails.prototype = {
   },
 
   /**
    * Handles click in the marker details view. Based on the target,
    * can handle different actions -- only supporting view source links
    * for the moment.
    */
   _onClick: function (e) {
-    let data = findActionFromEvent(e.target);
+    let data = findActionFromEvent(e.target, this._parent);
     if (!data) {
       return;
     }
 
     if (data.action === "view-source") {
       this.emit("view-source", data.url, data.line);
     }
   },
@@ -116,17 +116,17 @@ MarkerDetails.prototype = {
    * Handles the "mouseup" event on the marker details view splitter.
    */
   _onSplitterMouseUp: function() {
     this.emit("resize");
   }
 };
 
 /**
- * Take an element from an event `target`, and asend through
+ * Take an element from an event `target`, and ascend through
  * the DOM, looking for an element with a `data-action` attribute. Return
  * the parsed `data-action` value found, or null if none found before
  * reaching the parent `container`.
  *
  * @param {Element} target
  * @param {Element} container
  * @return {?object}
  */
--- a/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
@@ -1,22 +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/. -->
 
 <!ENTITY tracking.label                 "Tracking">
 
-<!ENTITY dntTrackingNotOkay3.label     "Request that sites not track you.">
-<!ENTITY dntTrackingNotOkay3.accesskey "n">
+<!ENTITY dntTrackingNotOkay4.label     "Request that sites not track you">
+<!ENTITY dntTrackingNotOkay4.accesskey "n">
 <!ENTITY doNotTrackInfo.label          "Learn More">
-<!ENTITY trackingProtection3.label     "Stop sites from tracking you.">
-<!ENTITY trackingProtection3.accesskey "m">
+<!ENTITY trackingProtection4.label     "Stop sites from tracking you">
+<!ENTITY trackingProtection4.accesskey "m">
 <!ENTITY trackingProtectionLearnMore.label "Learn more">
-<!ENTITY trackingProtectionPBM3.label         "Stop sites from tracking you in Private Windows.">
-<!ENTITY trackingProtectionPBM3.accesskey     "y">
+<!ENTITY trackingProtectionPBM4.label         "Stop sites from tracking you in Private Windows">
+<!ENTITY trackingProtectionPBM4.accesskey     "y">
 <!ENTITY trackingProtectionPBMLearnMore.label "Learn more">
 
 <!ENTITY  history.label                 "History">
 
 <!ENTITY  locationBar.label             "Location Bar">
 
 <!ENTITY  locbar.suggest.label          "When using the location bar, suggest:">
 <!ENTITY  locbar.history.label          "History">
deleted file mode 100644
--- a/browser/themes/linux/devtools/canvasdebugger.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/canvasdebugger.inc.css
deleted file mode 100644
--- a/browser/themes/linux/devtools/debugger.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/debugger.inc.css
deleted file mode 100644
--- a/browser/themes/linux/devtools/performance.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/performance.inc.css
deleted file mode 100644
--- a/browser/themes/linux/devtools/scratchpad.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/scratchpad.inc.css
\ No newline at end of file
deleted file mode 100644
--- a/browser/themes/linux/devtools/shadereditor.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/shadereditor.inc.css
deleted file mode 100644
--- a/browser/themes/linux/devtools/webaudioeditor.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/webaudioeditor.inc.css
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -330,29 +330,29 @@ browser.jar:
   skin/classic/browser/devtools/editor-error.png       (../shared/devtools/images/editor-error.png)
   skin/classic/browser/devtools/editor-breakpoint.png  (../shared/devtools/images/editor-breakpoint.png)
   skin/classic/browser/devtools/editor-debug-location.png (../shared/devtools/images/editor-debug-location.png)
   skin/classic/browser/devtools/editor-debug-location@2x.png (../shared/devtools/images/editor-debug-location@2x.png)
   skin/classic/browser/devtools/breadcrumbs-divider@2x.png      (../shared/devtools/images/breadcrumbs-divider@2x.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton.png    (../shared/devtools/images/breadcrumbs-scrollbutton.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
   skin/classic/browser/devtools/animationinspector.css          (../shared/devtools/animationinspector.css)
-* skin/classic/browser/devtools/canvasdebugger.css    (devtools/canvasdebugger.css)
-* skin/classic/browser/devtools/debugger.css          (devtools/debugger.css)
+* skin/classic/browser/devtools/canvasdebugger.css    (../shared/devtools/canvasdebugger.css)
+  skin/classic/browser/devtools/debugger.css          (../shared/devtools/debugger.css)
   skin/classic/browser/devtools/eyedropper.css        (../shared/devtools/eyedropper.css)
 * skin/classic/browser/devtools/netmonitor.css        (devtools/netmonitor.css)
-* skin/classic/browser/devtools/performance.css       (devtools/performance.css)
+  skin/classic/browser/devtools/performance.css       (../shared/devtools/performance.css)
   skin/classic/browser/devtools/promisedebugger.css   (../shared/devtools/promisedebugger.css)
   skin/classic/browser/devtools/timeline-filter.svg   (../shared/devtools/images/timeline-filter.svg)
-* skin/classic/browser/devtools/scratchpad.css        (devtools/scratchpad.css)
-* skin/classic/browser/devtools/shadereditor.css      (devtools/shadereditor.css)
+* skin/classic/browser/devtools/scratchpad.css        (../shared/devtools/scratchpad.css)
+  skin/classic/browser/devtools/shadereditor.css      (../shared/devtools/shadereditor.css)
 * skin/classic/browser/devtools/splitview.css         (../shared/devtools/splitview.css)
   skin/classic/browser/devtools/styleeditor.css       (../shared/devtools/styleeditor.css)
   skin/classic/browser/devtools/storage.css           (../shared/devtools/storage.css)
-* skin/classic/browser/devtools/webaudioeditor.css    (devtools/webaudioeditor.css)
+  skin/classic/browser/devtools/webaudioeditor.css    (../shared/devtools/webaudioeditor.css)
   skin/classic/browser/devtools/magnifying-glass.png        (../shared/devtools/images/magnifying-glass.png)
   skin/classic/browser/devtools/magnifying-glass@2x.png     (../shared/devtools/images/magnifying-glass@2x.png)
   skin/classic/browser/devtools/magnifying-glass-light.png  (../shared/devtools/images/magnifying-glass-light.png)
   skin/classic/browser/devtools/magnifying-glass-light@2x.png (../shared/devtools/images/magnifying-glass-light@2x.png)
   skin/classic/browser/devtools/itemToggle.png         (../shared/devtools/images/itemToggle.png)
   skin/classic/browser/devtools/itemToggle@2x.png      (../shared/devtools/images/itemToggle@2x.png)
   skin/classic/browser/devtools/itemArrow-dark-rtl.svg (../shared/devtools/images/itemArrow-dark-rtl.svg)
   skin/classic/browser/devtools/itemArrow-dark-ltr.svg (../shared/devtools/images/itemArrow-dark-ltr.svg)
deleted file mode 100644
--- a/browser/themes/osx/devtools/canvasdebugger.css
+++ /dev/null
@@ -1,6 +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/. */
-
-%include ../shared.inc
-%include ../../shared/devtools/canvasdebugger.inc.css
deleted file mode 100644
--- a/browser/themes/osx/devtools/debugger.css
+++ /dev/null
@@ -1,6 +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/. */
-
-%include ../shared.inc
-%include ../../shared/devtools/debugger.inc.css
deleted file mode 100644
--- a/browser/themes/osx/devtools/performance.css
+++ /dev/null
@@ -1,6 +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/. */
-
-%include ../shared.inc
-%include ../../shared/devtools/performance.inc.css
deleted file mode 100644
--- a/browser/themes/osx/devtools/scratchpad.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/scratchpad.inc.css
\ No newline at end of file
deleted file mode 100644
--- a/browser/themes/osx/devtools/shadereditor.css
+++ /dev/null
@@ -1,6 +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/. */
-
-%include ../shared.inc
-%include ../../shared/devtools/shadereditor.inc.css
deleted file mode 100644
--- a/browser/themes/osx/devtools/webaudioeditor.css
+++ /dev/null
@@ -1,6 +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/. */
-
-%include ../shared.inc
-%include ../../shared/devtools/webaudioeditor.inc.css
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -432,29 +432,29 @@ browser.jar:
   skin/classic/browser/devtools/editor-debug-location@2x.png    (../shared/devtools/images/editor-debug-location@2x.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.svg                  (../shared/devtools/images/webconsole.svg)
   skin/classic/browser/devtools/breadcrumbs-divider@2x.png      (../shared/devtools/images/breadcrumbs-divider@2x.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton.png    (../shared/devtools/images/breadcrumbs-scrollbutton.png)
   skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
   skin/classic/browser/devtools/animationinspector.css          (../shared/devtools/animationinspector.css)
-* skin/classic/browser/devtools/canvasdebugger.css          (devtools/canvasdebugger.css)
-* skin/classic/browser/devtools/debugger.css                (devtools/debugger.css)
+* skin/classic/browser/devtools/canvasdebugger.css          (../shared/devtools/canvasdebugger.css)
+  skin/classic/browser/devtools/debugger.css                (../shared/devtools/debugger.css)
   skin/classic/browser/devtools/eyedropper.css              (../shared/devtools/eyedropper.css)
 * skin/classic/browser/devtools/netmonitor.css              (devtools/netmonitor.css)
-* skin/classic/browser/devtools/performance.css             (devtools/performance.css)
+  skin/classic/browser/devtools/performance.css             (../shared/devtools/performance.css)
   skin/classic/browser/devtools/promisedebugger.css         (../shared/devtools/promisedebugger.css)
   skin/classic/browser/devtools/timeline-filter.svg         (../shared/devtools/images/timeline-filter.svg)
-* skin/classic/browser/devtools/scratchpad.css              (devtools/scratchpad.css)
-* skin/classic/browser/devtools/shadereditor.css            (devtools/shadereditor.css)
+* skin/classic/browser/devtools/scratchpad.css              (../shared/devtools/scratchpad.css)
+  skin/classic/browser/devtools/shadereditor.css            (../shared/devtools/shadereditor.css)
 * skin/classic/browser/devtools/splitview.css               (../shared/devtools/splitview.css)
   skin/classic/browser/devtools/styleeditor.css             (../shared/devtools/styleeditor.css)
   skin/classic/browser/devtools/storage.css                 (../shared/devtools/storage.css)
-* skin/classic/browser/devtools/webaudioeditor.css          (devtools/webaudioeditor.css)
+  skin/classic/browser/devtools/webaudioeditor.css          (../shared/devtools/webaudioeditor.css)
   skin/classic/browser/devtools/magnifying-glass.png        (../shared/devtools/images/magnifying-glass.png)
   skin/classic/browser/devtools/magnifying-glass@2x.png     (../shared/devtools/images/magnifying-glass@2x.png)
   skin/classic/browser/devtools/magnifying-glass-light.png  (../shared/devtools/images/magnifying-glass-light.png)
   skin/classic/browser/devtools/magnifying-glass-light@2x.png (../shared/devtools/images/magnifying-glass-light@2x.png)
   skin/classic/browser/devtools/itemToggle.png              (../shared/devtools/images/itemToggle.png)
   skin/classic/browser/devtools/itemToggle@2x.png           (../shared/devtools/images/itemToggle@2x.png)
   skin/classic/browser/devtools/itemArrow-dark-rtl.svg      (../shared/devtools/images/itemArrow-dark-rtl.svg)
   skin/classic/browser/devtools/itemArrow-dark-ltr.svg      (../shared/devtools/images/itemArrow-dark-ltr.svg)
--- a/browser/themes/shared/devedition.inc.css
+++ b/browser/themes/shared/devedition.inc.css
@@ -48,20 +48,22 @@
   --toolbarbutton-combined-boxshadow: none;
   --toolbarbutton-combined-backgroundimage: linear-gradient(#5F6670 0, #5F6670 18px);
 
   /* Url and search bars */
   --url-and-searchbar-background-color: #171B1F;
   --url-and-searchbar-color: #fff;
   --urlbar-dropmarker-url: url("chrome://browser/skin/devedition/urlbar-history-dropmarker.svg");
   --urlbar-dropmarker-region: rect(0px, 11px, 14px, 0px);
-  --urlbar-dropmarker-active-region: rect(0px, 22px, 14px, 11px);
+  --urlbar-dropmarker-hover-region: rect(0, 22px, 14px, 11px);
+  --urlbar-dropmarker-active-region: rect(0px, 33px, 14px, 22px);
   --urlbar-dropmarker-2x-url: url("chrome://browser/skin/devedition/urlbar-history-dropmarker.svg");
   --urlbar-dropmarker-2x-region: rect(0px, 11px, 14px, 0px);
-  --urlbar-dropmarker-active-2x-region: rect(0px, 22px, 14px, 11px);
+  --urlbar-dropmarker-hover-2x-region: rect(0, 22px, 14px, 11px);
+  --urlbar-dropmarker-active-2x-region: rect(0px, 33px, 14px, 22px);
   --search-button-image: url("chrome://browser/skin/devedition/search.svg#search-icon-inverted");
 }
 
 :root[devtoolstheme="dark"] #identity-box {
   --identity-box-border-color: #5F6670;
   --identity-box-chrome-color: #46afe3;
   --identity-box-verified-background-color: transparent;
   --identity-box-selected-background-color: rgba(231,230,230,.2);
rename from browser/themes/shared/devtools/canvasdebugger.inc.css
rename to browser/themes/shared/devtools/canvasdebugger.css
rename from browser/themes/shared/devtools/debugger.inc.css
rename to browser/themes/shared/devtools/debugger.css
rename from browser/themes/shared/devtools/performance.inc.css
rename to browser/themes/shared/devtools/performance.css
rename from browser/themes/shared/devtools/scratchpad.inc.css
rename to browser/themes/shared/devtools/scratchpad.css
rename from browser/themes/shared/devtools/shadereditor.inc.css
rename to browser/themes/shared/devtools/shadereditor.css
rename from browser/themes/shared/devtools/webaudioeditor.inc.css
rename to browser/themes/shared/devtools/webaudioeditor.css
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -195,16 +195,17 @@ treecol {
   -moz-margin-end: 8px !important;
 }
 
 /* Privacy pane */
 
 #doNotTrackInfo,
 #trackingProtectionPBMLearnMore,
 #trackingProtectionLearnMore {
+  -moz-margin-start: 1.5em !important;
   margin-top: 0;
 }
 
 /* Collapse the non-active vboxes in decks to use only the height the
    active vbox needs */
 #historyPane:not([selectedIndex="1"]) > #historyDontRememberPane,
 #historyPane:not([selectedIndex="2"]) > #historyCustomPane,
 #weavePrefsDeck:not([selectedIndex="1"]) > #hasAccount,
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -767,19 +767,19 @@ toolbarbutton[constrain-size="true"][cui
 }
 
 #nav-bar toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
   /* XXXgijs box models strike again: this is 16px + 2 * 7px padding + 2 * 1px border (from the rules above) */
   width: 32px;
 }
 
 #nav-bar .toolbarbutton-1[type=panel] > .toolbarbutton-icon,
-#nav-bar .toolbarbutton-1[type=panel] > .toolbarbutton-badge-container,
-#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) > .toolbarbutton-icon,
-#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) > .toolbarbutton-badge-container,
+#nav-bar .toolbarbutton-1[type=panel] > .toolbarbutton-badge-stack,
+#nav-bar .toolbarbutton-1[type=menu]:not(#PanelUI-menu-button):not(#back-button):not(#forward-button) > .toolbarbutton-icon,
+#nav-bar .toolbarbutton-1[type=menu]:not(#PanelUI-menu-button) > .toolbarbutton-badge-stack,
 #nav-bar .toolbarbutton-1[type=menu] > .toolbarbutton-text /* hack for add-ons that forcefully display the label */ {
   -moz-padding-end: 17px;
 }
 
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker {
   -moz-margin-start: -15px;
 }
 
deleted file mode 100644
--- a/browser/themes/windows/devtools/canvasdebugger.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/canvasdebugger.inc.css
deleted file mode 100644
--- a/browser/themes/windows/devtools/debugger.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/debugger.inc.css
deleted file mode 100644
--- a/browser/themes/windows/devtools/performance.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/performance.inc.css
deleted file mode 100644
--- a/browser/themes/windows/devtools/scratchpad.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/scratchpad.inc.css
\ No newline at end of file
deleted file mode 100644
--- a/browser/themes/windows/devtools/shadereditor.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/shadereditor.inc.css
deleted file mode 100644
--- a/browser/themes/windows/devtools/webaudioeditor.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/devtools/webaudioeditor.inc.css
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -446,28 +446,28 @@ browser.jar:
 *       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.svg                (../shared/devtools/images/webconsole.svg)
         skin/classic/browser/devtools/breadcrumbs-divider@2x.png    (../shared/devtools/images/breadcrumbs-divider@2x.png)
         skin/classic/browser/devtools/breadcrumbs-scrollbutton.png  (../shared/devtools/images/breadcrumbs-scrollbutton.png)
         skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
         skin/classic/browser/devtools/animationinspector.css        (../shared/devtools/animationinspector.css)
         skin/classic/browser/devtools/eyedropper.css                (../shared/devtools/eyedropper.css)
-*       skin/classic/browser/devtools/canvasdebugger.css            (devtools/canvasdebugger.css)
-*       skin/classic/browser/devtools/debugger.css                  (devtools/debugger.css)
+*       skin/classic/browser/devtools/canvasdebugger.css            (../shared/devtools/canvasdebugger.css)
+        skin/classic/browser/devtools/debugger.css                  (../shared/devtools/debugger.css)
 *       skin/classic/browser/devtools/netmonitor.css                (devtools/netmonitor.css)
-*       skin/classic/browser/devtools/performance.css               (devtools/performance.css)
+        skin/classic/browser/devtools/performance.css               (../shared/devtools/performance.css)
         skin/classic/browser/devtools/promisedebugger.css           (../shared/devtools/promisedebugger.css)
         skin/classic/browser/devtools/timeline-filter.svg           (../shared/devtools/images/timeline-filter.svg)
-*       skin/classic/browser/devtools/scratchpad.css                (devtools/scratchpad.css)
-*       skin/classic/browser/devtools/shadereditor.css              (devtools/shadereditor.css)
+*       skin/classic/browser/devtools/scratchpad.css                (../shared/devtools/scratchpad.css)
+        skin/classic/browser/devtools/shadereditor.css              (../shared/devtools/shadereditor.css)
         skin/classic/browser/devtools/storage.css                   (../shared/devtools/storage.css)
 *       skin/classic/browser/devtools/splitview.css                 (../shared/devtools/splitview.css)
         skin/classic/browser/devtools/styleeditor.css               (../shared/devtools/styleeditor.css)
-*       skin/classic/browser/devtools/webaudioeditor.css            (devtools/webaudioeditor.css)
+        skin/classic/browser/devtools/webaudioeditor.css            (../shared/devtools/webaudioeditor.css)
         skin/classic/browser/devtools/magnifying-glass.png          (../shared/devtools/images/magnifying-glass.png)
         skin/classic/browser/devtools/magnifying-glass@2x.png       (../shared/devtools/images/magnifying-glass@2x.png)
         skin/classic/browser/devtools/magnifying-glass-light.png    (../shared/devtools/images/magnifying-glass-light.png)
         skin/classic/browser/devtools/magnifying-glass-light@2x.png  (../shared/devtools/images/magnifying-glass-light@2x.png)
         skin/classic/browser/devtools/itemToggle.png                (../shared/devtools/images/itemToggle.png)
         skin/classic/browser/devtools/itemToggle@2x.png             (../shared/devtools/images/itemToggle@2x.png)
         skin/classic/browser/devtools/itemArrow-dark-rtl.svg        (../shared/devtools/images/itemArrow-dark-rtl.svg)
         skin/classic/browser/devtools/itemArrow-dark-ltr.svg        (../shared/devtools/images/itemArrow-dark-ltr.svg)
--- a/mobile/android/base/DynamicToolbar.java
+++ b/mobile/android/base/DynamicToolbar.java
@@ -101,16 +101,21 @@ public class DynamicToolbar {
 
     public void setVisible(boolean visible, VisibilityTransition transition) {
         ThreadUtils.assertOnUiThread();
 
         if (layerView == null) {
             return;
         }
 
+        // Don't hide the ActionBar/Toolbar, if it's pinned open by TextSelection.
+        if (visible == false && pinFlags.contains(PinReason.ACTION_MODE)) {
+            return;
+        }
+
         final boolean isImmediate = transition == VisibilityTransition.IMMEDIATE;
         if (visible) {
             layerView.getLayerMarginsAnimator().showMargins(isImmediate);
         } else {
             layerView.getLayerMarginsAnimator().hideMargins(isImmediate);
         }
     }
 
--- a/mobile/android/base/GeckoProfile.java
+++ b/mobile/android/base/GeckoProfile.java
@@ -25,16 +25,17 @@ import org.mozilla.gecko.GeckoProfileDir
 import org.mozilla.gecko.GeckoProfileDirectories.NoSuchProfileException;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.LocalBrowserDB;
 import org.mozilla.gecko.db.StubBrowserDB;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.firstrun.FirstrunPane;
+import org.mozilla.gecko.RestrictedProfiles;
 import org.mozilla.gecko.util.INIParser;
 import org.mozilla.gecko.util.INISection;
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.text.TextUtils;
@@ -903,16 +904,19 @@ public final class GeckoProfile {
                 // Because we are running in the background, we want to synchronize on the
                 // GeckoProfile instance so that we don't race with main thread operations
                 // such as locking/unlocking/removing the profile.
                 synchronized (GeckoProfile.this) {
                     // Skip initialization if the profile directory has been removed.
                     if (!profileDir.exists()) {
                         return;
                     }
+                    if (RestrictedProfiles.isUserRestricted(context)) {
+                        return;
+                    }
 
                     // We pass the number of added bookmarks to ensure that the
                     // indices of the distribution and default bookmarks are
                     // contiguous. Because there are always at least as many
                     // bookmarks as there are favicons, we can also guarantee that
                     // the favicon IDs won't overlap.
                     final LocalBrowserDB db = new LocalBrowserDB(getName());
                     final int offset = distribution == null ? 0 : db.addDistributionBookmarks(cr, distribution, 0);
--- a/mobile/android/base/gfx/LayerRenderer.java
+++ b/mobile/android/base/gfx/LayerRenderer.java
@@ -66,17 +66,16 @@ public class LayerRenderer implements Ta
     private final ScrollbarLayer mHorizScrollLayer;
     private final ScrollbarLayer mVertScrollLayer;
     private final FadeRunnable mFadeRunnable;
     private ByteBuffer mCoordByteBuffer;
     private FloatBuffer mCoordBuffer;
     private RenderContext mLastPageContext;
     private int mMaxTextureSize;
     private int mBackgroundColor;
-    private int mOverscrollColor;
 
     private long mLastFrameTime;
     private final CopyOnWriteArrayList<RenderTask> mTasks;
 
     private final CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
 
     // Dropped frames display
     private final int[] mFrameTimings;
@@ -141,17 +140,16 @@ public class LayerRenderer implements Ta
         "varying vec2 vTexCoord;\n" +
         "uniform sampler2D sTexture;\n" +
         "void main() {\n" +
         "    gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
         "}\n";
 
     public LayerRenderer(LayerView view) {
         mView = view;
-        setOverscrollColor(R.color.toolbar_grey);
 
         final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
         bitmapOptions.inScaled = false;
         Bitmap scrollbarImage =
                 BitmapUtils.decodeResource(view.getContext(), R.drawable.scrollbar, bitmapOptions);
         IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight());
         scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size);
 
@@ -200,22 +198,16 @@ public class LayerRenderer implements Ta
     }
 
     void onSurfaceCreated(EGLConfig config) {
         checkMonitoringEnabled();
         createDefaultProgram();
         activateDefaultProgram();
     }
 
-    void setOverscrollColor(int colorId) {
-        try {
-            mOverscrollColor = mView.getContext().getResources().getColor(colorId);
-        } catch (Resources.NotFoundException nfe) { mOverscrollColor = Color.BLACK; }
-    }
-
     public void createDefaultProgram() {
         int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DEFAULT_VERTEX_SHADER);
         int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DEFAULT_FRAGMENT_SHADER);
 
         mProgram = GLES20.glCreateProgram();
         GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
         GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
         GLES20.glLinkProgram(mProgram);                  // creates OpenGL program executables
@@ -457,38 +449,16 @@ public class LayerRenderer implements Ta
             RectF pageRect = mFrameMetrics.getPageRect();
             mAbsolutePageRect = RectUtils.round(pageRect);
 
             PointF origin = mFrameMetrics.getOrigin();
             pageRect.offset(-origin.x, -origin.y);
             mPageRect = RectUtils.round(pageRect);
         }
 
-        private void setScissorRect() {
-            Rect scissorRect = transformToScissorRect(mPageRect);
-            GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
-            GLES20.glScissor(scissorRect.left, scissorRect.top,
-                             scissorRect.width(), scissorRect.height());
-        }
-
-        private Rect transformToScissorRect(Rect rect) {
-            IntSize screenSize = new IntSize(mFrameMetrics.getSize());
-
-            int left = Math.max(0, rect.left);
-            int top = Math.max(0, rect.top);
-            int right = Math.min(screenSize.width, rect.right);
-            int bottom = Math.min(screenSize.height, rect.bottom);
-
-            Rect scissorRect = new Rect(left, screenSize.height - bottom, right,
-                                        (screenSize.height - bottom) + (bottom - top));
-            scissorRect.offset(Math.round(-mRenderOffset.x), Math.round(-mRenderOffset.y));
-
-            return scissorRect;
-        }
-
         /** This function is invoked via JNI; be careful when modifying signature. */
         @JNITarget
         public void beginDrawing() {
             mFrameStartTime = System.nanoTime();
 
             TextureReaper.get().reap();
             TextureGenerator.get().fill();
 
@@ -545,26 +515,21 @@ public class LayerRenderer implements Ta
         /** This function is invoked via JNI; be careful when modifying signature. */
         @JNITarget
         public void drawBackground() {
             // Any GL state which is changed here must be restored in
             // restoreState(...)
 
             GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
 
-            // Draw the overscroll background area as a solid color
-            clear(mOverscrollColor);
-
             // Update background color.
             mBackgroundColor = mView.getBackgroundColor();
 
             // Clear the page area to the page background colour.
-            setScissorRect();
             clear(mBackgroundColor);
-            GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
         }
 
         @JNITarget
         public void drawForeground() {
             // Any GL state which is changed here must be restored in
             // restoreState(...)
 
             /* Draw any extra layers that were added (likely plugins) */
@@ -685,20 +650,16 @@ public class LayerRenderer implements Ta
     @Override
     public void onTabChanged(final Tab tab, Tabs.TabEvents msg, Object data) {
         // Sets the background of the newly selected tab. This background color
         // gets cleared in endDrawing(). This function runs on the UI thread,
         // but other code that touches the paint state is run on the compositor
         // thread, so this may need to be changed if any problems appear.
         if (msg == Tabs.TabEvents.SELECTED) {
             if (mView != null) {
-                final int overscrollColor =
-                        (tab.isPrivate() ? R.color.tabs_tray_grey_pressed : R.color.toolbar_grey);
-                setOverscrollColor(overscrollColor);
-
                 if (mView.getChildAt(0) != null) {
                     mView.getChildAt(0).setBackgroundColor(tab.getBackgroundColor());
                 }
                 mView.setPaintState(LayerView.PAINT_START);
             }
         }
     }
 
--- a/mobile/android/base/home/SearchEngineBar.java
+++ b/mobile/android/base/home/SearchEngineBar.java
@@ -5,31 +5,30 @@
 
  package org.mozilla.gecko.home;
 
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.graphics.drawable.DrawableCompat;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.util.DrawableUtil;
 import org.mozilla.gecko.widget.TwoWayView;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class SearchEngineBar extends TwoWayView
                              implements AdapterView.OnItemClickListener {
     private static final String LOGTAG = "Gecko" + SearchEngineBar.class.getSimpleName();
@@ -179,19 +178,18 @@ public class SearchEngineBar extends Two
             }
         }
 
         private View getLabelView(View view, final ViewGroup parent) {
             if (view == null) {
                 view = LayoutInflater.from(getContext()).inflate(R.layout.search_engine_bar_label, parent, false);
             }
 
-            final Drawable icon = DrawableCompat.wrap(
-                    ContextCompat.getDrawable(parent.getContext(), R.drawable.search_icon_active).mutate());
-            DrawableCompat.setTint(icon, getResources().getColor(R.color.disabled_grey));
+            final Drawable icon =
+                    DrawableUtil.tintDrawable(parent.getContext(), R.drawable.search_icon_active, R.color.disabled_grey);
 
             final ImageView iconView = (ImageView) view.findViewById(R.id.search_engine_label);
             iconView.setImageDrawable(icon);
             iconView.setScaleType(ImageView.ScaleType.FIT_XY);
 
             return view;
         }
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -71,16 +71,17 @@ mgjar.extra_jars += [
 mgjar.javac_flags += ['-Xlint:all']
 
 gujar = add_java_jar('gecko-util')
 gujar.sources += [
     'util/ActivityResultHandler.java',
     'util/ActivityResultHandlerMap.java',
     'util/ActivityUtils.java',
     'util/Clipboard.java',
+    'util/DrawableUtil.java',
     'util/EventCallback.java',
     'util/FileUtils.java',
     'util/FloatUtils.java',
     'util/GamepadUtils.java',
     'util/GeckoBackgroundThread.java',
     'util/GeckoEventListener.java',
     'util/GeckoJarReader.java',
     'util/GeckoRequest.java',
index ed29b45cfd7a276100bc9113d2b63f859c1e81bc..0e9b94ddd0b63ef51911f2b29909e6198b6b73ed
GIT binary patch
literal 2808
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
KMNUMnLSTa9xlE@3
index ecde4163c25b97a1d44b052ab42b3b9bf85f182b..476445f847a93222f668f3a24d1e0278c7985c9d
GIT binary patch
literal 3796
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
KMNUMnLSTYXBt1U>
index a0df50a122f62eba8dd84859c7a0e0de92b9a134..571d7aa6a16bbb1d43c3cdba5a2de3f1e02d7114
GIT binary patch
literal 5576
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
W0dWUdS-IE%0000<MNUMnLSTX<(AZo6
--- a/mobile/android/base/resources/layout/site_identity.xml
+++ b/mobile/android/base/resources/layout/site_identity.xml
@@ -66,16 +66,26 @@
 
                 <TextView android:id="@+id/verifier"
                           android:layout_width="match_parent"
                           android:layout_height="wrap_content"
                           android:layout_marginTop="@dimen/doorhanger_section_padding_medium"
                           android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"/>
 
             </LinearLayout>
+
+            <TextView android:id="@+id/site_identity_link"
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:textAppearance="@style/TextAppearance.DoorHanger.Medium"
+                      android:textColor="@color/link_blue"
+                      android:layout_marginTop="@dimen/doorhanger_section_padding_large"
+                      android:text="@string/learn_more"
+                      android:visibility="gone"/>
+
             <TextView android:id="@+id/site_settings_link"
                       android:layout_width="match_parent"
                       android:layout_height="wrap_content"
                       android:textAppearance="@style/TextAppearance.DoorHanger.Medium"
                       android:textColor="@color/link_blue"
                       android:layout_marginTop="@dimen/doorhanger_section_padding_large"
                       android:text="@string/contextmenu_site_settings"
                       android:visibility="gone"/>
--- a/mobile/android/base/toolbar/SiteIdentityPopup.java
+++ b/mobile/android/base/toolbar/SiteIdentityPopup.java
@@ -45,20 +45,22 @@ import org.mozilla.gecko.widget.Doorhang
 import org.mozilla.gecko.widget.SiteLogins;
 
 /**
  * SiteIdentityPopup is a singleton class that displays site identity data in
  * an arrow panel popup hanging from the lock icon in the browser toolbar.
  */
 public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListener {
 
-    public static enum ButtonType { DISABLE, ENABLE, KEEP_BLOCKING, CANCEL, COPY };
+    public static enum ButtonType { DISABLE, ENABLE, KEEP_BLOCKING, CANCEL, COPY }
 
     private static final String LOGTAG = "GeckoSiteIdentityPopup";
 
+    private static final String MIXED_CONTENT_SUPPORT_URL =
+        "https://support.mozilla.org/kb/how-does-insecure-content-affect-safety-android";
     private static final String TRACKING_CONTENT_SUPPORT_URL =
         "https://support.mozilla.org/kb/firefox-android-tracking-protection";
 
     // Placeholder string.
     private final static String FORMAT_S = "%s";
 
     private final Resources mResources;
     private SiteIdentity mSiteIdentity;
@@ -69,16 +71,17 @@ public class SiteIdentityPopup extends A
 
     private ImageView mIcon;
     private TextView mTitle;
     private TextView mSecurityState;
     private TextView mMixedContentActivity;
     private TextView mOwner;
     private TextView mOwnerSupplemental;
     private TextView mVerifier;
+    private TextView mLink;
     private TextView mSiteSettingsLink;
 
     private View mDivider;
 
     private DoorHanger mTrackingContentNotification;
     private DoorHanger mSelectLoginDoorhanger;
 
     private final OnButtonClickListener mContentButtonClickListener;
@@ -113,16 +116,24 @@ public class SiteIdentityPopup extends A
         mSecurityState = (TextView) mIdentity.findViewById(R.id.site_identity_state);
         mMixedContentActivity = (TextView) mIdentity.findViewById(R.id.mixed_content_activity);
 
         mOwner = (TextView) mIdentityKnownContainer.findViewById(R.id.owner);
         mOwnerSupplemental = (TextView) mIdentityKnownContainer.findViewById(R.id.owner_supplemental);
         mVerifier = (TextView) mIdentityKnownContainer.findViewById(R.id.verifier);
         mDivider = mIdentity.findViewById(R.id.divider_doorhanger);
 
+        mLink = (TextView) mIdentity.findViewById(R.id.site_identity_link);
+        mLink.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Tabs.getInstance().loadUrlInTab(MIXED_CONTENT_SUPPORT_URL);
+            }
+        });
+
         mSiteSettingsLink = (TextView) mIdentity.findViewById(R.id.site_settings_link);
     }
 
     private void updateIdentity(final SiteIdentity siteIdentity) {
         if (!mInflated) {
             init();
         }
 
@@ -294,27 +305,31 @@ public class SiteIdentityPopup extends A
             stateIcon.setBounds(0, 0, stateIcon.getIntrinsicWidth()/2, stateIcon.getIntrinsicHeight()/2);
             mSecurityState.setCompoundDrawables(stateIcon, null, null, null);
             mSecurityState.setCompoundDrawablePadding((int) mResources.getDimension(R.dimen.doorhanger_drawable_padding));
             mSecurityState.setText(R.string.identity_connection_secure);
 
             if (siteIdentity.getMixedMode() == MixedMode.MIXED_CONTENT_BLOCKED) {
                 mMixedContentActivity.setVisibility(View.VISIBLE);
                 mMixedContentActivity.setText(R.string.mixed_content_blocked_all);
+                mLink.setVisibility(View.VISIBLE);
             } else {
                 mMixedContentActivity.setVisibility(View.GONE);
+                mLink.setVisibility(View.GONE);
             }
         } else {
             if (siteIdentity.getMixedMode() == MixedMode.MIXED_CONTENT_LOADED) {
                 mIcon.setImageResource(R.drawable.lock_disabled);
                 mMixedContentActivity.setVisibility(View.VISIBLE);
                 mMixedContentActivity.setText(R.string.mixed_content_protection_disabled);
+                mLink.setVisibility(View.VISIBLE);
             } else {
                 mIcon.setImageResource(R.drawable.globe_light);
                 mMixedContentActivity.setVisibility(View.GONE);
+                mLink.setVisibility(View.GONE);
             }
 
             mSecurityState.setText(R.string.identity_connection_insecure);
             mSecurityState.setTextColor(mResources.getColor(R.color.placeholder_active_grey));
             mSecurityState.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
             mSecurityState.setCompoundDrawablePadding(0);
         }
     }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/util/DrawableUtil.java
@@ -0,0 +1,21 @@
+/* -*- 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.util;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
+
+public class DrawableUtil {
+
+    public static Drawable tintDrawable(final Context context, final int drawableID, final int colorID) {
+        final Drawable icon = DrawableCompat.wrap(
+                ContextCompat.getDrawable(context, drawableID).mutate());
+        DrawableCompat.setTint(icon, context.getResources().getColor(colorID));
+        return icon;
+    }
+}
--- a/mobile/android/chrome/content/aboutLogins.js
+++ b/mobile/android/chrome/content/aboutLogins.js
@@ -97,17 +97,17 @@ let Logins = {
     }
 
   },
 
   init: function () {
     window.addEventListener("popstate", this , false);
 
     Services.obs.addObserver(this, "passwordmgr-storage-changed", false);
-    document.getElementById("save-btn").addEventListener("click", this._onSaveEditLogin.bind(this), false);
+    document.getElementById("update-btn").addEventListener("click", this._onSaveEditLogin.bind(this), false);
     document.getElementById("password-btn").addEventListener("click", this._onPasswordBtn.bind(this), false);
 
     this._loadList(this._getLogins());
 
     let filterInput = document.getElementById("filter-input");
     let filterContainer = document.getElementById("filter-input-container");
 
     filterInput.addEventListener("input", (event) => {
@@ -209,16 +209,29 @@ let Logins = {
 
     let headerText = document.getElementById("edit-login-header-text");
     if (login.hostname && (login.hostname != "")) {
       headerText.textContent = login.hostname;
     }
     else {
       headerText.textContent = gStringBundle.GetStringFromName("editLogin.fallbackTitle");
     }
+
+    passwordField.addEventListener("input", (event) => {
+      let newPassword = passwordField.value;
+      let updateBtn = document.getElementById("update-btn");
+
+      if (newPassword === "") {
+        updateBtn.disabled = true;
+        updateBtn.classList.add("disabled-btn");
+      } else if ((newPassword !== "") && (updateBtn.disabled === true)) {
+        updateBtn.disabled = false;
+        updateBtn.classList.remove("disabled-btn");
+      }
+    }, false);
   },
 
   _onSaveEditLogin: function() {
     Services.telemetry.getHistogramById("PWMGR_ABOUT_LOGINS_USAGE").add(LOGIN_EDITED);
     let newUsername = document.getElementById("username").value;
     let newPassword = document.getElementById("password").value;
     let newDomain  = document.getElementById("hostname").value;
     let origUsername = this._selectedLogin.username;
--- a/mobile/android/chrome/content/aboutLogins.xhtml
+++ b/mobile/android/chrome/content/aboutLogins.xhtml
@@ -61,14 +61,14 @@
       <div class="edit-login-div">
         <input type="text" name="username" id="username" class="edit-login-input"/>
       </div>
       <div class="edit-login-div">
         <input type="password" id="password" name="password" value="password" class="edit-login-input" />
         <button id="password-btn"></button>
       </div>
       <div class="edit-login-div">
-        <button id="save-btn">&aboutLogins.update;</button>
+        <button id="update-btn" class="update-button">&aboutLogins.update;</button>
       </div>
     </div>
 
   </body>
 </html>
--- a/mobile/android/themes/core/aboutLogins.css
+++ b/mobile/android/themes/core/aboutLogins.css
@@ -94,28 +94,32 @@ body {
 }
 
 #edit-login-page {
   background-color: #FFFFFF;
   height: 100%;
 }
 
 
-#save-btn {
+.update-button {
   flex: 1;
   height: 60px;
   background-color: #E66000; /*matched to action_orange in java codebase*/
   color: #FFFFFF;
   font-size: 20px;
   font-weight: bold;
   border-radius: 4px;
   border-width: 0px;
   margin-top: 10px;
 }
 
+.disabled-btn {
+  background-color: #BFBFBF; /*matched to disabled_grey in the java codebase,in colors.xml*/
+}
+
 .password-btn-hide {
   background-color: #777777;
   color: white;
 }
 
 .edit-login-input {
   flex: 1;
   height: 36px;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4702,16 +4702,58 @@
   "TELEMETRY_PENDING_CHECKING_OVER_QUOTA_MS": {
     "alert_emails": ["telemetry-client-dev@mozilla.com"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "300000",
     "n_buckets": 20,
     "description": "Time (ms) it takes for checking if the pending pings are over-quota"
   },
+  "TELEMETRY_PING_SIZE_EXCEEDED_SEND": {
+    "alert_emails": ["telemetry-client-dev@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "count",
+    "description": "Number of Telemetry pings discarded before sending because they exceeded the maximum size"
+  },
+  "TELEMETRY_PING_SIZE_EXCEEDED_PENDING": {
+    "alert_emails": ["telemetry-client-dev@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "count",
+    "description": "Number of Telemetry pending pings discarded because they exceeded the maximum size"
+  },
+  "TELEMETRY_PING_SIZE_EXCEEDED_ARCHIVED": {
+    "alert_emails": ["telemetry-client-dev@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "count",
+    "description": "Number of archived Telemetry pings discarded because they exceeded the maximum size"
+  },
+  "TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB": {
+    "alert_emails": ["telemetry-client-dev@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "linear",
+    "high": "30",
+    "n_buckets": 29,
+    "description": "The size (MB) of the Telemetry pending pings exceeding the maximum file size"
+  },
+  "TELEMETRY_DISCARDED_ARCHIVED_PINGS_SIZE_MB": {
+    "alert_emails": ["telemetry-client-dev@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "linear",
+    "high": "30",
+    "n_buckets": 29,
+    "description": "The size (MB) of the Telemetry archived, compressed, pings exceeding the maximum file size"
+  },
+  "TELEMETRY_DISCARDED_SEND_PINGS_SIZE_MB": {
+    "alert_emails": ["telemetry-client-dev@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "linear",
+    "high": "30",
+    "n_buckets": 29,
+    "description": "The size (MB) of the ping data submitted to Telemetry exceeding the maximum size"
+  },
   "TELEMETRY_DISCARDED_CONTENT_PINGS_COUNT": {
     "alert_emails": ["perf-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "count",
     "description": "Count of discarded content payloads."
   },
   "TELEMETRY_COMPRESS": {
     "expires_in_version": "never",
--- a/toolkit/components/telemetry/TelemetrySend.jsm
+++ b/toolkit/components/telemetry/TelemetrySend.jsm
@@ -923,16 +923,29 @@ let TelemetrySendImpl = {
     request.setRequestHeader("Content-Encoding", "gzip");
     let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                     .createInstance(Ci.nsIScriptableUnicodeConverter);
     converter.charset = "UTF-8";
     startTime = new Date();
     let utf8Payload = converter.ConvertFromUnicode(JSON.stringify(networkPayload));
     utf8Payload += converter.Finish();
     Telemetry.getHistogramById("TELEMETRY_STRINGIFY").add(new Date() - startTime);
+
+    // Check the size and drop pings which are too big.
+    const pingSizeBytes = utf8Payload.length;
+    if (pingSizeBytes > TelemetryStorage.MAXIMUM_PING_SIZE) {
+      this._log.error("_doPing - submitted ping exceeds the size limit, size: " + pingSizeBytes);
+      Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_SEND").add();
+      Telemetry.getHistogramById("TELEMETRY_DISCARDED_SEND_PINGS_SIZE_MB")
+               .add(Math.floor(pingSizeBytes / 1024 / 1024));
+      // We don't need to call |request.abort()| as it was not sent yet.
+      this._pendingPingRequests.delete(id);
+      return TelemetryStorage.removePendingPing(id);
+    }
+
     let payloadStream = Cc["@mozilla.org/io/string-input-stream;1"]
                         .createInstance(Ci.nsIStringInputStream);
     startTime = new Date();
     payloadStream.data = gzipCompressString(utf8Payload);
     Telemetry.getHistogramById("TELEMETRY_COMPRESS").add(new Date() - startTime);
     startTime = new Date();
     request.send(payloadStream);
 
--- a/toolkit/components/telemetry/TelemetryStorage.jsm
+++ b/toolkit/components/telemetry/TelemetryStorage.jsm
@@ -54,16 +54,19 @@ const MAX_ARCHIVED_PINGS_RETENTION_MS = 
 
 // Maximum space the archive can take on disk (in Bytes).
 const ARCHIVE_QUOTA_BYTES = 120 * 1024 * 1024; // 120 MB
 // Maximum space the outgoing pings can take on disk, for Desktop (in Bytes).
 const PENDING_PINGS_QUOTA_BYTES_DESKTOP = 15 * 1024 * 1024; // 15 MB
 // Maximum space the outgoing pings can take on disk, for Mobile (in Bytes).
 const PENDING_PINGS_QUOTA_BYTES_MOBILE = 1024 * 1024; // 1 MB
 
+// The maximum size a pending/archived ping can take on disk.
+const PING_FILE_MAXIMUM_SIZE_BYTES = 1024 * 1024; // 1 MB
+
 // This special value is submitted when the archive is outside of the quota.
 const ARCHIVE_SIZE_PROBE_SPECIAL_VALUE = 300;
 
 // This special value is submitted when the pending pings is outside of the quota, as
 // we don't know the size of the pings above the quota.
 const PENDING_PINGS_SIZE_PROBE_SPECIAL_VALUE = 17;
 
 const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -131,16 +134,22 @@ function waitForAll(it) {
 }
 
 this.TelemetryStorage = {
   get pingDirectoryPath() {
     return OS.Path.join(OS.Constants.Path.profileDir, "saved-telemetry-pings");
   },
 
   /**
+   * The maximum size a ping can have, in bytes.
+   */
+  get MAXIMUM_PING_SIZE() {
+    return PING_FILE_MAXIMUM_SIZE_BYTES;
+  },
+  /**
    * Shutdown & block on any outstanding async activity in this module.
    *
    * @return {Promise} Promise that is resolved when shutdown is complete.
    */
   shutdown: function() {
     return TelemetryStorageImpl.shutdown();
   },
 
@@ -639,23 +648,37 @@ let TelemetryStorageImpl = {
     if (!data) {
       this._log.trace("loadArchivedPing - no ping with id: " + id);
       return Promise.reject(new Error("TelemetryStorage.loadArchivedPing - no ping with id " + id));
     }
 
     const path = getArchivedPingPath(id, new Date(data.timestampCreated), data.type);
     const pathCompressed = path + "lz4";
 
+    // Purge pings which are too big.
+    let checkSize = function*(path) {
+      const fileSize = (yield OS.File.stat(path)).size;
+      if (fileSize > PING_FILE_MAXIMUM_SIZE_BYTES) {
+        Telemetry.getHistogramById("TELEMETRY_DISCARDED_ARCHIVED_PINGS_SIZE_MB")
+                 .add(Math.floor(fileSize / 1024 / 1024));
+        Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_ARCHIVED").add();
+        yield OS.File.remove(path, {ignoreAbsent: true});
+        throw new Error("loadArchivedPing - exceeded the maximum ping size: " + fileSize);
+      }
+    };
+
     try {
       // Try to load a compressed version of the archived ping first.
       this._log.trace("loadArchivedPing - loading ping from: " + pathCompressed);
+      yield* checkSize(pathCompressed);
       return yield this.loadPingFile(pathCompressed, /*compressed*/ true);
     } catch (ex if ex.becauseNoSuchFile) {
       // If that fails, look for the uncompressed version.
       this._log.trace("loadArchivedPing - compressed ping not found, loading: " + path);
+      yield* checkSize(path);
       return yield this.loadPingFile(path, /*compressed*/ false);
     }
   }),
 
   /**
    * Remove an archived ping from disk.
    *
    * @param {string} id The pings id.
@@ -666,16 +689,18 @@ let TelemetryStorageImpl = {
   _removeArchivedPing: Task.async(function*(id, timestampCreated, type) {
     this._log.trace("_removeArchivedPing - id: " + id + ", timestampCreated: " + timestampCreated + ", type: " + type);
     const path = getArchivedPingPath(id, new Date(timestampCreated), type);
     const pathCompressed = path + "lz4";
 
     this._log.trace("_removeArchivedPing - removing ping from: " + path);
     yield OS.File.remove(path, {ignoreAbsent: true});
     yield OS.File.remove(pathCompressed, {ignoreAbsent: true});
+    // Remove the ping from the cache.
+    this._archivedPings.delete(id);
   }),
 
   /**
    * Clean the pings archive by removing old pings.
    *
    * @return {Promise} Resolved when the cleanup task completes.
    */
   runCleanPingArchiveTask: function() {
@@ -808,16 +833,29 @@ let TelemetryStorageImpl = {
       // Get the size for this ping.
       const fileSize =
         yield getArchivedPingSize(ping.id, new Date(ping.timestampCreated), ping.type);
       if (!fileSize) {
         this._log.warn("_enforceArchiveQuota - Unable to find the size of ping " + ping.id);
         continue;
       }
 
+      // Enforce a maximum file size limit on archived pings.
+      if (fileSize > PING_FILE_MAXIMUM_SIZE_BYTES) {
+        this._log.error("_enforceArchiveQuota - removing file exceeding size limit, size: " + fileSize);
+        // We just remove the ping from the disk, we don't bother removing it from pingList
+        // since it won't contribute to the quota.
+        yield this._removeArchivedPing(ping.id, ping.timestampCreated, ping.type)
+                  .catch(e => this._log.error("_enforceArchiveQuota - failed to remove archived ping" + ping.id));
+        Telemetry.getHistogramById("TELEMETRY_DISCARDED_ARCHIVED_PINGS_SIZE_MB")
+                 .add(Math.floor(fileSize / 1024 / 1024));
+        Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_ARCHIVED").add();
+        continue;
+      }
+
       archiveSizeInBytes += fileSize;
 
       if (archiveSizeInBytes < SAFE_QUOTA) {
         // We save the index of the last ping which is ok to keep in order to speed up ping
         // pruning.
         lastPingIndexToKeep = i;
       } else if (archiveSizeInBytes > Policy.getArchiveQuota()) {
         // Ouch, our ping archive is too big. Bail out and start pruning!
@@ -852,19 +890,16 @@ let TelemetryStorageImpl = {
       if (this._shutdown) {
         this._log.trace("_enforceArchiveQuota - Terminating the clean up task due to shutdown");
         return;
       }
 
       // This list is guaranteed to be in order, so remove the pings at its
       // beginning (oldest).
       yield this._removeArchivedPing(ping.id, ping.timestampCreated, ping.type);
-
-      // Remove outdated pings from the cache.
-      this._archivedPings.delete(ping.id);
     }
 
     const endTimeStamp = Policy.now().getTime();
     submitProbes(ARCHIVE_SIZE_PROBE_SPECIAL_VALUE, pingsToPurge.length,
                  Math.ceil(endTimeStamp - startTimeStamp));
   }),
 
   _cleanArchive: Task.async(function*() {
@@ -1186,36 +1221,58 @@ let TelemetryStorageImpl = {
       this._pendingPings.set(ping.id, {
         path: path,
         lastModificationDate: Policy.now().getTime(),
       });
       this._log.trace("savePendingPing - saved ping with id " + ping.id);
     });
   },
 
-  loadPendingPing: function(id) {
+  loadPendingPing: Task.async(function*(id) {
     this._log.trace("loadPendingPing - id: " + id);
     let info = this._pendingPings.get(id);
     if (!info) {
       this._log.trace("loadPendingPing - unknown id " + id);
-      return Promise.reject(new Error("TelemetryStorage.loadPendingPing - no ping with id " + id));
+      throw new Error("TelemetryStorage.loadPendingPing - no ping with id " + id);
+    }
+
+    // Try to get the dimension of the ping. If that fails, update the histograms.
+    let fileSize = 0;
+    try {
+      fileSize = (yield OS.File.stat(info.path)).size;
+    } catch (e if e instanceof OS.File.Error && e.becauseNoSuchFile) {
+      // Fall through and let |loadPingFile| report the error.
     }
 
-    return this.loadPingFile(info.path, false).catch(e => {
+    // Purge pings which are too big.
+    if (fileSize > PING_FILE_MAXIMUM_SIZE_BYTES) {
+      yield this.removePendingPing(id);
+      Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB")
+               .add(Math.floor(fileSize / 1024 / 1024));
+      Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").add();
+      throw new Error("loadPendingPing - exceeded the maximum ping size: " + fileSize);
+    }
+
+    // Try to load the ping file. Update the related histograms on failure.
+    let ping;
+    try {
+      ping = yield this.loadPingFile(info.path, false);
+    } catch(e) {
       // If we failed to load the ping, check what happened and update the histogram.
       // Then propagate the rejection.
       if (e instanceof PingReadError) {
         Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_READ").add();
       } else if (e instanceof PingParseError) {
         Telemetry.getHistogramById("TELEMETRY_PENDING_LOAD_FAILURE_PARSE").add();
       }
+      throw e;
+    };
 
-      return Promise.reject(e);
-    });
-  },
+    return ping;
+  }),
 
   removePendingPing: function(id) {
     let info = this._pendingPings.get(id);
     if (!info) {
       this._log.trace("removePendingPing - unknown id " + id);
       return Promise.resolve();
     }
 
@@ -1276,16 +1333,31 @@ let TelemetryStorageImpl = {
       let info;
       try {
         info = yield OS.File.stat(file.path);
       } catch (ex) {
         this._log.error("_scanPendingPings - failed to stat file " + file.path, ex);
         continue;
       }
 
+      // Enforce a maximum file size limit on pending pings.
+      if (info.size > PING_FILE_MAXIMUM_SIZE_BYTES) {
+        this._log.error("_scanPendingPings - removing file exceeding size limit " + file.path);
+        try {
+          yield OS.File.remove(file.path);
+        } catch (ex) {
+          this._log.error("_scanPendingPings - failed to remove file " + file.path, ex);
+        } finally {
+          Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB")
+                   .add(Math.floor(info.size / 1024 / 1024));
+          Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").add();
+          continue;
+        }
+      }
+
       let id = OS.Path.basename(file.path);
       if (!UUID_REGEX.test(id)) {
         this._log.trace("_scanPendingPings - filename is not a UUID: " + id);
         id = Utils.generateUUID();
       }
 
       this._pendingPings.set(id, {
         path: file.path,
--- a/toolkit/components/telemetry/tests/unit/head.js
+++ b/toolkit/components/telemetry/tests/unit/head.js
@@ -282,16 +282,27 @@ function truncateToDays(aMsec) {
 }
 
 // Returns a promise that resolves to true when the passed promise rejects,
 // false otherwise.
 function promiseRejects(promise) {
   return promise.then(() => false, () => true);
 }
 
+// Generates a random string of at least a specific length.
+function generateRandomString(length) {
+  let string = "";
+
+  while (string.length < length) {
+    string += Math.random().toString(36);
+  }
+
+  return string.substring(0, length);
+}
+
 if (runningInParent) {
   // Set logging preferences for all the tests.
   Services.prefs.setCharPref("toolkit.telemetry.log.level", "Trace");
   // Telemetry archiving should be on.
   Services.prefs.setBoolPref("toolkit.telemetry.archive.enabled", true);
   // Telemetry xpcshell tests cannot show the infobar.
   Services.prefs.setBoolPref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
 
--- a/toolkit/components/telemetry/tests/unit/test_PingAPI.js
+++ b/toolkit/components/telemetry/tests/unit/test_PingAPI.js
@@ -195,16 +195,19 @@ add_task(function* test_archivedPings() 
 add_task(function* test_archiveCleanup() {
   const PING_TYPE = "foo";
 
   // Empty the archive.
   yield OS.File.removeDir(gPingsArchivePath);
 
   Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SCAN_PING_COUNT").clear();
   Telemetry.getHistogramById("TELEMETRY_ARCHIVE_DIRECTORIES_COUNT").clear();
+  // Also reset these histograms to make sure normal sized pings don't get counted.
+  Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_ARCHIVED").clear();
+  Telemetry.getHistogramById("TELEMETRY_DISCARDED_ARCHIVED_PINGS_SIZE_MB").clear();
 
   // Build the cache. Nothing should be evicted as there's no ping directory.
   yield TelemetryController.reset();
   yield TelemetryStorage.testCleanupTaskPromise();
   yield TelemetryArchive.promiseArchivedPingList();
 
   let h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SCAN_PING_COUNT").snapshot();
   Assert.equal(h.sum, 0, "Telemetry must report 0 pings scanned if no archive dir exists.");
@@ -358,16 +361,49 @@ add_task(function* test_archiveCleanup()
   h = Telemetry.getHistogramById("TELEMETRY_ARCHIVE_SIZE_MB").snapshot();
   Assert.equal(h.sum, 300, "Archive quota was hit, a special size must be reported.");
 
   // Trigger a cleanup again and make sure we're not removing anything.
   yield TelemetryController.reset();
   yield TelemetryStorage.testCleanupTaskPromise();
   yield TelemetryArchive.promiseArchivedPingList();
   yield checkArchive();
+
+  const OVERSIZED_PING_ID = "9b21ec8f-f762-4d28-a2c1-44e1c4694f24";
+  // Create and archive an oversized, uncompressed, ping.
+  const OVERSIZED_PING = {
+    id: OVERSIZED_PING_ID,
+    type: PING_TYPE,
+    creationDate: (new Date()).toISOString(),
+    // Generate a ~2MB string to use as the payload.
+    payload: generateRandomString(2 * 1024 * 1024)
+  };
+  yield TelemetryArchive.promiseArchivePing(OVERSIZED_PING);
+
+  // Get the size of the archived ping.
+  const oversizedPingPath =
+    TelemetryStorage._testGetArchivedPingPath(OVERSIZED_PING.id, new Date(OVERSIZED_PING.creationDate), PING_TYPE) + "lz4";
+  const archivedPingSizeMB = Math.floor((yield OS.File.stat(oversizedPingPath)).size / 1024 / 1024);
+
+  // We expect the oversized ping to be pruned when scanning the archive.
+  expectedPrunedInfo.push({ id: OVERSIZED_PING_ID, creationDate: new Date(OVERSIZED_PING.creationDate) });
+
+  // Scan the archive.
+  yield TelemetryController.reset();
+  yield TelemetryStorage.testCleanupTaskPromise();
+  yield TelemetryArchive.promiseArchivedPingList();
+  // The following also checks that non oversized pings are not removed.
+  yield checkArchive();
+
+  // Make sure we're correctly updating the related histograms.
+  h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_ARCHIVED").snapshot();
+  Assert.equal(h.sum, 1, "Telemetry must report 1 oversized ping in the archive.");
+  h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_ARCHIVED_PINGS_SIZE_MB").snapshot();
+  Assert.equal(h.counts[archivedPingSizeMB], 1,
+               "Telemetry must report the correct size for the oversized ping.");
 });
 
 add_task(function* test_clientId() {
   // Check that a ping submitted after the delayed telemetry initialization completed
   // should get a valid client id.
   yield TelemetryController.reset();
   const clientId = TelemetryController.clientID;
 
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySend.js
@@ -233,16 +233,43 @@ add_task(function* test_backoffTimeout()
   Assert.equal(countByType.get(TEST_TYPE_C), 1, "Should have received the correct amount of type C pings");
   Assert.equal(countByType.get(TEST_TYPE_D), 1, "Should have received the correct amount of type D pings");
   Assert.equal(countByType.get(TEST_TYPE_E), 1, "Should have received the correct amount of type E pings");
 
   yield TelemetrySend.testWaitOnOutgoingPings();
   Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings left");
 });
 
+add_task(function* test_discardBigPings() {
+  const TEST_PING_TYPE = "test-ping-type";
+
+  // Generate a 2MB string and create an oversized payload.
+  const OVERSIZED_PAYLOAD = generateRandomString(2 * 1024 * 1024);
+
+  // Reset the histograms.
+  Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_SEND").clear();
+  Telemetry.getHistogramById("TELEMETRY_DISCARDED_SEND_PINGS_SIZE_MB").clear();
+
+  // Submit a ping of a normal size and check that we don't count it in the histogram.
+  yield TelemetryController.submitExternalPing(TEST_PING_TYPE, { test: "test" });
+  yield TelemetrySend.testWaitOnOutgoingPings();
+  let h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_SEND").snapshot();
+  Assert.equal(h.sum, 0, "Telemetry must report no oversized ping submitted.");
+  h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_SEND_PINGS_SIZE_MB").snapshot();
+  Assert.equal(h.sum, 0, "Telemetry must report no oversized pings.");
+
+  // Submit an oversized ping and check that it gets discarded.
+  yield TelemetryController.submitExternalPing(TEST_PING_TYPE, OVERSIZED_PAYLOAD);
+  yield TelemetrySend.testWaitOnOutgoingPings();
+  h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_SEND").snapshot();
+  Assert.equal(h.sum, 1, "Telemetry must report 1 oversized ping submitted.");
+  h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_SEND_PINGS_SIZE_MB").snapshot();
+  Assert.equal(h.counts[2], 1, "Telemetry must report a 2MB, oversized, ping submitted.");
+});
+
 add_task(function* test_evictedOnServerErrors() {
   const TEST_TYPE = "test-evicted";
 
   yield TelemetrySend.reset();
 
   // Write a custom ping handler which will return 403. This will trigger ping eviction
   // on client side.
   PingServer.registerPingHandler((req, res) => {
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js
@@ -506,14 +506,56 @@ add_task(function* test_pendingPingsQuot
   h = Telemetry.getHistogramById("TELEMETRY_PENDING_PINGS_SIZE_MB").snapshot();
   Assert.equal(h.sum, 17, "Pending pings quota was hit, a special size must be reported.");
 
   // Trigger a cleanup again and make sure we're not removing anything.
   yield TelemetryController.reset();
   yield TelemetryStorage.testPendingQuotaTaskPromise();
   yield checkPendingPings();
 
+  const OVERSIZED_PING_ID = "9b21ec8f-f762-4d28-a2c1-44e1c4694f24";
+  // Create a pending oversized ping.
+  const OVERSIZED_PING = {
+    id: OVERSIZED_PING_ID,
+    type: PING_TYPE,
+    creationDate: (new Date()).toISOString(),
+    // Generate a 2MB string to use as the ping payload.
+    payload: generateRandomString(2 * 1024 * 1024),
+  };
+  yield TelemetryStorage.savePendingPing(OVERSIZED_PING);
+
+  // Reset the histograms.
+  Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").clear();
+  Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB").clear();
+
+  // Try to manually load the oversized ping.
+  yield Assert.rejects(TelemetryStorage.loadPendingPing(OVERSIZED_PING_ID),
+                       "The oversized ping should have been pruned.");
+  Assert.ok(!(yield OS.File.exists(getSavePathForPingId(OVERSIZED_PING_ID))),
+            "The ping should not be on the disk anymore.");
+
+  // Make sure we're correctly updating the related histograms.
+  h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").snapshot();
+  Assert.equal(h.sum, 1, "Telemetry must report 1 oversized ping in the pending pings directory.");
+  h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB").snapshot();
+  Assert.equal(h.counts[2], 1, "Telemetry must report a 2MB, oversized, ping.");
+
+  // Save the ping again to check if it gets pruned when scanning the pings directory.
+  yield TelemetryStorage.savePendingPing(OVERSIZED_PING);
+  expectedPrunedPings.push(OVERSIZED_PING_ID);
+
+  // Scan the pending pings directory.
+  yield TelemetryController.reset();
+  yield TelemetryStorage.testPendingQuotaTaskPromise();
+  yield checkPendingPings();
+
+  // Make sure we're correctly updating the related histograms.
+  h = Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").snapshot();
+  Assert.equal(h.sum, 2, "Telemetry must report 1 oversized ping in the pending pings directory.");
+  h = Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB").snapshot();
+  Assert.equal(h.counts[2], 2, "Telemetry must report two 2MB, oversized, pings.");
+
   Services.prefs.setBoolPref(PREF_FHR_UPLOAD, true);
 });
 
 add_task(function* teardown() {
   yield PingServer.stop();
 });
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -928,16 +928,17 @@ add_task(function* test_dailyCollection(
 });
 
 add_task(function* test_dailyDuplication() {
   if (gIsAndroid) {
     // We don't do daily collections yet on Android.
     return;
   }
 
+  clearPendingPings();
   PingServer.clearRequests();
 
   let schedulerTickCallback = null;
   let now = new Date(2030, 1, 1, 0, 0, 0);
   fakeNow(now);
   // Fake scheduler functions to control daily collection flow in tests.
   fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {});
   yield TelemetrySession.setup();
@@ -1426,16 +1427,18 @@ add_task(function* test_abortedDailyCoal
 add_task(function* test_schedulerComputerSleep() {
   if (gIsAndroid || gIsGonk) {
     // We don't have the aborted session or the daily ping here.
     return;
   }
 
   const ABORTED_FILE = OS.Path.join(DATAREPORTING_PATH, ABORTED_PING_FILE_NAME);
 
+  clearPendingPings();
+  yield TelemetrySend.reset();
   PingServer.clearRequests();
 
   // Remove any aborted-session ping from the previous tests.
   yield OS.File.removeDir(DATAREPORTING_PATH, { ignoreAbsent: true });
 
   // Set a fake current date and start Telemetry.
   let nowDate = new Date(2009, 10, 18, 0, 0, 0);
   fakeNow(nowDate);
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -108,17 +108,16 @@ const PSEUDO_SELECTORS = [
   [":empty", 0],
   [":target", 0],
   [":enabled", 0],
   [":disabled", 0],
   [":checked", 1],
   ["::selection", 0]
 ];
 
-
 let HELPER_SHEET = ".__fx-devtools-hide-shortcut__ { visibility: hidden !important } ";
 HELPER_SHEET += ":-moz-devtools-highlighted { outline: 2px dashed #F06!important; outline-offset: -2px!important } ";
 
 loader.lazyRequireGetter(this, "DevToolsUtils",
                          "devtools/toolkit/DevToolsUtils");
 
 loader.lazyGetter(this, "DOMParser", function() {
   return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
@@ -307,34 +306,30 @@ var NodeActor = exports.NodeActor = prot
   // a call to children() if possible.
   get numChildren() {
     // For pseudo elements, childNodes.length returns 1, but the walker
     // will return 0.
     if (this.isBeforePseudoElement || this.isAfterPseudoElement) {
       return 0;
     }
 
-    let numChildren = this.rawNode.childNodes.length;
+    let rawNode = this.rawNode;
+    let numChildren = rawNode.childNodes.length;
+    let hasAnonChildren = rawNode.nodeType === Ci.nsIDOMNode.ELEMENT_NODE &&
+                          rawNode.ownerDocument.getAnonymousNodes(rawNode);
+
     if (numChildren === 0 &&
-        (this.rawNode.contentDocument || this.rawNode.getSVGDocument)) {
+        (rawNode.contentDocument || rawNode.getSVGDocument)) {
       // This might be an iframe with virtual children.
       numChildren = 1;
     }
 
-    // Count any anonymous children
-    if (this.rawNode.nodeType === Ci.nsIDOMNode.ELEMENT_NODE) {
-      let anonChildren = this.rawNode.ownerDocument.getAnonymousNodes(this.rawNode);
-      if (anonChildren) {
-        numChildren += anonChildren.length;
-      }
-    }
-
-    // Normal counting misses ::before/::after, so we have to check to make sure
-    // we aren't missing anything
-    if (numChildren === 0) {
+    // Normal counting misses ::before/::after.  Also, some anonymous children
+    // may ultimately be skipped, so we have to consult with the walker.
+    if (numChildren === 0 || hasAnonChildren) {
       numChildren = this.walker.children(this).nodes.length;
     }
 
     return numChildren;
   },
 
   get computedStyle() {
     return CssLogic.getComputedStyle(this.rawNode);
@@ -3856,45 +3851,49 @@ DocumentWalker.prototype = {
     let node = this.walker.nextSibling();
     while (node && this.filter(node) === Ci.nsIDOMNodeFilter.FILTER_SKIP) {
       node = this.walker.nextSibling();
     }
     return node;
   }
 };
 
-function isXULElement(el) {
-  return el &&
-         el.namespaceURI === XUL_NS;
+function isInXULDocument(el) {
+  let doc = nodeDocument(el);
+  return doc &&
+         doc.documentElement &&
+         doc.documentElement.namespaceURI === XUL_NS;
 }
 
 /**
  * This DeepTreeWalker filter skips whitespace text nodes and anonymous
  * content with the exception of ::before and ::after and anonymous content
  * in XUL document (needed to show all elements in the browser toolbox).
  */
 function standardTreeWalkerFilter(aNode) {
+  // ::before and ::after are native anonymous content, but we always
+  // want to show them
+  if (aNode.nodeName === "_moz_generated_content_before" ||
+      aNode.nodeName === "_moz_generated_content_after") {
+    return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
+  }
+
   // Ignore empty whitespace text nodes.
   if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
       !/[^\s]/.exec(aNode.nodeValue)) {
     return Ci.nsIDOMNodeFilter.FILTER_SKIP;
   }
 
-  // Ignore all native anonymous content (like internals for form
-  // controls).  Except for:
-  //   1) Anonymous content in a XUL document. This is needed for all
-  //      elements within the Browser Toolbox to properly show up.
-  //   2) ::before/::after - we do want this to show in the walker so
-  //      they can be inspected.
-  if (LayoutHelpers.isNativeAnonymous(aNode) &&
-      !isXULElement(aNode.parentNode) &&
-      (
-        aNode.nodeName !== "_moz_generated_content_before" &&
-        aNode.nodeName !== "_moz_generated_content_after")
-      ) {
+  // Ignore all native and XBL anonymous content inside a non-XUL document
+  if (!isInXULDocument(aNode) && (LayoutHelpers.isXBLAnonymous(aNode) ||
+                                  LayoutHelpers.isNativeAnonymous(aNode))) {
+    // Note: this will skip inspecting the contents of feedSubscribeLine since
+    // that's XUL content injected in an HTML document, but we need to because
+    // this also skips many other elements that need to be skipped - like form
+    // controls, scrollbars, video controls, etc (see bug 1187482).
     return Ci.nsIDOMNodeFilter.FILTER_SKIP;
   }
 
   return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
 }
 
 /**
  * This DeepTreeWalker filter is like standardTreeWalkerFilter except that
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -27,16 +27,17 @@ loader.lazyGetter(this, "Debugger", () =
   hackDebugger(Debugger);
   return Debugger;
 });
 loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
 loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true);
 loader.lazyRequireGetter(this, "CssLogic", "devtools/styleinspector/css-logic", true);
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
+loader.lazyRequireGetter(this, "setTimeout", "sdk/timers", true);
 
 /**
  * A BreakpointActorMap is a map from locations to instances of BreakpointActor.
  */
 function BreakpointActorMap() {
   this._size = 0;
   this._actors = {};
 }
@@ -55,16 +56,23 @@ BreakpointActorMap.prototype = {
   /**
    * Generate all BreakpointActors that match the given location in
    * this BreakpointActorMap.
    *
    * @param OriginalLocation location
    *        The location for which matching BreakpointActors should be generated.
    */
   findActors: function* (location = new OriginalLocation()) {
+    // Fast shortcut for when we know we won't find any actors. Surprisingly
+    // enough, this speeds up refreshing when there are no breakpoints set by
+    // about 2x!
+    if (this.size === 0) {
+      return;
+    }
+
     function* findKeys(object, key) {
       if (key !== undefined) {
         if (key in object) {
           yield key;
         }
       }
       else {
         for (let key of Object.keys(object)) {
@@ -416,16 +424,18 @@ function ThreadActor(aParent, aGlobal)
   this._options = {
     useSourceMaps: false,
     autoBlackBox: false
   };
 
   this.breakpointActorMap = new BreakpointActorMap();
   this.sourceActorStore = new SourceActorStore();
 
+  this._debuggerSourcesSeen = null;
+
   // A map of actorID -> actor for breakpoints created and managed by the
   // server.
   this._hiddenBreakpoints = new Map();
 
   this.global = aGlobal;
 
   this._allEventsListener = this._allEventsListener.bind(this);
   this.onNewGlobal = this.onNewGlobal.bind(this);
@@ -606,16 +616,17 @@ ThreadActor.prototype = {
     }
 
     if (this.state !== "detached") {
       return { error: "wrongState",
                message: "Current state is " + this.state };
     }
 
     this._state = "attached";
+    this._debuggerSourcesSeen = new Set();
 
     update(this._options, aRequest.options || {});
     this.sources.reconfigure(this._options);
     this.sources.on('newSource', (name, source) => {
       this.onNewSource(source);
     });
 
     // Initialize an event loop stack. This can't be done in the constructor,
@@ -653,16 +664,17 @@ ThreadActor.prototype = {
       reportError(e);
       return { error: "notAttached", message: e.toString() };
     }
   },
 
   onDetach: function (aRequest) {
     this.disconnect();
     this._state = "detached";
+    this._debuggerSourcesSeen = null;
 
     dumpn("ThreadActor.prototype.onDetach: returning 'detached' packet");
     return {
       type: "detached"
     };
   },
 
   onReconfigure: function (aRequest) {
@@ -757,32 +769,32 @@ ThreadActor.prototype = {
       error: "notImplemented",
       message: "forced completion is not yet implemented."
     };
   },
 
   _makeOnEnterFrame: function ({ pauseAndRespond }) {
     return aFrame => {
       const generatedLocation = this.sources.getFrameLocation(aFrame);
-      let { originalSourceActor } = this.synchronize(this.sources.getOriginalLocation(
+      let { originalSourceActor } = this.unsafeSynchronize(this.sources.getOriginalLocation(
         generatedLocation));
       let url = originalSourceActor.url;
 
       return this.sources.isBlackBoxed(url)
         ? undefined
         : pauseAndRespond(aFrame);
     };
   },
 
   _makeOnPop: function ({ thread, pauseAndRespond, createValueGrip }) {
     return function (aCompletion) {
       // onPop is called with 'this' set to the current frame.
 
       const generatedLocation = thread.sources.getFrameLocation(this);
-      const { originalSourceActor } = thread.synchronize(thread.sources.getOriginalLocation(
+      const { originalSourceActor } = thread.unsafeSynchronize(thread.sources.getOriginalLocation(
         generatedLocation));
       const url = originalSourceActor.url;
 
       if (thread.sources.isBlackBoxed(url)) {
         return undefined;
       }
 
       // Note that we're popping this frame; we need to watch for
@@ -814,17 +826,17 @@ ThreadActor.prototype = {
       };
     }
 
     // Otherwise take what a "step" means into consideration.
     return function () {
       // onStep is called with 'this' set to the current frame.
 
       const generatedLocation = thread.sources.getFrameLocation(this);
-      const newLocation = thread.synchronize(thread.sources.getOriginalLocation(
+      const newLocation = thread.unsafeSynchronize(thread.sources.getOriginalLocation(
         generatedLocation));
 
       // Cases when we should pause because we have executed enough to consider
       // a "step" to have occured:
       //
       // 1.1. We change frames.
       // 1.2. We change URLs (can happen without changing frames thanks to
       //      source mapping).
@@ -1036,32 +1048,36 @@ ThreadActor.prototype = {
         // packet.
         : error;
     });
   },
 
   /**
    * Spin up a nested event loop so we can synchronously resolve a promise.
    *
+   * DON'T USE THIS UNLESS YOU ABSOLUTELY MUST! Nested event loops suck: the
+   * world's state can change out from underneath your feet because JS is no
+   * longer run-to-completion.
+   *
    * @param aPromise
    *        The promise we want to resolve.
    * @returns The promise's resolution.
    */
-  synchronize: function(aPromise) {
+  unsafeSynchronize: function(aPromise) {
     let needNest = true;
     let eventLoop;
     let returnVal;
 
     aPromise
       .then((aResolvedVal) => {
         needNest = false;
         returnVal = aResolvedVal;
       })
       .then(null, (aError) => {
-        reportError(aError, "Error inside synchronize:");
+        reportError(aError, "Error inside unsafeSynchronize:");
       })
       .then(() => {
         if (eventLoop) {
           eventLoop.resolve();
         }
       });
 
     if (needNest) {
@@ -1313,16 +1329,21 @@ ThreadActor.prototype = {
 
     return all([...sourcesToScripts.values()].map(script => {
       return this.sources.createSourceActors(script.source);
     }));
   },
 
   onSources: function (aRequest) {
     return this._discoverSources().then(() => {
+      // No need to flush the new source packets here, as we are sending the
+      // list of sources out immediately and we don't need to invoke the
+      // overhead of an RDP packet for every source right now. Let the default
+      // timeout flush the buffered packets.
+
       return {
         sources: this.sources.iter().map(s => s.form())
       };
     });
   },
 
   /**
    * Disassociate all breakpoint actors from their scripts and clear the
@@ -1781,17 +1802,17 @@ ThreadActor.prototype = {
    *
    * @param aFrame Debugger.Frame
    *        The stack frame that contained the debugger statement.
    */
   onDebuggerStatement: function (aFrame) {
     // Don't pause if we are currently stepping (in or over) or the frame is
     // black-boxed.
     const generatedLocation = this.sources.getFrameLocation(aFrame);
-    const { originalSourceActor } = this.synchronize(this.sources.getOriginalLocation(
+    const { originalSourceActor } = this.unsafeSynchronize(this.sources.getOriginalLocation(
       generatedLocation));
     const url = originalSourceActor ? originalSourceActor.url : null;
 
     return this.sources.isBlackBoxed(url) || aFrame.onStep
       ? undefined
       : this._pauseAndRespond(aFrame, { type: "debuggerStatement" });
   },
 
@@ -1813,17 +1834,17 @@ ThreadActor.prototype = {
       }
     }
 
     if (willBeCaught && this._options.ignoreCaughtExceptions) {
       return undefined;
     }
 
     const generatedLocation = this.sources.getFrameLocation(aFrame);
-    const { sourceActor } = this.synchronize(this.sources.getOriginalLocation(
+    const { sourceActor } = this.unsafeSynchronize(this.sources.getOriginalLocation(
       generatedLocation));
     const url = sourceActor ? sourceActor.url : null;
 
     if (this.sources.isBlackBoxed(url)) {
       return undefined;
     }
 
     try {
@@ -1851,23 +1872,16 @@ ThreadActor.prototype = {
    * scope of the specified debuggee global.
    *
    * @param aScript Debugger.Script
    *        The source script that has been loaded into a debuggee compartment.
    * @param aGlobal Debugger.Object
    *        A Debugger.Object instance whose referent is the global object.
    */
   onNewScript: function (aScript, aGlobal) {
-    // XXX: The scripts must be added to the ScriptStore before restoring
-    // breakpoints in _addScript. If we try to add them to the ScriptStore
-    // inside _addScript, we can accidentally set a breakpoint in a top level
-    // script as a "closest match" because we wouldn't have added the child
-    // scripts to the ScriptStore yet.
-    this.scripts.addScripts(this.dbg.findScripts({ source: aScript.source }));
-
     this._addSource(aScript.source);
   },
 
   onNewSource: function (aSource) {
     this.conn.send({
       from: this.actorID,
       type: "newSource",
       source: aSource.form()
@@ -1890,40 +1904,45 @@ ThreadActor.prototype = {
   /**
    * Add the provided source to the server cache.
    *
    * @param aSource Debugger.Source
    *        The source that will be stored.
    * @returns true, if the source was added; false otherwise.
    */
   _addSource: function (aSource) {
-    if (!this.sources.allowSource(aSource)) {
+    if (!this.sources.allowSource(aSource) || this._debuggerSourcesSeen.has(aSource)) {
       return false;
     }
 
+    // The scripts must be added to the ScriptStore before restoring
+    // breakpoints. If we try to add them to the ScriptStore any later, we can
+    // accidentally set a breakpoint in a top level script as a "closest match"
+    // because we wouldn't have added the child scripts to the ScriptStore yet.
+    this.scripts.addScripts(this.dbg.findScripts({ source: aSource }));
+
     let sourceActor = this.sources.createNonSourceMappedActor(aSource);
 
     // Go ahead and establish the source actors for this script, which
     // fetches sourcemaps if available and sends onNewSource
     // notifications.
     //
-    // We need to use synchronize here because if the page is being reloaded,
+    // We need to use unsafeSynchronize here because if the page is being reloaded,
     // this call will replace the previous set of source actors for this source
     // with a new one. If the source actors have not been replaced by the time
     // we try to reset the breakpoints below, their location objects will still
     // point to the old set of source actors, which point to different scripts.
-    this.synchronize(this.sources.createSourceActors(aSource));
+    this.unsafeSynchronize(this.sources.createSourceActors(aSource));
 
     // Set any stored breakpoints.
     let promises = [];
 
     for (let _actor of this.breakpointActorMap.findActors()) {
-      // XXX bug 1142115: We do async work in here, so we need to
-      // create a fresh binding because for/of does not yet do that in
-      // SpiderMonkey
+      // XXX bug 1142115: We do async work in here, so we need to create a fresh
+      // binding because for/of does not yet do that in SpiderMonkey.
       let actor = _actor;
 
       if (actor.isPending) {
         promises.push(actor.originalLocation.originalSourceActor._setBreakpoint(actor));
       } else {
         promises.push(this.sources.getAllGeneratedLocations(actor.originalLocation)
                                   .then((generatedLocations) => {
           if (generatedLocations.length > 0 &&
@@ -1933,19 +1952,20 @@ ThreadActor.prototype = {
               generatedLocations
             );
           }
         }));
       }
     }
 
     if (promises.length > 0) {
-      this.synchronize(promise.all(promises));
+      this.unsafeSynchronize(promise.all(promises));
     }
 
+    this._debuggerSourcesSeen.add(aSource);
     return true;
   },
 
 
   /**
    * Get prototypes and properties of multiple objects.
    */
   onPrototypesAndProperties: function (aRequest) {
@@ -3270,17 +3290,17 @@ BreakpointActor.prototype = {
    *
    * @param aFrame Debugger.Frame
    *        The stack frame that contained the breakpoint.
    */
   hit: function (aFrame) {
     // Don't pause if we are currently stepping (in or over) or the frame is
     // black-boxed.
     let generatedLocation = this.threadActor.sources.getFrameLocation(aFrame);
-    let { originalSourceActor } = this.threadActor.synchronize(
+    let { originalSourceActor } = this.threadActor.unsafeSynchronize(
       this.threadActor.sources.getOriginalLocation(generatedLocation));
     let url = originalSourceActor.url;
 
     if (this.threadActor.sources.isBlackBoxed(url)
         || aFrame.onStep) {
       return undefined;
     }
 
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -1704,17 +1704,17 @@ TabActor.prototype = {
       return;
     }
 
     // Proceed normally only if the debuggee is not paused.
     // TODO bug 997119: move that code to ThreadActor by listening to will-navigate
     let threadActor = this.threadActor;
     if (request && threadActor.state == "paused") {
       request.suspend();
-      this.conn.send(threadActor.synchronize(Promise.resolve(threadActor.onResume())));
+      this.conn.send(threadActor.unsafeSynchronize(Promise.resolve(threadActor.onResume())));
       threadActor.dbg.enabled = false;
       this._pendingNavigation = request;
     }
     threadActor.disableAllBreakpoints();
 
     this.conn.send({
       from: this.actorID,
       type: "tabNavigated",
--- a/toolkit/devtools/server/tests/mochitest/test_inspector-anonymous.html
+++ b/toolkit/devtools/server/tests/mochitest/test_inspector-anonymous.html
@@ -49,18 +49,18 @@ window.onload = function() {
   addAsyncTest(function* testXBLAnonymousInHTMLDocument() {
     info ("Testing XBL anonymous in an HTML document.");
     let rawToolbarbutton = gInspectee.createElementNS(XUL_NS, "toolbarbutton");
     gInspectee.documentElement.appendChild(rawToolbarbutton);
 
     let toolbarbutton = yield gWalker.querySelector(gWalker.rootNode, "toolbarbutton");
     let children = yield gWalker.children(toolbarbutton);
 
-    is (toolbarbutton.numChildren, 3, "XBL content is visible even in HTML doc");
-    is (children.nodes.length, 3, "XBL content is returned even in HTML doc");
+    is (toolbarbutton.numChildren, 0, "XBL content is not visible in HTML doc");
+    is (children.nodes.length, 0, "XBL content is not returned in HTML doc");
 
     runNextTest();
   });
 
   addAsyncTest(function* testNativeAnonymous() {
     info ("Testing native anonymous content with walker.");
 
     let select = yield gWalker.querySelector(gWalker.rootNode, "select");
--- a/toolkit/devtools/server/tests/unit/test_nesting-01.js
+++ b/toolkit/devtools/server/tests/unit/test_nesting-01.js
@@ -1,14 +1,14 @@
 /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that we can nest event loops when needed in
-// ThreadActor.prototype.synchronize.
+// ThreadActor.prototype.unsafeSynchronize.
 
 var gClient;
 var gThreadActor;
 
 function run_test() {
   initTestDebuggerServer();
   let gDebuggee = addTestGlobal("test-nesting");
   gClient = new DebuggerClient(DebuggerServer.connectPipe());
@@ -27,22 +27,22 @@ function test_nesting() {
   const thread = gThreadActor;
   const { resolve, reject, promise: p } = promise.defer();
 
   let currentStep = 0;
 
   executeSoon(function () {
     // Should be on the first step
     do_check_eq(++currentStep, 1);
-    // We should have one nested event loop from synchronize
+    // We should have one nested event loop from unsfeSynchronize
     do_check_eq(thread._nestedEventLoops.size, 1);
     resolve(true);
   });
 
-  do_check_eq(thread.synchronize(p), true);
+  do_check_eq(thread.unsafeSynchronize(p), true);
 
   // Should be on the second step
   do_check_eq(++currentStep, 2);
   // There shouldn't be any nested event loops anymore
   do_check_eq(thread._nestedEventLoops.size, 0);
 
   finishClient(gClient);
 }
--- a/toolkit/devtools/server/tests/unit/test_nesting-02.js
+++ b/toolkit/devtools/server/tests/unit/test_nesting-02.js
@@ -9,70 +9,73 @@ var gClient;
 var gThreadActor;
 
 function run_test() {
   initTestDebuggerServer();
   let gDebuggee = addTestGlobal("test-nesting");
   gClient = new DebuggerClient(DebuggerServer.connectPipe());
   gClient.connect(function () {
     attachTestTabAndResume(gClient, "test-nesting", function (aResponse, aTabClient, aThreadClient) {
-      // Reach over the protocol connection and get a reference to the thread actor.
+      // Reach over the protocol connection and get a reference to the thread
+      // actor.
       gThreadActor = aThreadClient._transport._serverConnection.getActor(aThreadClient._actor);
 
       test_nesting();
     });
   });
   do_test_pending();
 }
 
 function test_nesting() {
   const thread = gThreadActor;
   const { resolve, reject, promise: p } = promise.defer();
 
   // The following things should happen (in order):
-  // 1. In the new event loop (created by synchronize)
+  // 1. In the new event loop (created by unsafeSynchronize)
   // 2. Resolve the promise (shouldn't exit any event loops)
-  // 3. Exit the event loop (should also then exit synchronize's event loop)
-  // 4. Be after the synchronize call
+  // 3. Exit the event loop (should also then exit unsafeSynchronize's event loop)
+  // 4. Be after the unsafeSynchronize call
   let currentStep = 0;
 
   executeSoon(function () {
     let eventLoop;
 
     executeSoon(function () {
       // Should be at step 2
       do_check_eq(++currentStep, 2);
-      // Before resolving, should have the synchronize event loop and the one just created.
+      // Before resolving, should have the unsafeSynchronize event loop and the
+      // one just created.
       do_check_eq(thread._nestedEventLoops.size, 2);
 
       executeSoon(function () {
         // Should be at step 3
         do_check_eq(++currentStep, 3);
         // Before exiting the manually created event loop, should have the
-        // synchronize event loop and the manual event loop.
+        // unsafeSynchronize event loop and the manual event loop.
         do_check_eq(thread._nestedEventLoops.size, 2);
         // Should have the event loop
         do_check_true(!!eventLoop);
         eventLoop.resolve();
       });
 
       resolve(true);
-      // Shouldn't exit any event loops because a new one started since the call to synchronize
+      // Shouldn't exit any event loops because a new one started since the call
+      // to unsafeSynchronize
       do_check_eq(thread._nestedEventLoops.size, 2);
     });
 
     // Should be at step 1
     do_check_eq(++currentStep, 1);
-    // Should have only the synchronize event loop
+    // Should have only the unsafeSynchronize event loop
     do_check_eq(thread._nestedEventLoops.size, 1);
     eventLoop = thread._nestedEventLoops.push();
     eventLoop.enter();
   });
 
-  do_check_eq(thread.synchronize(p), true);
+  do_check_eq(thread.unsafeSynchronize(p), true);
 
   // Should be on the fourth step
   do_check_eq(++currentStep, 4);
   // There shouldn't be any nested event loops anymore
   do_check_eq(thread._nestedEventLoops.size, 0);
 
   finishClient(gClient);
 }