merge fx-team to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 17 Jun 2016 00:30:21 +0100
changeset 301997 05873df0c0c268e419a714b8a6204cb852049098
parent 301985 b9f4f38063951cd5a8b249911aea61869f40fd1f (current diff)
parent 301996 942a42c22de790ca091f1e329250ef11217636bc (diff)
child 301998 5f95858f8ddf21ea2271a12810332efd09eff138
push id78533
push usercbook@mozilla.com
push dateFri, 17 Jun 2016 00:03:15 +0000
treeherdermozilla-inbound@759277e45f04 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.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/shared/download-blocked.svg
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -276,17 +276,17 @@ function securityOnLoad(uri, windowInfo)
     msg2 = pkiBundle.getString("pageInfo_Privacy_Encrypted2");
     security._cert = info.cert;
   }
   else {
     hdr = pkiBundle.getString("pageInfo_NoEncryption");
     if (info.hostName != null)
       msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [info.hostName]);
     else
-      msg1 = pkiBundle.getString("pageInfo_Privacy_None3");
+      msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
     msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
   }
   setText("security-technical-shortform", hdr);
   setText("security-technical-longform1", msg1);
   setText("security-technical-longform2", msg2);
 }
 
 function setText(id, value)
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -86,17 +86,17 @@ function IsLivemark(aItemId) {
 let InternalFaviconLoader = {
   /**
    * This gets called for every inner window that is destroyed.
    * In the parent process, we process the destruction ourselves. In the child process,
    * we notify the parent which will then process it based on that message.
    */
   observe(subject, topic, data) {
     let innerWindowID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
-    this.onInnerDestroyed(innerWindowID);
+    this.removeRequestsForInner(innerWindowID);
   },
 
   /**
    * Actually cancel the request, and clear the timeout for cancelling it.
    */
   _cancelRequest({uri, innerWindowID, timerID, callback}, reason) {
     // Break cycle
     let request = callback.request;
@@ -108,22 +108,22 @@ let InternalFaviconLoader = {
     } catch (ex) {
       Cu.reportError("When cancelling a request for " + uri.spec + " because " + reason + ", it was already canceled!");
     }
   },
 
   /**
    * Called for every inner that gets destroyed, only in the parent process.
    */
-  onInnerDestroyed(innerID) {
+  removeRequestsForInner(innerID) {
     for (let [window, loadDataForWindow] of gFaviconLoadDataMap) {
       let newLoadDataForWindow = loadDataForWindow.filter(loadData => {
         let innerWasDestroyed = loadData.innerWindowID == innerID;
         if (innerWasDestroyed) {
-          this._cancelRequest(loadData, "the inner window was destroyed");
+          this._cancelRequest(loadData, "the inner window was destroyed or a new favicon was loaded for it");
         }
         // Keep the items whose inner is still alive.
         return !innerWasDestroyed;
       });
       // Map iteration with for...of is safe against modification, so
       // now just replace the old value:
       gFaviconLoadDataMap.set(window, newLoadDataForWindow);
     }
@@ -175,38 +175,42 @@ let InternalFaviconLoader = {
   ensureInitialized() {
     if (this._initialized) {
       return;
     }
     this._initialized = true;
 
     Services.obs.addObserver(this, "inner-window-destroyed", false);
     Services.ppmm.addMessageListener("Toolkit:inner-window-destroyed", msg => {
-      this.onInnerDestroyed(msg.data);
+      this.removeRequestsForInner(msg.data);
     });
   },
 
   loadFavicon(browser, principal, uri) {
     this.ensureInitialized();
     let win = browser.ownerDocument.defaultView;
     if (!gFaviconLoadDataMap.has(win)) {
       gFaviconLoadDataMap.set(win, []);
       let unloadHandler = event => {
         let doc = event.target;
-        let eventWin = doc.defaultview;
-        if (win == win.top && doc.documentURI != "about:blank") {
+        let eventWin = doc.defaultView;
+        if (eventWin == win) {
           win.removeEventListener("unload", unloadHandler);
           this.onUnload(win);
         }
       };
       win.addEventListener("unload", unloadHandler, true);
     }
 
+    let {innerWindowID, currentURI} = browser;
+
+    // Immediately cancel any earlier requests
+    this.removeRequestsForInner(innerWindowID);
+
     // First we do the actual setAndFetch call:
-    let {innerWindowID, currentURI} = browser;
     let loadType = PrivateBrowsingUtils.isWindowPrivate(win)
       ? PlacesUtils.favicons.FAVICON_LOAD_PRIVATE
       : PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE;
     let callback = this._makeCompletionCallback(win, innerWindowID);
     let request = PlacesUtils.favicons.setAndFetchFaviconForPage(currentURI, uri, false,
                                                                  loadType, callback, principal);
 
     // Now register the result so we can cancel it if/when necessary.
--- a/browser/themes/osx/devedition.css
+++ b/browser/themes/osx/devedition.css
@@ -27,16 +27,26 @@
 }
 
 /* Resize things so that the native titlebar is in line with the tabs */
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-buttonbox-container,
 #main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
   margin-top: 6px;
 }
 
+/* In private windows, the #titlebar-content is higher because of the
+ * private browsing indicator. With the margin-top the titlebar buttons
+ * align to the top correctly in that case, but only if we don't stretch
+ * the box they're in because the container is too high, so we override
+ * the default alignment value (stretch).
+ */
+#main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-buttonbox-container {
+  -moz-box-align: start;
+}
+
 /* Square back and forward buttons.  Need !important on these because there
    are a lot of more specific selectors sprinkled around elsewhere for changing
    background / shadows for different states */
 #back-button,
 #forward-button {
   height: 24px !important;
   box-shadow: none !important;
   border: 1px solid var(--chrome-nav-bar-controls-border-color) !important;
deleted file mode 100644
--- a/browser/themes/shared/download-blocked.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    circle {
-      fill: #D92215;
-    }
-
-    rect {
-      fill: #fff;
-    }
-  </style>
-
-  <circle cx="8" cy="8" r="8" />
-  <rect x="3" y="6" width="10" height="4" rx=".5" ry=".5" />
-</svg>
--- a/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css
+++ b/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css
@@ -50,17 +50,17 @@
     margin-inline-start: 8px;
   }
 }
 %endif
 
 .blockedIcon {
   --overlay-image-dimensions: top right / 16px no-repeat;
   padding: 0;
-  background: url("chrome://browser/skin/download-blocked.svg") var(--overlay-image-dimensions);
+  background: url("chrome://browser/skin/downloads/download-blocked.svg") var(--overlay-image-dimensions);
 }
 
 @item@[verdict="PotentiallyUnwanted"] .blockedIcon {
   background: url("chrome://browser/skin/warning.svg") var(--overlay-image-dimensions);
 }
 
 @item@[verdict="Uncommon"] .blockedIcon {
   background: url("chrome://browser/skin/info.svg") var(--overlay-image-dimensions);
--- a/browser/themes/shared/downloads/downloads.inc.css
+++ b/browser/themes/shared/downloads/downloads.inc.css
@@ -99,17 +99,17 @@ richlistitem[type="download"]:last-child
   width: calc(var(--icon-size) + var(--inline-offset));
   height: calc(var(--icon-size) + var(--block-offset));
   padding: var(--block-offset) var(--inline-offset) 0 0;
 }
 
 .blockedIcon {
   --overlay-image-dimensions: top right / 16px no-repeat;
   padding: 0;
-  background: url("chrome://browser/skin/download-blocked.svg") var(--overlay-image-dimensions);
+  background: url("chrome://browser/skin/downloads/download-blocked.svg") var(--overlay-image-dimensions);
 }
 
 @item@[verdict="PotentiallyUnwanted"] .blockedIcon {
   background: url("chrome://browser/skin/warning.svg") var(--overlay-image-dimensions);
 }
 
 @item@[verdict="Uncommon"] .blockedIcon {
   background: url("chrome://browser/skin/info.svg") var(--overlay-image-dimensions);
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -46,17 +46,16 @@
   skin/classic/browser/customizableui/panelarrow-customizeTip.png  (../shared/customizableui/panelarrow-customizeTip.png)
   skin/classic/browser/customizableui/panelarrow-customizeTip@2x.png  (../shared/customizableui/panelarrow-customizeTip@2x.png)
   skin/classic/browser/customizableui/subView-arrow-back-inverted.png  (../shared/customizableui/subView-arrow-back-inverted.png)
   skin/classic/browser/customizableui/subView-arrow-back-inverted@2x.png  (../shared/customizableui/subView-arrow-back-inverted@2x.png)
   skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl.png  (../shared/customizableui/subView-arrow-back-inverted-rtl.png)
   skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl@2x.png  (../shared/customizableui/subView-arrow-back-inverted-rtl@2x.png)
   skin/classic/browser/customizableui/whimsy.png               (../shared/customizableui/whimsy.png)
   skin/classic/browser/customizableui/whimsy@2x.png            (../shared/customizableui/whimsy@2x.png)
-  skin/classic/browser/download-blocked.svg                    (../shared/download-blocked.svg)
   skin/classic/browser/downloads/contentAreaDownloadsView.css  (../shared/downloads/contentAreaDownloadsView.css)
   skin/classic/browser/downloads/download-blocked.svg          (../shared/downloads/download-blocked.svg)
   skin/classic/browser/drm-icon.svg                            (../shared/drm-icon.svg)
   skin/classic/browser/filters.svg                             (../shared/filters.svg)
   skin/classic/browser/fullscreen/insecure.svg                 (../shared/fullscreen/insecure.svg)
   skin/classic/browser/fullscreen/secure.svg                   (../shared/fullscreen/secure.svg)
   skin/classic/browser/glyphs.svg                              (../shared/glyphs.svg)
   skin/classic/browser/heartbeat-icon.svg                      (../shared/heartbeat-icon.svg)
--- a/devtools/client/inspector/test/browser_inspector_highlighter-keybinding_03.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-keybinding_03.js
@@ -1,40 +1,49 @@
 /* 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/. */
 
 "use strict";
 
 // Test that the keybindings for Picker work alright
 
+const IS_OSX = Services.appinfo.OS === "Darwin";
 const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
 
 add_task(function* () {
   let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
 
   yield startPicker(toolbox);
-
-  info("Selecting the #another DIV");
   yield moveMouseOver("#another");
 
-  // Testing pick-node shortcut
   info("Testing enter/return key as pick-node command");
   yield doKeyPick({key: "VK_RETURN", options: {}});
   is(inspector.selection.nodeFront.id, "another",
      "The #another node was selected. Passed.");
 
-  // Testing cancel-picker command
+  info("Testing escape key as cancel-picker command");
   yield startPicker(toolbox);
+  yield moveMouseOver("#ahoy");
+  yield doKeyStop({key: "VK_ESCAPE", options: {}});
+  is(inspector.selection.nodeFront.id, "another",
+     "The #another DIV is still selected. Passed.");
 
-  info("Selecting the ahoy DIV");
+  info("Testing Ctrl+Shift+C shortcut as cancel-picker command");
+  yield startPicker(toolbox);
   yield moveMouseOver("#ahoy");
-
-  info("Testing escape key as cancel-picker command");
-  yield doKeyStop({key: "VK_ESCAPE", options: {}});
+  let shortcutOpts = {key: "VK_C", options: {}};
+  if (IS_OSX) {
+    shortcutOpts.options.metaKey = true;
+    shortcutOpts.options.altKey = true;
+  } else {
+    shortcutOpts.options.ctrlKey = true;
+    shortcutOpts.options.shiftKey = true;
+  }
+  yield doKeyStop(shortcutOpts);
   is(inspector.selection.nodeFront.id, "another",
      "The #another DIV is still selected. Passed.");
 
   function doKeyPick(args) {
     info("Key pressed. Waiting for element to be picked");
     testActor.synthesizeKey(args);
     return promise.all([
       toolbox.selection.once("new-node-front"),
--- a/devtools/server/actors/highlighters.js
+++ b/devtools/server/actors/highlighters.js
@@ -5,22 +5,24 @@
 "use strict";
 
 const { Ci } = require("chrome");
 
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const EventEmitter = require("devtools/shared/event-emitter");
 const events = require("sdk/event/core");
 const protocol = require("devtools/shared/protocol");
+const Services = require("Services");
 const { isWindowIncluded } = require("devtools/shared/layout/utils");
 const { highlighterSpec, customHighlighterSpec } = require("devtools/shared/specs/highlighters");
 const { isXUL, isNodeValid } = require("./highlighters/utils/markup");
 const { SimpleOutlineHighlighter } = require("./highlighters/simple-outline");
 
 const HIGHLIGHTER_PICKED_TIMER = 1000;
+const IS_OSX = Services.appinfo.OS === "Darwin";
 
 /**
  * The registration mechanism for highlighters provide a quick way to
  * have modular highlighters, instead of a hard coded list.
  * It allow us to split highlighers in sub modules, and add them dynamically
  * using add-on (useful for 3rd party developers, or prototyping)
  *
  * Note that currently, highlighters added using add-ons, can only work on
@@ -286,17 +288,17 @@ var HighlighterActor = exports.Highlight
 
       let currentNode = this._currentNode.node.rawNode;
 
       /**
        * KEY: Action/scope
        * LEFT_KEY: wider or parent
        * RIGHT_KEY: narrower or child
        * ENTER/CARRIAGE_RETURN: Picks currentNode
-       * ESC: Cancels picker, picks currentNode
+       * ESC/CTRL+SHIFT+C: Cancels picker, picks currentNode
        */
       switch (event.keyCode) {
         // Wider.
         case Ci.nsIDOMKeyEvent.DOM_VK_LEFT:
           if (!currentNode.parentElement) {
             return;
           }
           currentNode = currentNode.parentElement;
@@ -327,17 +329,23 @@ var HighlighterActor = exports.Highlight
           this._onPick(event);
           return;
 
         // Cancel pick mode.
         case Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE:
           this.cancelPick();
           events.emit(this._walker, "picker-node-canceled");
           return;
-
+        case Ci.nsIDOMKeyEvent.DOM_VK_C:
+          if ((IS_OSX && event.metaKey && event.altKey) ||
+            (!IS_OSX && event.ctrlKey && event.shiftKey)) {
+            this.cancelPick();
+            events.emit(this._walker, "picker-node-canceled");
+            return;
+          }
         default: return;
       }
 
       // Store currently attached element
       this._currentNode = this._walker.attachElement(currentNode);
       this._highlighter.show(this._currentNode.node.rawNode);
       events.emit(this._walker, "picker-node-hovered", this._currentNode);
     };
--- a/security/manager/locales/en-US/chrome/pippki/pippki.properties
+++ b/security/manager/locales/en-US/chrome/pippki/pippki.properties
@@ -54,17 +54,17 @@ certNotVerified_Unknown=Could not verify
 #Client auth
 clientAuthMessage1=Organization: ā€œ%Sā€
 clientAuthMessage2=Issued Under: ā€œ%Sā€
 
 #Page Info
 pageInfo_NoEncryption=Connection Not Encrypted
 pageInfo_Privacy_None1=The website %S does not support encryption for the page you are viewing.
 pageInfo_Privacy_None2=Information sent over the Internet without encryption can be seen by other people while it is in transit. 
-pageInfo_Privacy_None3=The page you are viewing is not encrypted.
+pageInfo_Privacy_None4=The page you are viewing was not encrypted before being transmitted over the Internet.
 # LOCALIZATION NOTE (pageInfo_EncryptionWithBitsAndProtocol and pageInfo_BrokenEncryption):
 # %1$S is the name of the encryption standard,
 # %2$S is the key size of the cipher.
 # %3$S is protocol version like "SSL 3" or "TLS 1.2"
 pageInfo_EncryptionWithBitsAndProtocol=Connection Encrypted (%1$S, %2$S bit keys, %3$S)
 pageInfo_BrokenEncryption=Broken Encryption (%1$S, %2$S bit keys, %3$S)
 pageInfo_Privacy_Encrypted1=The page you are viewing was encrypted before being transmitted over the Internet.
 pageInfo_Privacy_Encrypted2=Encryption makes it difficult for unauthorized people to view information traveling between computers. It is therefore unlikely that anyone read this page as it traveled across the network.
--- a/toolkit/components/extensions/ext-webNavigation.js
+++ b/toolkit/components/extensions/ext-webNavigation.js
@@ -116,16 +116,20 @@ function WebNavigationEventManager(conte
 
       let data2 = {
         url: data.url,
         timeStamp: Date.now(),
         frameId: ExtensionManagement.getFrameId(data.windowId),
         parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId),
       };
 
+      if (eventName == "onErrorOccurred") {
+        data2.error = data.error;
+      }
+
       // Fills in tabId typically.
       let result = {};
       extensions.emit("fill-browser-data", data.browser, data2, result);
       if (result.cancel) {
         return;
       }
 
       fillTransitionProperties(eventName, data, data2);
--- a/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webnavigation.html
@@ -77,16 +77,17 @@ const REDIRECT = BASE + "/redirection.sj
 const REDIRECTED = BASE + "/dummy_page.html";
 const CLIENT_REDIRECT = BASE + "/file_webNavigation_clientRedirect.html";
 const CLIENT_REDIRECT_HTTPHEADER = BASE + "/file_webNavigation_clientRedirect_httpHeaders.html";
 const FRAME_CLIENT_REDIRECT = BASE + "/file_webNavigation_frameClientRedirect.html";
 const FRAME_REDIRECT = BASE + "/file_webNavigation_frameRedirect.html";
 const FRAME_MANUAL = BASE + "/file_webNavigation_manualSubframe.html";
 const FRAME_MANUAL_PAGE1 = BASE + "/file_webNavigation_manualSubframe_page1.html";
 const FRAME_MANUAL_PAGE2 = BASE + "/file_webNavigation_manualSubframe_page2.html";
+const INVALID_PAGE = "https://invalid.localhost/";
 
 const REQUIRED = [
   "onBeforeNavigate",
   "onCommitted",
   "onDOMContentLoaded",
   "onCompleted",
 ];
 
@@ -490,12 +491,67 @@ add_task(function* webnav_ordering() {
     info(`Received ${expectedEvent} from ${waitURL} - history.forward() from ${fromURL} to ${waitURL}`);
   }
 
   win.close();
 
   yield extension.unload();
   info("webnavigation extension unloaded");
 });
+
+add_task(function* webnav_error_event() {
+  function backgroundScriptErrorEvent() {
+    browser.webNavigation.onErrorOccurred.addListener((details) => {
+      browser.test.log(`Got onErrorOccurred ${details.url} ${details.error}`);
+
+      browser.test.sendMessage("received", {url: details.url, details, event: "onErrorOccurred"});
+    });
+
+    browser.test.sendMessage("ready");
+  }
+
+  let extensionData = {
+    manifest: {
+      permissions: [
+        "webNavigation",
+      ],
+    },
+    background: `(${backgroundScriptErrorEvent})()`,
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
+
+  extension.onMessage("received", ({url, event, details}) => {
+    received.push({url, event, details});
+
+    if (event == waitingEvent && url == waitingURL) {
+      completedResolve();
+    }
+  });
+
+  yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
+  info("webnavigation extension loaded");
+
+  let win = window.open();
+
+  received = [];
+  yield loadAndWait(win, "onErrorOccurred", INVALID_PAGE, () => { win.location = INVALID_PAGE; });
+
+  let found = received.find((data) => (data.event == "onErrorOccurred" &&
+                                   data.url == INVALID_PAGE));
+
+  ok(found, "Got the onErrorOccurred event");
+
+  if (found) {
+    ok(found.details.error.match(/Error code [0-9]+/),
+      "Got the expected error string in the onErrorOccurred event");
+  }
+
+  // cleanup phase
+  win.close();
+
+  yield extension.unload();
+  info("webnavigation extension unloaded");
+});
 </script>
 
 </body>
 </html>
--- a/toolkit/components/places/FaviconHelpers.cpp
+++ b/toolkit/components/places/FaviconHelpers.cpp
@@ -436,17 +436,21 @@ AsyncFetchAndSetIconForPage::FetchFromNe
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(channel);
   if (priorityChannel) {
     priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST);
   }
 
-  return channel->AsyncOpen2(this);
+  rv = channel->AsyncOpen2(this);
+  if (NS_SUCCEEDED(rv)) {
+    mRequest = channel;
+  }
+  return rv;
 }
 
 NS_IMETHODIMP
 AsyncFetchAndSetIconForPage::Cancel()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mCanceled) {
     return NS_ERROR_UNEXPECTED;
@@ -457,16 +461,19 @@ AsyncFetchAndSetIconForPage::Cancel()
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AsyncFetchAndSetIconForPage::OnStartRequest(nsIRequest* aRequest,
                                             nsISupports* aContext)
 {
+  // mRequest should already be set from ::FetchFromNetwork, but in the case of
+  // a redirect we might get a new request, and we should make sure we keep a
+  // reference to the most current request.
   mRequest = aRequest;
   if (mCanceled) {
     mRequest->Cancel(NS_BINDING_ABORTED);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -1413,41 +1413,16 @@ this.PlacesUtils = {
 
       deferred.resolve(charset);
     }, Ci.nsIThread.DISPATCH_NORMAL);
 
     return deferred.promise;
   },
 
   /**
-   * Promised wrapper for mozIAsyncHistory::updatePlaces for a single place.
-   *
-   * @param aPlaces
-   *        a single mozIPlaceInfo object
-   * @resolves {Promise}
-   */
-  promiseUpdatePlace: function PU_promiseUpdatePlaces(aPlace) {
-    let deferred = Promise.defer();
-    PlacesUtils.asyncHistory.updatePlaces(aPlace, {
-      _placeInfo: null,
-      handleResult: function handleResult(aPlaceInfo) {
-        this._placeInfo = aPlaceInfo;
-      },
-      handleError: function handleError(aResultCode, aPlaceInfo) {
-        deferred.reject(new Components.Exception("Error", aResultCode));
-      },
-      handleCompletion: function() {
-        deferred.resolve(this._placeInfo);
-      }
-    });
-
-    return deferred.promise;
-  },
-
-  /**
    * Promised wrapper for mozIAsyncHistory::getPlacesInfo for a single place.
    *
    * @param aPlaceIdentifier
    *        either an nsIURI or a GUID (@see getPlacesInfo)
    * @resolves to the place info object handed to handleResult.
    */
   promisePlaceInfo: function PU_promisePlaceInfo(aPlaceIdentifier) {
     let deferred = Promise.defer();
--- a/toolkit/modules/FinderHighlighter.jsm
+++ b/toolkit/modules/FinderHighlighter.jsm
@@ -39,33 +39,47 @@ const kModalStyle = `
   margin-inline-end: 0;
   margin-bottom: 0;
   margin-inline-start: -3px;
   padding-top: 2px;
   padding-inline-end: 2px;
   padding-bottom: 0;
   padding-inline-start: 4px;
   pointer-events: none;
+  z-index: 2;
 }
 
 .findbar-modalHighlight-outline[grow] {
   transform: scaleX(1.5) scaleY(1.5)
 }
 
 .findbar-modalHighlight-outline[hidden] {
   opacity: 0;
   display: -moz-box;
 }
 
 .findbar-modalHighlight-outline:not([disable-transitions]) {
   transition-property: opacity, transform, top, left;
   transition-duration: 50ms;
   transition-timing-function: linear;
+}
+
+.findbar-modalHighlight-outlineMask {
+  background: #000;
+  mix-blend-mode: multiply;
+  opacity: .2;
+  position: absolute;
+  z-index: 1;
+}
+
+.findbar-modalHighlight-rect {
+  background: #fff;
+  border: 1px solid #666;
+  position: absolute;
 }`;
-const kSVGNS = "http://www.w3.org/2000/svg";
 const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 /**
  * FinderHighlighter class that is used by Finder.jsm to take care of the
  * 'Highlight All' feature, which can highlight all find occurrences in a page.
  *
  * @param {Finder} finder Finder.jsm instance
  */
@@ -492,20 +506,19 @@ FinderHighlighter.prototype = {
    */
   _modalHighlight(range, controller, window) {
     if (!this._getRangeContentArray(range).length)
       return;
 
     let rects = new Set();
     // Absolute positions should include the viewport scroll offset.
     let { scrollX, scrollY } = this._getScrollPosition(window);
-    // A range may consist of multiple rectangles, but since we're cutting them
-    // out using SVG we can also do these kind of precise cut-outs.
-    // range.getBoundingClientRect() returns the fully encompassing rectangle,
-    // which is too much for our purpose here.
+    // A range may consist of multiple rectangles, we can also do these kind of
+    // precise cut-outs. range.getBoundingClientRect() returns the fully
+    // encompassing rectangle, which is too much for our purpose here.
     for (let dims of range.getClientRects()) {
       rects.add({
         height: dims.bottom - dims.top,
         width: dims.right - dims.left,
         y: dims.top + scrollY,
         x: dims.left + scrollX
       });
     }
@@ -564,60 +577,52 @@ FinderHighlighter.prototype = {
 
     this._repaintHighlightAllMask(window);
 
     this._modalHighlightOutline = document.insertAnonymousContent(container);
     return this._modalHighlightOutline;
   },
 
   /**
-   * Build and draw the SVG mask that takes care of the dimmed background that
+   * Build and draw the mask that takes care of the dimmed background that
    * overlays the current page and the mask that cuts out all the rectangles of
    * the ranges that were found.
    *
    * @param {nsIDOMWindow} window Window to draw in.
    */
   _repaintHighlightAllMask(window) {
     let document = window.document;
 
     const kMaskId = kModalIdPrefix + "-findbar-modalHighlight-outlineMask";
-    let svgNode = document.createElementNS(kSVGNS, "svg");
-    // Make sure the SVG drawing takes the full width and height that's available.
-    let {width, height} = this._getWindowDimensions(window);
-    svgNode.setAttribute("viewBox", "0 0 " + width + " " + height);
+    let maskNode = document.createElement("div");
 
-    // The mask functions as a sort of inverse clip-path: instead of defining
-    // what to draw where, we need to do the opposite. We want the rectangles for
-    // each found range to be cut out of the dimmed-black background. That's why
-    // the mask is a full white large rectangle with small black rectangles that
-    // specifies where to let color bleed through and how much.
-    let svgContent = [`<mask id="${kMaskId}">
-      <rect x="0" y="0" height="${height}" width="${width}" fill="white"/>`];
+    // Make sure the dimmed mask node takes the full width and height that's available.
+    let {width, height} = this._getWindowDimensions(window);
+    maskNode.setAttribute("id", kMaskId);
+    maskNode.setAttribute("class", kMaskId);
+    maskNode.setAttribute("style", `width: ${width}px; height: ${height}px;`);
 
+    // Create a DOM node for each rectangle representing the ranges we found.
+    let maskContent = [];
+    const kRectClassName = kModalIdPrefix + "-findbar-modalHighlight-rect";
     if (this._modalHighlightRectsMap) {
       for (let rects of this._modalHighlightRectsMap.values()) {
         for (let rect of rects) {
-          // The #666 stroke works to create the effect of blurred edges.
-          svgContent.push(`<rect x="${rect.x}" y="${rect.y}"
-            height="${rect.height}" width="${rect.width}"
-            style="fill: #000; stroke-width: 1; stroke: #666"/>`);
+          maskContent.push(`<div class="${kRectClassName}" style="top: ${rect.y}px;
+            left: ${rect.x}px; height: ${rect.height}px; width: ${rect.width}px;"></div>`);
         }
       }
     }
-
-    // The big black opaque rectangle to which the mask is applied.
-    svgNode.innerHTML = svgContent.join("") + `</mask>
-      <rect x="0" y="0" height="${height}" width="${width}" fill="rgba(0,0,0,.2)"
-            mask="url(#${kMaskId})"/>`;
+    maskNode.innerHTML = maskContent.join("");
 
     // Always remove the current mask and insert it a-fresh, because we're not
     // free to alter DOM nodes inside the CanvasFrame.
     this._removeHighlightAllMask(window);
 
-    this._modalHighlightAllMask = document.insertAnonymousContent(svgNode);
+    this._modalHighlightAllMask = document.insertAnonymousContent(maskNode);
   },
 
   /**
    * Safely remove the mask AnoymousContent node from the CanvasFrame.
    *
    * @param {nsIDOMWindow} window
    */
   _removeHighlightAllMask(window) {
--- a/toolkit/modules/tests/browser/browser_FinderHighlighter.js
+++ b/toolkit/modules/tests/browser/browser_FinderHighlighter.js
@@ -77,24 +77,23 @@ function promiseTestHighlighterOutput(br
 
         Assert.equal(callCounts.insertCalls.length, expectedResult.insertCalls,
           `Insert calls should match for '${word}'.`);
         Assert.equal(callCounts.removeCalls.length, expectedResult.removeCalls,
           `Remove calls should match for '${word}'.`);
 
         // We reached the amount of calls we expected, so now we can check
         // the amount of rects.
-        let lastSVGNode = callCounts.insertCalls.pop();
-        if (!lastSVGNode && expectedResult.rectCount !== 0) {
-          Assert.ok(false, `No SVG node found, but expected ${expectedResult.rectCount} rects.`);
+        let lastMaskNode = callCounts.insertCalls.pop();
+        if (!lastMaskNode && expectedResult.rectCount !== 0) {
+          Assert.ok(false, `No mask node found, but expected ${expectedResult.rectCount} rects.`);
         }
-        if (lastSVGNode) {
-          Assert.equal(lastSVGNode.getElementsByTagName("mask")[0]
-            .getElementsByTagName("rect").length, expectedResult.rectCount,
-            `Amount of inserted rects should match for '${word}'.`);
+        if (lastMaskNode) {
+          Assert.equal(lastMaskNode.getElementsByTagName("div").length,
+            expectedResult.rectCount, `Amount of inserted rects should match for '${word}'.`);
         }
 
         resolve();
       }
 
       // Create a function that will stub the original version and collects
       // the arguments so we can check the results later.
       function stub(which) {
@@ -118,32 +117,32 @@ add_task(function* setup() {
     ["findbar.modalHighlight", true]
   ]});
 });
 
 // Test the results of modal highlighting, which is on by default.
 add_task(function* testModalResults() {
   let tests = new Map([
     ["mo", {
-      rectCount: 5,
+      rectCount: 4,
       insertCalls: 2,
       removeCalls: AppConstants.platform == "linux" ? 1 : 2
     }],
     ["m", {
-      rectCount: 9,
+      rectCount: 8,
       insertCalls: 1,
       removeCalls: 1
     }],
     ["new", {
-      rectCount: 2,
+      rectCount: 1,
       insertCalls: 1,
       removeCalls: 1
     }],
     ["o", {
-      rectCount: 1218,
+      rectCount: 1217,
       insertCalls: 1,
       removeCalls: 1
     }]
   ]);
   yield BrowserTestUtils.withNewTab("about:mozilla", function* (browser) {
     // We're inserting 1200 additional o's at the end of the document.
     yield ContentTask.spawn(browser, null, function* () {
       let document = content.document;
@@ -172,17 +171,17 @@ add_task(function* testModalSwitching() 
   yield BrowserTestUtils.withNewTab("about:mozilla", function* (browser) {
     let findbar = gBrowser.getFindBar();
 
     yield promiseOpenFindbar(findbar);
     Assert.ok(!findbar.hidden, "Findbar should be open now.");
 
     let word = "mo";
     let expectedResult = {
-      rectCount: 5,
+      rectCount: 4,
       insertCalls: 2,
       removeCalls: AppConstants.platform == "linux" ? 1 : 2
     };
     let promise = promiseTestHighlighterOutput(browser, word, expectedResult);
     yield promiseEnterStringIntoFindField(findbar, word);
     yield promise;
 
     yield SpecialPowers.pushPrefEnv({ "set": [[ kPrefModalHighlight, false ]] });