merge fx-team to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 12 May 2015 12:01:27 +0200
changeset 243528 0ca37b3cb73dcc1f087e4fea3193900efb423136
parent 243479 8b64c75b0b86b63acc48d2cafb49bcfe79a3426e (current diff)
parent 243527 21158b018fb9fcc384c57f20cbd227a344832a1e (diff)
child 243529 2ad2f521c01704384b2f5c7cf1dbfa7a452cc8e2
push id59683
push usercbook@mozilla.com
push dateTue, 12 May 2015 15:13:10 +0000
treeherdermozilla-inbound@ed36c6879944 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone41.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/base/content/sync/progress.js
browser/base/content/sync/progress.xhtml
browser/base/content/test/general/browser_aboutSyncProgress.js
browser/locales/en-US/chrome/browser/syncProgress.dtd
browser/locales/jar.mn
browser/themes/linux/syncProgress.css
browser/themes/osx/syncProgress.css
browser/themes/windows/syncProgress.css
toolkit/components/viewsource/test/browser/browser_viewsourceprefs_nonhtml.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1912,9 +1912,9 @@ pref("reader.parse-node-limit", 0);
 pref("dom.serviceWorkers.enabled", true);
 #endif
 
 pref("browser.pocket.enabled", true);
 pref("browser.pocket.api", "api.getpocket.com");
 pref("browser.pocket.site", "getpocket.com");
 pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
 pref("browser.pocket.useLocaleList", true);
-pref("browser.pocket.enabledLocales", "en-US");
+pref("browser.pocket.enabledLocales", "en-US de es-ES ja ru");
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -372,17 +372,17 @@
                 label="&viewPartialSourceForMathMLCmd.label;"
                 accesskey="&viewPartialSourceCmd.accesskey;"
                 oncommand="gContextMenu.viewPartialSource('mathml');"
                 observes="isImage"/>
       <menuseparator id="context-sep-viewsource"/>
       <menuitem id="context-viewsource"
                 label="&viewPageSourceCmd.label;"
                 accesskey="&viewPageSourceCmd.accesskey;"
-                oncommand="BrowserViewSourceOfDocument(gContextMenu.browser.contentDocumentAsCPOW);"
+                oncommand="BrowserViewSource(gContextMenu.browser);"
                 observes="isImage"/>
       <menuitem id="context-viewinfo"
                 label="&viewPageInfoCmd.label;"
                 accesskey="&viewPageInfoCmd.accesskey;"
                 oncommand="gContextMenu.viewInfo();"/>
       <menuseparator id="spell-separator"/>
       <menuitem id="spell-check-enabled"
                 label="&spellCheckToggle.label;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -33,17 +33,17 @@
     <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()" reserved="true"/>
     <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()" reserved="true"/>
     <command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
     <command id="cmd_quitApplication" oncommand="goQuitApplication()" reserved="true"/>
 
 
     <commandset id="editMenuCommands"/>
 
-    <command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(window.gBrowser.selectedBrowser.contentDocumentAsCPOW);" observes="isImage"/>
+    <command id="View:PageSource" oncommand="BrowserViewSource(window.gBrowser.selectedBrowser);" observes="isImage"/>
     <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
     <command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
     <command id="View:ReaderView" oncommand="ReaderParent.toggleReaderMode(event);"/>
     <command id="cmd_find"
              oncommand="gFindBar.onFindCommand();"
              observes="isImage"/>
     <command id="cmd_findAgain"
              oncommand="gFindBar.onFindAgainCommand(false);"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2277,72 +2277,81 @@ function readFromClipboard()
       url = data.data.substring(0, dataLen.value / 2);
     }
   } catch (ex) {
   }
 
   return url;
 }
 
-function BrowserViewSourceOfDocument(aDocument)
-{
-  var pageCookie;
-  var webNav;
-
-  // Get the document charset
-  var docCharset = "charset=" + aDocument.characterSet;
-
-  // Get the nsIWebNavigation associated with the document
-  try {
-      var win;
-      var ifRequestor;
-
-      // Get the DOMWindow for the requested document.  If the DOMWindow
-      // cannot be found, then just use the content window...
-      //
-      // XXX:  This is a bit of a hack...
-      win = aDocument.defaultView;
-      if (win == window) {
-        win = content;
-      }
-      ifRequestor = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
-
-      webNav = ifRequestor.getInterface(nsIWebNavigation);
-  } catch(err) {
-      // If nsIWebNavigation cannot be found, just get the one for the whole
-      // window...
-      webNav = gBrowser.webNavigation;
-  }
-  //
-  // Get the 'PageDescriptor' for the current document. This allows the
-  // view-source to access the cached copy of the content rather than
-  // refetching it from the network...
-  //
-  try {
-
-#ifdef E10S_TESTING_ONLY
-    // Workaround for bug 988133, which causes a crash if we attempt to load
-    // the document from the cache when the document is a CPOW (which occurs
-    // if we're using remote tabs). This causes us to reload the document from
-    // the network in this case, so it's not a permanent solution, hence hiding
-    // it behind the E10S_TESTING_ONLY ifdef. This is just a band-aid fix until
-    // we can find something better - see bug 1025146.
-    if (!Cu.isCrossProcessWrapper(aDocument)) {
-#endif
-      var PageLoader = webNav.QueryInterface(Components.interfaces.nsIWebPageDescriptor);
-
-      pageCookie = PageLoader.currentDescriptor;
-#ifdef E10S_TESTING_ONLY
-    }
-#endif
-  } catch(err) {
-    // If no page descriptor is available, just use the view-source URL...
-  }
-
-  top.gViewSourceUtils.viewSource(webNav.currentURI.spec, pageCookie, aDocument);
+/**
+ * Open the View Source dialog.
+ *
+ * @param aArgsOrDocument
+ *        Either an object or a Document. Passing a Document is deprecated,
+ *        and is not supported with e10s. This function will throw if
+ *        aArgsOrDocument is a CPOW.
+ *
+ *        If aArgsOrDocument is an object, that object can take the
+ *        following properties:
+ *
+ *        browser:
+ *          The browser containing the document that we would like to view the
+ *          source of.
+ *        URL:
+ *          A string URL for the page we'd like to view the source of.
+ *        outerWindowID (optional):
+ *          The outerWindowID of the content window containing the document that
+ *          we want to view the source of. You only need to provide this if you
+ *          want to attempt to retrieve the document source from the network
+ *          cache.
+ *        lineNumber (optional):
+ *          The line number to focus on once the source is loaded.
+ */
+function BrowserViewSourceOfDocument(aArgsOrDocument) {
+  let args;
+
+  if (aArgsOrDocument instanceof Document) {
+    let doc = aArgsOrDocument;
+    // Deprecated API - callers should pass args object instead.
+    if (Cu.isCrossProcessWrapper(doc)) {
+      throw new Error("BrowserViewSourceOfDocument cannot accept a CPOW " +
+                      "as a document.");
+    }
+
+    let requestor = doc.defaultView
+                       .QueryInterface(Ci.nsIInterfaceRequestor);
+    let browser = requestor.getInterface(Ci.nsIWebNavigation)
+                           .QueryInterface(Ci.nsIDocShell)
+                           .chromeEventHandler;
+    let outerWindowID = requestor.getInterface(Ci.nsIDOMWindowUtils)
+                                 .outerWindowID;
+    let URL = browser.currentURI.spec;
+    args = { browser, outerWindowID, URL };
+  } else {
+    args = aArgsOrDocument;
+  }
+
+  top.gViewSourceUtils.viewSource(args);
+}
+
+/**
+ * Opens the View Source dialog for the source loaded in the root
+ * top-level document of the browser. This is really just a
+ * convenience wrapper around BrowserViewSourceOfDocument.
+ *
+ * @param browser
+ *        The browser that we want to load the source of.
+ */
+function BrowserViewSource(browser) {
+  BrowserViewSourceOfDocument({
+    browser: browser,
+    outerWindowID: browser.outerWindowID,
+    URL: browser.currentURI.spec,
+  });
 }
 
 // doc - document to use for source, or null for this window's document
 // initialTab - name of the initial tab to display, or null for the first tab
 // imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
 function BrowserPageInfo(doc, initialTab, imageElement) {
   var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
   var windows = Services.wm.getEnumerator("Browser:page-info");
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -26,16 +26,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PluginContent",
   "resource:///modules/PluginContent.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver",
   "resource:///modules/FormSubmitObserver.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
   "resource://gre/modules/PageMetadata.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
+  "resource:///modules/PlacesUIUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "PageMenuChild", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuChild();
 });
 
 // TabChildGlobal
 var global = this;
@@ -638,8 +640,15 @@ addMessageListener("ContextMenu:ReloadFr
   message.objects.target.ownerDocument.location.reload();
 });
 
 addMessageListener("ContextMenu:ReloadImage", (message) => {
   let image = message.objects.target;
   if (image instanceof Ci.nsIImageLoadingContent)
     image.forceReload();
 });
+
+addMessageListener("ContextMenu:BookmarkFrame", (message) => {
+  let frame = message.objects.target.ownerDocument;
+  sendAsyncMessage("ContextMenu:BookmarkFrame:Result",
+                   { title: frame.title,
+                     description: PlacesUIUtils.getDescriptionFromDocument(frame) });
+});
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -988,17 +988,21 @@ nsContextMenu.prototype = {
     var docUrl = null;
     window.openDialog("chrome://global/content/viewPartialSource.xul",
                       "_blank", "scrollbars,resizable,chrome,dialog=no",
                       docUrl, docCharset, reference, aContext);
   },
 
   // Open new "view source" window with the frame's URL.
   viewFrameSource: function() {
-    BrowserViewSourceOfDocument(this.target.ownerDocument);
+    BrowserViewSourceOfDocument({
+      browser: this.browser,
+      URL: gContextMenuContentData.docLocation,
+      outerWindowID: gContextMenuContentData.frameOuterWindowID,
+    });
   },
 
   viewInfo: function() {
     BrowserPageInfo(this.target.ownerDocument.defaultView.top.document);
   },
 
   viewImageInfo: function() {
     BrowserPageInfo(this.target.ownerDocument.defaultView.top.document,
@@ -1594,40 +1598,45 @@ nsContextMenu.prototype = {
   },
 
   bookmarkLink: function CM_bookmarkLink() {
     window.top.PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId,
                                               this.linkURL, this.linkTextStr);
   },
 
   addBookmarkForFrame: function CM_addBookmarkForFrame() {
-    var doc = this.target.ownerDocument;
-    var uri = doc.documentURIObject;
+    let uri = gContextMenuContentData.documentURIObject;
+    let mm = this.browser.messageManager;
+
+    let onMessage = (message) => {
+      mm.removeMessageListener("ContextMenu:BookmarkFrame:Result", onMessage);
 
-    var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
-    if (itemId == -1) {
-      var title = doc.title;
-      var description = PlacesUIUtils.getDescriptionFromDocument(doc);
-      PlacesUIUtils.showBookmarkDialog({ action: "add"
-                                       , type: "bookmark"
-                                       , uri: uri
-                                       , title: title
-                                       , description: description
-                                       , hiddenRows: [ "description"
-                                                     , "location"
-                                                     , "loadInSidebar"
-                                                     , "keyword" ]
-                                       }, window.top);
-    }
-    else {
-      PlacesUIUtils.showBookmarkDialog({ action: "edit"
-                                       , type: "bookmark"
-                                       , itemId: itemId
-                                       }, window.top);
-    }
+      let itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
+      if (itemId == -1) {
+        PlacesUIUtils.showBookmarkDialog({ action: "add"
+                                         , type: "bookmark"
+                                         , uri: uri
+                                         , title: message.data.title
+                                         , description: message.data.description
+                                         , hiddenRows: [ "description"
+                                                       , "location"
+                                                       , "loadInSidebar"
+                                                       , "keyword" ]
+                                         }, window.top);
+      }
+      else {
+        PlacesUIUtils.showBookmarkDialog({ action: "edit"
+                                         , type: "bookmark"
+                                         , itemId: itemId
+                                         }, window.top);
+      }
+    };
+    mm.addMessageListener("ContextMenu:BookmarkFrame:Result", onMessage);
+
+    mm.sendAsyncMessage("ContextMenu:BookmarkFrame", null, { target: this.target });
   },
   markLink: function CM_markLink(origin) {
     // send link to social, if it is the page url linkURI will be null
     SocialMarks.markLink(origin, this.linkURI ? this.linkURI.spec : null, this.target);
   },
   shareLink: function CM_shareLink() {
     SocialShare.sharePage(null, { url: this.linkURI.spec }, this.target);
   },
deleted file mode 100644
--- a/browser/base/content/sync/progress.js
+++ /dev/null
@@ -1,71 +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/. */
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://services-sync/main.js");
-
-let gProgressBar;
-let gCounter = 0;
-
-function onLoad(event) {
-  Services.obs.addObserver(onEngineSync, "weave:engine:sync:finish", false);
-  Services.obs.addObserver(onEngineSync, "weave:engine:sync:error", false);
-  Services.obs.addObserver(onServiceSync, "weave:service:sync:finish", false);
-  Services.obs.addObserver(onServiceSync, "weave:service:sync:error", false);
-
-  gProgressBar = document.getElementById('uploadProgressBar');
-
-  if (Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
-    gProgressBar.hidden = false;
-  }
-  else {
-    gProgressBar.hidden = true;
-  }
-}
-
-function onUnload(event) {
-  cleanUpObservers();
-}
-
-function cleanUpObservers() {
-  try {
-    Services.obs.removeObserver(onEngineSync, "weave:engine:sync:finish");
-    Services.obs.removeObserver(onEngineSync, "weave:engine:sync:error");
-    Services.obs.removeObserver(onServiceSync, "weave:service:sync:finish");
-    Services.obs.removeObserver(onServiceSync, "weave:service:sync:error");
-  }
-  catch (e) {
-    // may be double called by unload & exit. Ignore.
-  }
-}
-
-function onEngineSync(subject, topic, data) {
-  // The Clients engine syncs first. At this point we don't necessarily know
-  // yet how many engines will be enabled, so we'll ignore the Clients engine
-  // and evaluate how many engines are enabled when the first "real" engine
-  // syncs.
-  if (data == "clients") {
-    return;
-  }
-
-  if (!gCounter &&
-      Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
-    gProgressBar.max = Weave.Service.engineManager.getEnabled().length;
-  }
-
-  gCounter += 1;
-  gProgressBar.setAttribute("value", gCounter);
-}
-
-function onServiceSync(subject, topic, data) {
-  // To address the case where 0 engines are synced, we will fill the
-  // progress bar so the user knows that the sync has finished.
-  gProgressBar.setAttribute("value", gProgressBar.max);
-  cleanUpObservers();
-}
-
-function closeTab() {
-  window.close();
-}
deleted file mode 100644
--- a/browser/base/content/sync/progress.xhtml
+++ /dev/null
@@ -1,55 +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/. -->
-
-<!DOCTYPE html [
-  <!ENTITY % htmlDTD
-    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "DTD/xhtml1-strict.dtd">
-  %htmlDTD;
-  <!ENTITY % syncProgressDTD
-    SYSTEM "chrome://browser/locale/syncProgress.dtd">
-    %syncProgressDTD;
-  <!ENTITY % syncSetupDTD
-    SYSTEM "chrome://browser/locale/syncSetup.dtd">
-    %syncSetupDTD;
-  <!ENTITY % globalDTD 
-    SYSTEM "chrome://global/locale/global.dtd">
-    %globalDTD;
-]>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>&syncProgress.pageTitle;</title>
-
-    <link rel="stylesheet" type="text/css" media="all"
-          href="chrome://browser/skin/syncProgress.css"/>
-
-    <link rel="icon" type="image/png" id="favicon"
-          href="chrome://browser/skin/sync-16.png"/>
-
-    <script type="text/javascript;version=1.8"
-            src="chrome://browser/content/sync/progress.js"/>
-  </head>
-  <body onload="onLoad(event)" onunload="onUnload(event)" dir="&locale.dir;">
-    <title>&setup.successPage.title;</title>
-    <div id="floatingBox" class="main-content">
-      <div id="title">
-        <h1>&setup.successPage.title;</h1>
-      </div>
-      <div id="successLogo">
-        <img id="brandSyncLogo" src="chrome://browser/skin/sync-128.png" alt="&syncProgress.logoAltText;" />
-      </div>
-      <div id="loadingText">
-        <p id="blurb">&syncProgress.textBlurb; </p>
-      </div>
-      <div id="progressBar">
-        <progress id="uploadProgressBar" value="0"/>
-      </div>
-      <div id="bottomRow">
-        <button id="closeButton" onclick="closeTab()">&syncProgress.closeButton; </button>
-      </div>
-    </div>
-  </body>
-</html>
--- a/browser/base/content/sync/setup.js
+++ b/browser/base/content/sync/setup.js
@@ -552,18 +552,16 @@ var gSyncSetup = {
         Weave.Svc.Prefs.set(prefs[i], isChecked(prefs[i]));
       }
       this._handleNoScript(false);
       if (Weave.Svc.Prefs.get("firstSync", "") == "notReady")
         Weave.Svc.Prefs.reset("firstSync");
 
       Weave.Service.persistLogin();
       Weave.Svc.Obs.notify("weave:service:setup-complete");
-
-      gSyncUtils.openFirstSyncProgressPage();
     }
     Weave.Utils.nextTick(Weave.Service.sync, Weave.Service);
     window.close();
   },
 
   onWizardCancel: function () {
     if (this._resettingSync)
       return;
--- a/browser/base/content/sync/utils.js
+++ b/browser/base/content/sync/utils.js
@@ -82,20 +82,16 @@ let gSyncUtils = {
     this._openLink(Weave.Svc.Prefs.get(root + "termsURL"));
   },
 
   openPrivacyPolicy: function () {
     let root = this.fxAccountsEnabled ? "fxa." : "";
     this._openLink(Weave.Svc.Prefs.get(root + "privacyURL"));
   },
 
-  openFirstSyncProgressPage: function () {
-    this._openLink("about:sync-progress");
-  },
-
   /**
    * Prepare an invisible iframe with the passphrase backup document.
    * Used by both the print and saving methods.
    *
    * @param elid : ID of the form element containing the passphrase.
    * @param callback : Function called once the iframe has loaded.
    */
   _preparePPiframe: function(elid, callback) {
@@ -125,17 +121,17 @@ let gSyncUtils = {
       el.firstChild.nodeValue = privacyURL;
 
       callback(iframe);
     }, false);
   },
 
   /**
    * Print passphrase backup document.
-   * 
+   *
    * @param elid : ID of the form element containing the passphrase.
    */
   passphrasePrint: function(elid) {
     this._preparePPiframe(elid, function(iframe) {
       let webBrowserPrint = iframe.contentWindow
                                   .QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIWebBrowserPrint);
       let printSettings = PrintUtils.getPrintSettings();
@@ -153,17 +149,17 @@ let gSyncUtils = {
       } catch (ex) {
         // print()'s return codes are expressed as exceptions. Ignore.
       }
     });
   },
 
   /**
    * Save passphrase backup document to disk as HTML file.
-   * 
+   *
    * @param elid : ID of the form element containing the passphrase.
    */
   passphraseSave: function(elid) {
     let dialogTitle = this.bundle.GetStringFromName("save.recoverykey.title");
     let defaultSaveName = this.bundle.GetStringFromName("save.recoverykey.defaultfilename");
     this._preparePPiframe(elid, function(iframe) {
       let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
       let fpCallback = function fpCallback_done(aResult) {
@@ -191,17 +187,17 @@ let gSyncUtils = {
     });
   },
 
   /**
    * validatePassword
    *
    * @param el1 : the first textbox element in the form
    * @param el2 : the second textbox element, if omitted it's an update form
-   * 
+   *
    * returns [valid, errorString]
    */
   validatePassword: function (el1, el2) {
     let valid = false;
     let val1 = el1.value;
     let val2 = el2 ? el2.value : "";
     let error = "";
 
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -116,17 +116,16 @@ skip-if = (os == "linux" || os == "mac")
 skip-if = os == "linux" # Bug 958026
 support-files =
   content_aboutAccounts.js
 [browser_aboutSupport_newtab_security_state.js]
 [browser_aboutHealthReport.js]
 skip-if = os == "linux" # Bug 924307
 [browser_aboutHome.js]
 skip-if = e10s # Bug 1093153 - no about:home support yet
-[browser_aboutSyncProgress.js]
 [browser_action_keyword.js]
 [browser_action_keyword_override.js]
 [browser_action_searchengine.js]
 [browser_action_searchengine_alias.js]
 [browser_addKeywordSearch.js]
 [browser_search_favicon.js]
 [browser_alltabslistener.js]
 [browser_autocomplete_a11y_label.js]
deleted file mode 100644
--- a/browser/base/content/test/general/browser_aboutSyncProgress.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-Cu.import("resource://services-sync/main.js");
-
-let gTests = [ {
-  desc: "Makes sure the progress bar appears if firstSync pref is set",
-  setup: function () {
-    Services.prefs.setCharPref("services.sync.firstSync", "newAccount");
-  },
-  run: function () {
-    let doc = gBrowser.selectedBrowser.contentDocument;
-    let progressBar = doc.getElementById("uploadProgressBar");
-
-    let win = doc.defaultView;
-    isnot(win.getComputedStyle(progressBar).display, "none", "progress bar should be visible");
-    executeSoon(runNextTest);
-  }
-},
-
-{
-  desc: "Makes sure the progress bar is hidden if firstSync pref is not set",
-  setup: function () {
-    Services.prefs.clearUserPref("services.sync.firstSync");
-    is(Services.prefs.getPrefType("services.sync.firstSync"),
-       Ci.nsIPrefBranch.PREF_INVALID, "pref DNE" );
-  },
-  run: function () {
-    let doc = gBrowser.selectedBrowser.contentDocument;
-    let progressBar = doc.getElementById("uploadProgressBar");
-
-    let win = doc.defaultView;
-    is(win.getComputedStyle(progressBar).display, "none",
-       "progress bar should not be visible");
-    executeSoon(runNextTest);
-  }
-},
-{
-  desc: "Makes sure the observer updates are reflected in the progress bar",
-  setup: function () {
-  },
-  run: function () {
-     let doc = gBrowser.selectedBrowser.contentDocument;
-     let progressBar = doc.getElementById("uploadProgressBar");
-
-     Services.obs.notifyObservers(null, "weave:engine:sync:finish", null);
-     Services.obs.notifyObservers(null, "weave:engine:sync:error", null);
-
-     let received = progressBar.getAttribute("value");
-
-     is(received, 2, "progress bar received correct notifications");
-     executeSoon(runNextTest);
-  }
-},
-{
-  desc: "Close button should close tab",
-  setup: function (){
-  },
-  run: function () {
-    function onTabClosed() {
-      ok(true, "received TabClose notification");
-      gBrowser.tabContainer.removeEventListener("TabClose", onTabClosed, false);
-      executeSoon(runNextTest);
-    }
-    let doc = gBrowser.selectedBrowser.contentDocument;
-    let button = doc.getElementById('closeButton');
-    let window = doc.defaultView;
-    gBrowser.tabContainer.addEventListener("TabClose", onTabClosed, false);
-    EventUtils.sendMouseEvent({type: "click"}, button, window);
-  }
-},
-];
-
-function test () {
-  waitForExplicitFinish();
-  executeSoon(runNextTest);
-}
-
-function runNextTest()
-{
-  while (gBrowser.tabs.length > 1) {
-    gBrowser.removeCurrentTab();
-  }
-
-  if (gTests.length) {
-    let test = gTests.shift();
-    info(test.desc);
-    test.setup();
-    let tab = gBrowser.selectedTab = gBrowser.addTab("about:sync-progress");
-    tab.linkedBrowser.addEventListener("load", function (event) {
-      tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
-      // Some part of the page is populated on load, so enqueue on it.
-      executeSoon(test.run);
-    }, true);
-  }
-  else {
-    finish();
-  }
-}
-
--- a/browser/base/content/test/general/browser_parsable_css.js
+++ b/browser/base/content/test/general/browser_parsable_css.js
@@ -19,18 +19,16 @@ const kWhitelist = [
    errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i},
   // Tracked in bug 1004428.
   {sourceName: /aboutaccounts\/(main|normalize)\.css/i},
   // TokBox SDK assets, see bug 1032469.
   {sourceName: /loop\/.*sdk-content\/.*\.css$/i},
   // Highlighter CSS uses chrome-only pseudo-class, see bug 985597.
   {sourceName: /highlighter\.css/i,
    errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i},
-  // Tracked in bug 1160629.
-  {sourceName: /pocket\/panels\/css\/.*\.css/i},
 ];
 
 let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
 let {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
 
 /**
  * Check if an error should be ignored due to matching one of the whitelist
  * objects defined in kWhitelist
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1144,17 +1144,21 @@ file, You can obtain one at http://mozil
           this.setAttribute("showonlysettings", "true");
 
           // Setting this with an xbl-inherited attribute gets overridden the
           // second time the user clicks the glass icon for some reason...
           tree.collapsed = true;
         }
         else {
           this.removeAttribute("showonlysettings");
-          tree.collapsed = false;
+          // Uncollapse as long as we have a tree with a view which has >= 1 row.
+          // The autocomplete binding itself will take care of uncollapsing later,
+          // if we currently have no rows but end up having some in the future
+          // when the search string changes
+          tree.collapsed = !tree.view || !tree.view.rowCount;
         }
 
         // Show the current default engine in the top header of the panel.
         this.updateHeader();
 
         // Update the 'Search for <keywords> with:" header.
         let headerSearchText =
           document.getAnonymousElementByAttribute(this, "anonid",
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -129,18 +129,16 @@ browser.jar:
 *       content/browser/sync/setup.js                 (content/sync/setup.js)
         content/browser/sync/genericChange.xul        (content/sync/genericChange.xul)
         content/browser/sync/genericChange.js         (content/sync/genericChange.js)
         content/browser/sync/key.xhtml                (content/sync/key.xhtml)
         content/browser/sync/notification.xml         (content/sync/notification.xml)
         content/browser/sync/quota.xul                (content/sync/quota.xul)
         content/browser/sync/quota.js                 (content/sync/quota.js)
         content/browser/sync/utils.js                 (content/sync/utils.js)
-        content/browser/sync/progress.js              (content/sync/progress.js)
-        content/browser/sync/progress.xhtml           (content/sync/progress.xhtml)
 *       content/browser/sync/customize.xul            (content/sync/customize.xul)
         content/browser/sync/customize.js             (content/sync/customize.js)
         content/browser/sync/customize.css            (content/sync/customize.css)
 #endif
         content/browser/safeMode.css                  (content/safeMode.css)
         content/browser/safeMode.js                   (content/safeMode.js)
         content/browser/safeMode.xul                  (content/safeMode.xul)
 *       content/browser/sanitize.js                   (content/sanitize.js)
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -74,18 +74,16 @@ static RedirEntry kRedirMap[] = {
   { "robots", "chrome://browser/content/aboutRobots.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT },
   { "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
   { "welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
 #ifdef MOZ_SERVICES_SYNC
-  { "sync-progress", "chrome://browser/content/sync/progress.xhtml",
-    nsIAboutModule::ALLOW_SCRIPT },
   { "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
     nsIAboutModule::ALLOW_SCRIPT },
 #endif
   { "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::ENABLE_INDEXED_DB },
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -95,17 +95,16 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "feeds", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "privatebrowsing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "rights", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "robots", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sessionrestore", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "welcomeback", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_SYNC
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-tabs", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
-    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-progress", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "permissions", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "accounts", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_HEALTHREPORT
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -190,17 +190,17 @@ let gEditItemOverlay = {
       this._namePicker.readOnly = this.readOnly;
     }
 
     // In some cases we want to hide the location field, since it's not
     // human-readable, but we still want to initialize it.
     showOrCollapse("locationRow", isURI, "location");
     if (isURI) {
       this._initLocationField();
-      this._locationField.readOnly = !this.readOnly;
+      this._locationField.readOnly = this.readOnly;
     }
 
     // hide the description field for
     if (showOrCollapse("descriptionRow", isItem && !this.readOnly,
                        "description")) {
       this._initDescriptionField();
       this._descriptionField.readOnly = this.readOnly;
     }
--- a/browser/components/places/content/editBookmarkOverlay.xul
+++ b/browser/components/places/content/editBookmarkOverlay.xul
@@ -9,17 +9,17 @@
 
 <?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
 <?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
 
 <overlay id="editBookmarkOverlay"
          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <vbox id="editBookmarkPanelContent" flex="1">
-    <hbox id="editBMPanel_selectionCount" hidden="true" pack="center">
+    <hbox id="editBMPanel_selectionCount" pack="center">
       <label id="editBMPanel_itemsCountText"/>
     </hbox>
 
     <grid id="editBookmarkPanelGrid" flex="1">
       <columns id="editBMPanel_columns">
         <column id="editBMPanel_labelColumn" />
         <column flex="1" id="editBMPanel_editColumn" />
       </columns>
--- a/browser/components/pocket/panels/js/saved.js
+++ b/browser/components/pocket/panels/js/saved.js
@@ -54,17 +54,17 @@ var PKT_SAVED_OVERLAY = function (option
         {
             myself.suggestedTagsLoaded = true;
             myself.startCloseTimer();
             return;
         }
 
         thePKT_SAVED.sendMessage("getSuggestedTags",
         {
-            url: myself.savedUrl || window.location.toString()
+            url: myself.savedUrl
         }, function(resp) 
         {
             $('.pkt_ext_suggestedtag_detail').removeClass('pkt_ext_suggestedtag_detail_loading');
             if (resp.status == 'success') 
             {
                 var newtags = [];
                 for (var i = 0; i < resp.value.suggestedTags.length; i++)
                 {
@@ -309,17 +309,17 @@ var PKT_SAVED_OVERLAY = function (option
                 if (text.length)
                 {
                     originaltags.push(text);
                 }
             });
 
             thePKT_SAVED.sendMessage("addTags",
             {
-                url: myself.savedUrl || window.location.toString(),
+                url: myself.savedUrl,
                 tags: originaltags   
             }, function(resp)
             {
                 if (resp.status == 'success') 
                 {
                     myself.showStateFinalMsg(myself.dictJSON.tagssaved);
                 }
                 else if (resp.status == 'error') 
@@ -406,17 +406,17 @@ var PKT_SAVED_OVERLAY = function (option
         });
     };
     this.showStateSaved = function(initobj) {
         this.wrapper.find('.pkt_ext_detail h2').text(this.dictJSON.pagesaved);
         this.wrapper.find('.pkt_ext_btn').addClass('pkt_ext_btn_disabled');
         if (typeof initobj.item == 'object')
         {
             this.savedItemId = initobj.item.item_id;
-            this.savedUrl = initobj.item.resolved_url;
+            this.savedUrl = initobj.item.given_url;
         }
         $('.pkt_ext_containersaved').addClass('pkt_ext_container_detailactive').removeClass('pkt_ext_container_finalstate');
 
         myself.fillUserTags();
         if (myself.suggestedTagsLoaded) {
             myself.startCloseTimer();
         }
         else {
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -633,16 +633,17 @@
                    anonid="searchbar-textbox"
                    type="autocomplete"
                    flex="1"
                    autocompletepopup="PopupSearchAutoComplete"
                    autocompletesearch="search-autocomplete"
                    autocompletesearchparam="searchbar-history"
                    maxrows="10"
                    completeselectedindex="true"
+                   minresultsforpopup="0"
                    xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines">
         <!--
         Empty <box> to properly position the icon within the autocomplete
         binding's anonymous children (the autocomplete binding positions <box>
         children differently)
         -->
         <xul:box>
           <xul:hbox class="searchbar-search-button-container">
@@ -1082,19 +1083,20 @@
         <body><![CDATA[
           let popup = this.popup;
           let list = document.getAnonymousElementByAttribute(popup, "anonid",
                                                              "search-panel-one-offs");
           let selectedButton = this.selectedButton;
           let buttons = this.getSelectableButtons(aCycleEngines);
 
           let suggestionsHidden;
-          if (!aSkipSuggestions && popup.hasAttribute("showonlysettings")) {
-            aSkipSuggestions = true;
-            suggestionsHidden = true;
+          if (!aSkipSuggestions) {
+            let suggestions = document.getAnonymousElementByAttribute(popup, "anonid", "tree");
+            suggestionsHidden = suggestions.getAttribute("collapsed") == "true";
+            aSkipSuggestions = suggestionsHidden;
           }
 
           // If the last suggestion is selected, DOWN selects the first button.
           if (!aSkipSuggestions && aForward &&
               popup.selectedIndex + 1 == popup.view.rowCount) {
             this.selectedButton = buttons[0];
             return false;
           }
--- a/browser/components/search/test/browser_searchbar_openpopup.js
+++ b/browser/components/search/test/browser_searchbar_openpopup.js
@@ -8,16 +8,17 @@ this._scriptLoader = Cc["@mozilla.org/mo
                      getService(Ci.mozIJSSubScriptLoader);
 this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
 
 const searchbar = document.getElementById("searchbar");
 const searchIcon = document.getAnonymousElementByAttribute(searchbar, "anonid", "searchbar-search-button");
 const goButton = document.getAnonymousElementByAttribute(searchbar, "anonid", "search-go-button");
 const textbox = searchbar._textbox;
 const searchPopup = document.getElementById("PopupSearchAutoComplete");
+const kValues = ["long text", "long text 2", "long text 3"];
 
 const isWindows = Services.appinfo.OS == "WINNT";
 const mouseDown = isWindows ? 2 : 1;
 const mouseUp = isWindows ? 4 : 2;
 const utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindowUtils);
 const scale = utils.screenPixelsPerCSSPixel;
 
@@ -39,16 +40,47 @@ function* synthesizeNativeMouseClick(aEl
 
     utils.sendNativeMouseEvent(x * scale, y * scale, mouseDown, 0, null);
     utils.sendNativeMouseEvent(x * scale, y * scale, mouseUp, 0, null);
   });
 }
 
 add_task(function* init() {
   yield promiseNewEngine("testEngine.xml");
+
+  // First cleanup the form history in case other tests left things there.
+  yield new Promise((resolve, reject) => {
+    info("cleanup the search history");
+    searchbar.FormHistory.update({op: "remove", fieldname: "searchbar-history"},
+                                 {handleCompletion: resolve,
+                                  handleError: reject});
+  });
+
+  yield new Promise((resolve, reject) => {
+    info("adding search history values: " + kValues);
+    let ops = kValues.map(value => { return {op: "add",
+                                             fieldname: "searchbar-history",
+                                             value: value}
+                                   });
+    searchbar.FormHistory.update(ops, {
+      handleCompletion: function() {
+        registerCleanupFunction(() => {
+          info("removing search history values: " + kValues);
+          let ops =
+            kValues.map(value => { return {op: "remove",
+                                           fieldname: "searchbar-history",
+                                           value: value}
+                                 });
+          searchbar.FormHistory.update(ops);
+        });
+        resolve();
+      },
+      handleError: reject
+    });
+  });
 });
 
 // Adds a task that shouldn't show the search suggestions popup.
 function add_no_popup_task(task) {
   add_task(function*() {
     let sawPopup = false;
     function listener() {
       sawPopup = true;
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -11,16 +11,17 @@ support-files =
   code_binary_search.js
   code_binary_search.map
   code_blackboxing_blackboxme.js
   code_blackboxing_one.js
   code_blackboxing_three.js
   code_blackboxing_two.js
   code_breakpoints-break-on-last-line-of-script-on-reload.js
   code_breakpoints-other-tabs.js
+  code_bug-896139.js
   code_frame-script.js
   code_function-search-01.js
   code_function-search-02.js
   code_function-search-03.js
   code_location-changes.js
   code_math.js
   code_math.map
   code_math.min.js
@@ -42,16 +43,17 @@ support-files =
   code_ugly-8^headers^
   doc_auto-pretty-print-01.html
   doc_auto-pretty-print-02.html
   doc_binary_search.html
   doc_blackboxing.html
   doc_breakpoints-break-on-last-line-of-script-on-reload.html
   doc_breakpoints-other-tabs.html
   doc_breakpoints-reload.html
+  doc_bug-896139.html
   doc_closures.html
   doc_closure-optimized-out.html
   doc_cmd-break.html
   doc_cmd-dbg.html
   doc_breakpoint-move.html
   doc_conditional-breakpoints.html
   doc_domnode-variables.html
   doc_editor-mode.html
@@ -159,16 +161,18 @@ skip-if = e10s && debug
 [browser_dbg_breakpoints-new-script.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-other-tabs.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-pane.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-reload.js]
 skip-if = e10s && debug
+[browser_dbg_bug-896139.js]
+skip-if = e10s && debug
 [browser_dbg_chrome-create.js]
 skip-if = e10s && debug
 [browser_dbg_chrome-debugging.js]
 skip-if = e10s && debug
 [browser_dbg_clean-exit-window.js]
 skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_clean-exit.js]
 skip-if = true # Bug 1044985 (racy test)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_bug-896139.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Bug 896139 - Breakpoints not triggering when reloading script.
+ */
+
+const TAB_URL = "doc_bug-896139.html";
+const SCRIPT_URL = "code_bug-896139.js";
+
+function test() {
+  Task.spawn(function* () {
+    function testBreakpoint() {
+      let promise = waitForDebuggerEvents(panel, win.EVENTS.FETCHED_SCOPES);
+      callInTab(tab, "f");
+      return promise.then(() => doResume(panel));
+    }
+
+    let [tab,, panel] = yield initDebugger(EXAMPLE_URL + TAB_URL);
+    let win = panel.panelWin;
+    yield waitForSourceShown(panel, SCRIPT_URL);
+    yield panel.addBreakpoint({
+      actor: getSourceActor(win.DebuggerView.Sources, EXAMPLE_URL + SCRIPT_URL),
+      line: 6
+    });
+
+    // Race condition: the setBreakpoint request sometimes leaves the
+    // debugger in paused state for a bit because we are called before
+    // that request finishes (see bug 1156531 for plans to fix)
+    if(panel.panelWin.gThreadClient.state !== "attached") {
+      yield waitForThreadEvents(panel, "resumed");
+    }
+
+    yield testBreakpoint();
+    yield reloadActiveTab(panel, win.EVENTS.SOURCE_SHOWN);
+    yield testBreakpoint();
+
+    yield closeDebuggerAndFinish(panel);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/code_bug-896139.js
@@ -0,0 +1,8 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+function f() {
+  var a = 1;
+  var b = 2;
+  var c = 3;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_bug-896139.html
@@ -0,0 +1,18 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8"/>
+    <script>
+      window.onload = function () {
+        var script = document.createElement("script");
+        script.setAttribute("src", "code_bug-896139.js");
+        document.body.appendChild(script);
+      }
+    </script>
+  </head>
+  <body>
+  </body>
+</html>
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -13,16 +13,26 @@ let EventEmitter = require("devtools/too
 let clipboard = require("sdk/clipboard");
 let {HostType} = require("devtools/framework/toolbox").Toolbox;
 
 loader.lazyGetter(this, "MarkupView", () => require("devtools/markupview/markup-view").MarkupView);
 loader.lazyGetter(this, "HTMLBreadcrumbs", () => require("devtools/inspector/breadcrumbs").HTMLBreadcrumbs);
 loader.lazyGetter(this, "ToolSidebar", () => require("devtools/framework/sidebar").ToolSidebar);
 loader.lazyGetter(this, "SelectorSearch", () => require("devtools/inspector/selector-search").SelectorSearch);
 
+loader.lazyGetter(this, "strings", () => {
+  return Services.strings.createBundle("chrome://browser/locale/devtools/inspector.properties");
+});
+loader.lazyGetter(this, "toolboxStrings", () => {
+  return Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
+});
+loader.lazyGetter(this, "clipboardHelper", () => {
+  return Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
+});
+
 const LAYOUT_CHANGE_TIMER = 250;
 
 /**
  * Represents an open instance of the Inspector for a tab.
  * The inspector controls the breadcrumbs, the markup view, and the sidebar
  * (computed view, rule view, font view and layout view).
  *
  * Events:
@@ -72,17 +82,17 @@ function InspectorPanel(iframeWindow, to
 }
 
 exports.InspectorPanel = InspectorPanel;
 
 InspectorPanel.prototype = {
   /**
    * open is effectively an asynchronous constructor
    */
-  open: function InspectorPanel_open() {
+  open: function() {
     return this.target.makeRemote().then(() => {
       return this._getPageStyle();
     }).then(() => {
       return this._getDefaultNodeForSelection();
     }).then(defaultSelection => {
       return this._deferredOpen(defaultSelection);
     }).then(null, console.error);
   },
@@ -156,17 +166,17 @@ InspectorPanel.prototype = {
       // Show a warning when the debugger is paused.
       // We show the warning only when the inspector
       // is selected.
       this.updateDebuggerPausedWarning = () => {
         let notificationBox = this._toolbox.getNotificationBox();
         let notification = notificationBox.getNotificationWithValue("inspector-script-paused");
         if (!notification && this._toolbox.currentToolId == "inspector" &&
             this.target.isThreadPaused) {
-          let message = this.strings.GetStringFromName("debuggerPausedWarning.message");
+          let message = strings.GetStringFromName("debuggerPausedWarning.message");
           notificationBox.appendNotification(message,
             "inspector-script-paused", "", notificationBox.PRIORITY_WARNING_HIGH);
         }
 
         if (notification && this._toolbox.currentToolId != "inspector") {
           notificationBox.removeNotification(notification);
         }
 
@@ -283,37 +293,37 @@ InspectorPanel.prototype = {
     this._target = value;
   },
 
   /**
    * Indicate that a tool has modified the state of the page.  Used to
    * decide whether to show the "are you sure you want to navigate"
    * notification.
    */
-  markDirty: function InspectorPanel_markDirty() {
+  markDirty: function() {
     this.isDirty = true;
   },
 
   /**
    * Hooks the searchbar to show result and auto completion suggestions.
    */
-  setupSearchBox: function InspectorPanel_setupSearchBox() {
+  setupSearchBox: function() {
     // Initiate the selectors search object.
     if (this.searchSuggestions) {
       this.searchSuggestions.destroy();
       this.searchSuggestions = null;
     }
     this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
     this.searchSuggestions = new SelectorSearch(this, this.searchBox);
   },
 
   /**
    * Build the sidebar.
    */
-  setupSidebar: function InspectorPanel_setupSidebar() {
+  setupSidebar: function() {
     let tabbox = this.panelDoc.querySelector("#inspector-sidebar");
     this.sidebar = new ToolSidebar(tabbox, this, "inspector", {
       showAllTabsMenu: true
     });
 
     let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
 
     this._setDefaultSidebar = (event, toolId) => {
@@ -360,17 +370,17 @@ InspectorPanel.prototype = {
     this._paneToggleButton.addEventListener("mousedown",
       this.onPaneToggleButtonClicked);
     this.updatePaneToggleButton();
   },
 
   /**
    * Reset the inspector on new root mutation.
    */
-  onNewRoot: function InspectorPanel_onNewRoot() {
+  onNewRoot: function() {
     this._defaultNode = null;
     this.selection.setNodeFront(null);
     this._destroyMarkup();
     this.isDirty = false;
 
     let onNodeSelected = defaultNode => {
       // Cancel this promise resolution as a new one had
       // been queued up.
@@ -423,17 +433,17 @@ InspectorPanel.prototype = {
     } else {
       return null;
     }
   },
 
   /**
    * When a new node is selected.
    */
-  onNewSelection: function InspectorPanel_onNewSelection(event, value, reason) {
+  onNewSelection: function(event, value, reason) {
     if (reason === "selection-destroy") {
       return;
     }
 
     this.cancelLayoutChange();
 
     // Wait for all the known tools to finish updating and then let the
     // client know.
@@ -517,39 +527,38 @@ InspectorPanel.prototype = {
    */
   cancelUpdate: function() {
     this._updateProgress = null;
   },
 
   /**
    * When a new node is selected, before the selection has changed.
    */
-  onBeforeNewSelection: function InspectorPanel_onBeforeNewSelection(event,
-                                                                     node) {
+  onBeforeNewSelection: function(event, node) {
     if (this.breadcrumbs.indexOf(node) == -1) {
       // only clear locks if we'd have to update breadcrumbs
       this.clearPseudoClasses();
     }
   },
 
   /**
    * When a node is deleted, select its parent node or the defaultNode if no
    * parent is found (may happen when deleting an iframe inside which the
    * node was selected).
    */
-  onDetached: function InspectorPanel_onDetached(event, parentNode) {
+  onDetached: function(event, parentNode) {
     this.cancelLayoutChange();
     this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
     this.selection.setNodeFront(parentNode ? parentNode : this._defaultNode, "detached");
   },
 
   /**
    * Destroy the inspector.
    */
-  destroy: function InspectorPanel__destroy() {
+  destroy: function() {
     if (this._panelDestroyer) {
       return this._panelDestroyer;
     }
 
     if (this.walker) {
       this.walker.off("new-root", this.onNewRoot);
       this.pageStyle = null;
     }
@@ -602,49 +611,49 @@ InspectorPanel.prototype = {
     ]);
 
     return this._panelDestroyer;
   },
 
   /**
    * Show the node menu.
    */
-  showNodeMenu: function InspectorPanel_showNodeMenu(aButton, aPosition, aExtraItems) {
+  showNodeMenu: function(aButton, aPosition, aExtraItems) {
     if (aExtraItems) {
       for (let item of aExtraItems) {
         this.nodemenu.appendChild(item);
       }
     }
     this.nodemenu.openPopup(aButton, aPosition, 0, 0, true, false);
   },
 
-  hideNodeMenu: function InspectorPanel_hideNodeMenu() {
+  hideNodeMenu: function() {
     this.nodemenu.hidePopup();
   },
 
   /**
    * Returns the clipboard content if it is appropriate for pasting
    * into the current node's outer HTML, otherwise returns null.
    */
-  _getClipboardContentForPaste: function Inspector_getClipboardContentForPaste() {
+  _getClipboardContentForPaste: function() {
     let flavors = clipboard.currentFlavors;
     if (flavors.indexOf("text") != -1 ||
         (flavors.indexOf("html") != -1 && flavors.indexOf("image") == -1)) {
       let content = clipboard.get();
       if (content && content.trim().length > 0) {
         return content;
       }
     }
     return null;
   },
 
   /**
    * Disable the delete item if needed. Update the pseudo classes.
    */
-  _setupNodeMenu: function InspectorPanel_setupNodeMenu() {
+  _setupNodeMenu: function() {
     let isSelectionElement = this.selection.isElementNode() &&
                              !this.selection.isPseudoElementNode();
     let isEditableElement = isSelectionElement &&
                             !this.selection.isAnonymousNode();
 
     // Set the pseudo classes
     for (let name of ["hover", "active", "focus"]) {
       let menu = this.panelDoc.getElementById("node-menu-pseudo-" + name);
@@ -740,29 +749,29 @@ InspectorPanel.prototype = {
     let markupContainer = this.markup.getContainer(this.selection.nodeFront);
     if (isSelectionElement && markupContainer && markupContainer.isPreviewable()) {
       copyImageData.removeAttribute("disabled");
     } else {
       copyImageData.setAttribute("disabled", "true");
     }
   },
 
-  _resetNodeMenu: function InspectorPanel_resetNodeMenu() {
+  _resetNodeMenu: function() {
     // Remove any extra items
     while (this.lastNodemenuItem.nextSibling) {
       let toDelete = this.lastNodemenuItem.nextSibling;
       toDelete.parentNode.removeChild(toDelete);
     }
   },
 
   /**
    * Link menu items can be shown or hidden depending on the context and
    * selected node, and their labels can vary.
    */
-  _setupNodeLinkMenu: function InspectorPanel_setupNodeLinkMenu() {
+  _setupNodeLinkMenu: function() {
     let linkSeparator = this.panelDoc.getElementById("node-menu-link-separator");
     let linkFollow = this.panelDoc.getElementById("node-menu-link-follow");
     let linkCopy = this.panelDoc.getElementById("node-menu-link-copy");
 
     // Hide all by default.
     linkSeparator.setAttribute("hidden", "true");
     linkFollow.setAttribute("hidden", "true");
     linkCopy.setAttribute("hidden", "true");
@@ -781,41 +790,41 @@ InspectorPanel.prototype = {
           return;
         }
 
         linkSeparator.removeAttribute("hidden");
 
         // Links can't be opened in new tabs in the browser toolbox.
         if (type === "uri" && !this.target.chrome) {
           linkFollow.removeAttribute("hidden");
-          linkFollow.setAttribute("label", this.strings.GetStringFromName(
+          linkFollow.setAttribute("label", strings.GetStringFromName(
             "inspector.menu.openUrlInNewTab.label"));
         } else if (type === "cssresource") {
           linkFollow.removeAttribute("hidden");
-          linkFollow.setAttribute("label", this.toolboxStrings.GetStringFromName(
+          linkFollow.setAttribute("label", toolboxStrings.GetStringFromName(
             "toolbox.viewCssSourceInStyleEditor.label"));
         } else if (type === "jsresource") {
           linkFollow.removeAttribute("hidden");
-          linkFollow.setAttribute("label", this.toolboxStrings.GetStringFromName(
+          linkFollow.setAttribute("label", toolboxStrings.GetStringFromName(
             "toolbox.viewJsSourceInDebugger.label"));
         }
 
         linkCopy.removeAttribute("hidden");
-        linkCopy.setAttribute("label", this.strings.GetStringFromName(
+        linkCopy.setAttribute("label", strings.GetStringFromName(
           "inspector.menu.copyUrlToClipboard.label"));
       }, console.error);
     } else if (type === "idref") {
       linkSeparator.removeAttribute("hidden");
       linkFollow.removeAttribute("hidden");
-      linkFollow.setAttribute("label", this.strings.formatStringFromName(
+      linkFollow.setAttribute("label", strings.formatStringFromName(
         "inspector.menu.selectElement.label", [popupNode.dataset.link], 1));
     }
   },
 
-  _initMarkup: function InspectorPanel_initMarkup() {
+  _initMarkup: function() {
     let doc = this.panelDoc;
 
     this._markupBox = doc.getElementById("markup-box");
 
     // create tool iframe
     this._markupFrame = doc.createElement("iframe");
     this._markupFrame.setAttribute("flex", "1");
     this._markupFrame.setAttribute("tooltip", "aHTMLTooltip");
@@ -823,34 +832,34 @@ InspectorPanel.prototype = {
 
     // This is needed to enable tooltips inside the iframe document.
     this._boundMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
     this._markupFrame.addEventListener("load", this._boundMarkupFrameLoad, true);
 
     this._markupBox.setAttribute("collapsed", true);
     this._markupBox.appendChild(this._markupFrame);
     this._markupFrame.setAttribute("src", "chrome://browser/content/devtools/markup-view.xhtml");
-    this._markupFrame.setAttribute("aria-label", this.strings.GetStringFromName("inspector.panelLabel.markupView"));
+    this._markupFrame.setAttribute("aria-label", strings.GetStringFromName("inspector.panelLabel.markupView"));
   },
 
-  _onMarkupFrameLoad: function InspectorPanel__onMarkupFrameLoad() {
+  _onMarkupFrameLoad: function() {
     this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
     delete this._boundMarkupFrameLoad;
 
     this._markupFrame.contentWindow.focus();
 
     this._markupBox.removeAttribute("collapsed");
 
     let controllerWindow = this._toolbox.doc.defaultView;
     this.markup = new MarkupView(this, this._markupFrame, controllerWindow);
 
     this.emit("markuploaded");
   },
 
-  _destroyMarkup: function InspectorPanel__destroyMarkup() {
+  _destroyMarkup: function() {
     let destroyPromise;
 
     if (this._boundMarkupFrameLoad) {
       this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
       this._boundMarkupFrameLoad = null;
     }
 
     if (this.markup) {
@@ -895,198 +904,191 @@ InspectorPanel.prototype = {
     ViewHelpers.togglePane({
       visible: !isVisible,
       animated: true,
       delayed: true
     }, sidePane);
 
     if (isVisible) {
       button.setAttribute("pane-collapsed", "");
-      button.setAttribute("tooltiptext",
-        this.strings.GetStringFromName("inspector.expandPane"));
+      button.setAttribute("tooltiptext", strings.GetStringFromName("inspector.expandPane"));
     } else {
       button.removeAttribute("pane-collapsed");
-      button.setAttribute("tooltiptext",
-        this.strings.GetStringFromName("inspector.collapsePane"));
+      button.setAttribute("tooltiptext", strings.GetStringFromName("inspector.collapsePane"));
     }
   },
 
   /**
    * Update the pane toggle button visibility depending on the toolbox host type.
    */
   updatePaneToggleButton: function() {
     this._paneToggleButton.setAttribute("hidden",
       this._toolbox.hostType === HostType.SIDE);
   },
 
   /**
    * Toggle a pseudo class.
    */
-  togglePseudoClass: function InspectorPanel_togglePseudoClass(aPseudo) {
+  togglePseudoClass: function(aPseudo) {
     if (this.selection.isElementNode()) {
       let node = this.selection.nodeFront;
       if (node.hasPseudoClassLock(aPseudo)) {
         return this.walker.removePseudoClassLock(node, aPseudo, {parents: true});
       }
 
       let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
       return this.walker.addPseudoClassLock(node, aPseudo, {parents: hierarchical});
     }
   },
 
   /**
    * Show DOM properties
    */
-  showDOMProperties: function InspectorPanel_showDOMProperties() {
+  showDOMProperties: function() {
     this._toolbox.openSplitConsole().then(() => {
       let panel = this._toolbox.getPanel("webconsole");
       let jsterm = panel.hud.jsterm;
 
       jsterm.execute("inspect($0)");
       jsterm.inputNode.focus();
     });
   },
 
   /**
    * Clear any pseudo-class locks applied to the current hierarchy.
    */
-  clearPseudoClasses: function InspectorPanel_clearPseudoClasses() {
+  clearPseudoClasses: function() {
     if (!this.walker) {
       return;
     }
     return this.walker.clearPseudoClassLocks().then(null, console.error);
   },
 
   /**
    * Edit the outerHTML of the selected Node.
    */
-  editHTML: function InspectorPanel_editHTML() {
+  editHTML: function() {
     if (!this.selection.isNode()) {
       return;
     }
     if (this.markup) {
       this.markup.beginEditingOuterHTML(this.selection.nodeFront);
     }
   },
 
   /**
    * Paste the contents of the clipboard into the selected Node's outer HTML.
    */
-  pasteOuterHTML: function InspectorPanel_pasteOuterHTML() {
+  pasteOuterHTML: function() {
     let content = this._getClipboardContentForPaste();
     if (!content)
       return promise.reject("No clipboard content for paste");
 
     let node = this.selection.nodeFront;
     return this.markup.getNodeOuterHTML(node).then(oldContent => {
       this.markup.updateNodeOuterHTML(node, content, oldContent);
     });
   },
 
   /**
    * Paste the contents of the clipboard into the selected Node's inner HTML.
    */
-  pasteInnerHTML: function InspectorPanel_pasteInnerHTML() {
+  pasteInnerHTML: function() {
     let content = this._getClipboardContentForPaste();
     if (!content)
       return promise.reject("No clipboard content for paste");
 
     let node = this.selection.nodeFront;
     return this.markup.getNodeInnerHTML(node).then(oldContent => {
       this.markup.updateNodeInnerHTML(node, content, oldContent);
     });
   },
 
   /**
    * Paste the contents of the clipboard as adjacent HTML to the selected Node.
    * @param position The position as specified for Element.insertAdjacentHTML
    *        (i.e. "beforeBegin", "afterBegin", "beforeEnd", "afterEnd").
    */
-  pasteAdjacentHTML: function InspectorPanel_pasteAdjacent(position) {
+  pasteAdjacentHTML: function(position) {
     let content = this._getClipboardContentForPaste();
     if (!content)
       return promise.reject("No clipboard content for paste");
 
     let node = this.selection.nodeFront;
     return this.markup.insertAdjacentHTMLToNode(node, position, content);
   },
 
   /**
    * Copy the innerHTML of the selected Node to the clipboard.
    */
-  copyInnerHTML: function InspectorPanel_copyInnerHTML() {
+  copyInnerHTML: function() {
     if (!this.selection.isNode()) {
       return;
     }
     this._copyLongStr(this.walker.innerHTML(this.selection.nodeFront));
   },
 
   /**
    * Copy the outerHTML of the selected Node to the clipboard.
    */
-  copyOuterHTML: function InspectorPanel_copyOuterHTML()
-  {
+  copyOuterHTML: function() {
     if (!this.selection.isNode()) {
       return;
     }
 
     this._copyLongStr(this.walker.outerHTML(this.selection.nodeFront));
   },
 
   /**
    * Copy the data-uri for the currently selected image in the clipboard.
    */
-  copyImageDataUri: function InspectorPanel_copyImageDataUri()
-  {
+  copyImageDataUri: function() {
     let container = this.markup.getContainer(this.selection.nodeFront);
     if (container && container.isPreviewable()) {
       container.copyImageDataUri();
     }
   },
 
-  _copyLongStr: function InspectorPanel_copyLongStr(promise)
-  {
+  _copyLongStr: function(promise) {
     return promise.then(longstr => {
       return longstr.string().then(toCopy => {
         longstr.release().then(null, console.error);
         clipboardHelper.copyString(toCopy);
       });
     }).then(null, console.error);
   },
 
   /**
    * Copy a unique selector of the selected Node to the clipboard.
    */
-  copyUniqueSelector: function InspectorPanel_copyUniqueSelector()
-  {
+  copyUniqueSelector: function() {
     if (!this.selection.isNode()) {
       return;
     }
 
     this.selection.nodeFront.getUniqueSelector().then((selector) => {
       clipboardHelper.copyString(selector);
     }).then(null, console.error);
   },
 
   /**
    * Scroll the node into view.
    */
-  scrollNodeIntoView: function InspectorPanel_scrollNodeIntoView()
-  {
+  scrollNodeIntoView: function() {
     if (!this.selection.isNode()) {
       return;
     }
 
     this.selection.nodeFront.scrollIntoView();
   },
 
   /**
    * Delete the selected node.
    */
-  deleteNode: function IUI_deleteNode() {
+  deleteNode: function() {
     if (!this.selection.isNode() ||
          this.selection.isRoot()) {
       return;
     }
 
     // If the markup panel is active, use the markup panel to delete
     // the node, making this an undoable action.
     if (this.markup) {
@@ -1097,17 +1099,17 @@ InspectorPanel.prototype = {
     }
   },
 
   /**
    * This method is here for the benefit of the node-menu-link-follow menu item
    * in the inspector contextual-menu. It's behavior depends on which node was
    * right-clicked when the menu was opened.
    */
-  followAttributeLink: function InspectorPanel_followLink(e) {
+  followAttributeLink: function(e) {
     let type = this.panelDoc.popupNode.dataset.type;
     let link = this.panelDoc.popupNode.dataset.link;
 
     if (type === "uri" || type === "cssresource" || type === "jsresource") {
       // Open link in a new tab.
       // When the inspector menu was setup on click (see _setupNodeLinkMenu), we
       // already checked that resolveRelativeURL existed.
       this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
@@ -1134,78 +1136,53 @@ InspectorPanel.prototype = {
     }
   },
 
   /**
    * This method is here for the benefit of the node-menu-link-copy menu item
    * in the inspector contextual-menu. It's behavior depends on which node was
    * right-clicked when the menu was opened.
    */
-  copyAttributeLink: function InspectorPanel_copyLink(e) {
+  copyAttributeLink: function(e) {
     let link = this.panelDoc.popupNode.dataset.link;
     // When the inspector menu was setup on click (see _setupNodeLinkMenu), we
     // already checked that resolveRelativeURL existed.
     this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
       clipboardHelper.copyString(url);
     }, console.error);
   },
 
   /**
-  * Trigger a high-priority layout change for things that need to be
-  * updated immediately
-  */
-  immediateLayoutChange: function Inspector_immediateLayoutChange()
-  {
+   * Trigger a high-priority layout change for things that need to be
+   * updated immediately
+   */
+  immediateLayoutChange: function() {
     this.emit("layout-change");
   },
 
   /**
    * Schedule a low-priority change event for things like paint
    * and resize.
    */
-  scheduleLayoutChange: function Inspector_scheduleLayoutChange(event)
-  {
+  scheduleLayoutChange: function(event) {
     // Filter out non browser window resize events (i.e. triggered by iframes)
     if (this.browser.contentWindow === event.target) {
       if (this._timer) {
         return null;
       }
       this._timer = this.panelWin.setTimeout(() => {
         this.emit("layout-change");
         this._timer = null;
       }, LAYOUT_CHANGE_TIMER);
     }
   },
 
   /**
    * Cancel a pending low-priority change event if any is
    * scheduled.
    */
-  cancelLayoutChange: function Inspector_cancelLayoutChange()
-  {
+  cancelLayoutChange: function() {
     if (this._timer) {
       this.panelWin.clearTimeout(this._timer);
       delete this._timer;
     }
   }
 };
-
-/////////////////////////////////////////////////////////////////////////
-//// Initializers
-
-loader.lazyGetter(InspectorPanel.prototype, "strings", function () {
-  return Services.strings.createBundle(
-    "chrome://browser/locale/devtools/inspector.properties");
-});
-
-loader.lazyGetter(InspectorPanel.prototype, "toolboxStrings", function () {
-  return Services.strings.createBundle(
-    "chrome://browser/locale/devtools/toolbox.properties");
-});
-
-loader.lazyGetter(this, "clipboardHelper", function() {
-  return Cc["@mozilla.org/widget/clipboardhelper;1"].
-    getService(Ci.nsIClipboardHelper);
-});
-
-loader.lazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -495,19 +495,19 @@ MarkupView.prototype = {
 
     switch(aEvent.keyCode) {
       case Ci.nsIDOMKeyEvent.DOM_VK_H:
         if (aEvent.metaKey || aEvent.shiftKey) {
           handled = false;
         } else {
           let node = this._selectedContainer.node;
           if (node.hidden) {
-            this.walker.unhideNode(node).then(() => this.nodeChanged(node));
+            this.walker.unhideNode(node);
           } else {
-            this.walker.hideNode(node).then(() => this.nodeChanged(node));
+            this.walker.hideNode(node);
           }
         }
         break;
       case Ci.nsIDOMKeyEvent.DOM_VK_DELETE:
         this.deleteNode(this._selectedContainer.node);
         break;
       case Ci.nsIDOMKeyEvent.DOM_VK_BACK_SPACE:
         this.deleteNode(this._selectedContainer.node, true);
@@ -1201,25 +1201,16 @@ MarkupView.prototype = {
   unmarkSelectedNode: function() {
     if (this._selectedContainer) {
       this._selectedContainer.selected = false;
       this._selectedContainer = null;
     }
   },
 
   /**
-   * Called when the markup panel initiates a change on a node.
-   */
-  nodeChanged: function(aNode) {
-    if (aNode === this._inspector.selection.nodeFront) {
-      this._inspector.change("markupview");
-    }
-  },
-
-  /**
    * Check if the current selection is a descendent of the container.
    * if so, make sure it's among the visible set for the container,
    * and set the dirty flag if needed.
    * @returns The node that should be made visible, if any.
    */
   _checkSelectionVisible: function(aContainer) {
     let centered = null;
     let node = this._inspector.selection.nodeFront;
@@ -2268,23 +2259,19 @@ function TextEditor(aContainer, aNode, a
       if (!aCommit) {
         return;
       }
       this.node.getNodeValue().then(longstr => {
         longstr.string().then(oldValue => {
           longstr.release().then(null, console.error);
 
           this.container.undo.do(() => {
-            this.node.setNodeValue(aVal).then(() => {
-              this.markup.nodeChanged(this.node);
-            });
+            this.node.setNodeValue(aVal);
           }, () => {
-            this.node.setNodeValue(oldValue).then(() => {
-              this.markup.nodeChanged(this.node);
-            });
+            this.node.setNodeValue(oldValue);
           });
         });
       });
     }
   });
 
   this.update();
 }
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -515,72 +515,72 @@ NetworkEventsHandler.prototype = {
    * @param string type
    *        Message type.
    * @param object packet
    *        The message received from the server.
    * @param object networkInfo
    *        The network request information.
    */
   _onNetworkEventUpdate: function(type, { packet, networkInfo }) {
-    let actor = networkInfo.actor;
+    let { actor, request: { url } } = networkInfo;
 
     switch (packet.updateType) {
       case "requestHeaders":
         this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
-        window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
+        window.emit(EVENTS.UPDATING_REQUEST_HEADERS, [actor, url]);
         break;
       case "requestCookies":
         this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
-        window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
+        window.emit(EVENTS.UPDATING_REQUEST_COOKIES, [actor, url]);
         break;
       case "requestPostData":
         this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData);
-        window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
+        window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, [actor, url]);
         break;
       case "securityInfo":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           securityState: networkInfo.securityInfo,
         });
         this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
-        window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
+        window.emit(EVENTS.UPDATING_SECURITY_INFO, [actor, url]);
         break;
       case "responseHeaders":
         this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders);
-        window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
+        window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, [actor, url]);
         break;
       case "responseCookies":
         this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies);
-        window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
+        window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, [actor, url]);
         break;
       case "responseStart":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           httpVersion: networkInfo.response.httpVersion,
           remoteAddress: networkInfo.response.remoteAddress,
           remotePort: networkInfo.response.remotePort,
           status: networkInfo.response.status,
           statusText: networkInfo.response.statusText,
           headersSize: networkInfo.response.headersSize
         });
-        window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
+        window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, [actor, url]);
         break;
       case "responseContent":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           contentSize: networkInfo.response.bodySize,
           transferredSize: networkInfo.response.transferredSize,
           mimeType: networkInfo.response.content.mimeType
         });
         this.webConsoleClient.getResponseContent(actor, this._onResponseContent);
-        window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
+        window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, [actor, url]);
         break;
       case "eventTimings":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           totalTime: networkInfo.totalTime
         });
         this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
-        window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
+        window.emit(EVENTS.UPDATING_EVENT_TIMINGS, [actor, url]);
         break;
     }
   },
 
   /**
    * Handles additional information received for a "requestHeaders" packet.
    *
    * @param object aResponse
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -188,17 +188,16 @@ function teardown(aMonitor) {
   return deferred.promise;
 }
 
 function waitForNetworkEvents(aMonitor, aGetRequests, aPostRequests = 0) {
   let deferred = promise.defer();
 
   let panel = aMonitor.panelWin;
   let events = panel.EVENTS;
-  let menu = panel.NetMonitorView.RequestsMenu;
 
   let progress = {};
   let genericEvents = 0;
   let postEvents = 0;
 
   let awaitedEventsToListeners = [
     ["UPDATING_REQUEST_HEADERS", onGenericEvent],
     ["RECEIVED_REQUEST_HEADERS", onGenericEvent],
@@ -233,23 +232,22 @@ function waitForNetworkEvents(aMonitor, 
     maybeResolve(event, actor);
   }
 
   function onPostEvent(event, actor) {
     postEvents++;
     maybeResolve(event, actor);
   }
 
-  function maybeResolve(event, actor) {
+  function maybeResolve(event, [actor, url]) {
     info("> Network events progress: " +
       genericEvents + "/" + ((aGetRequests + aPostRequests) * 13) + ", " +
       postEvents + "/" + (aPostRequests * 2) + ", " +
       "got " + event + " for " + actor);
 
-    let url = menu.getItemByValue(actor).attachment.url;
     updateProgressForURL(url, event);
     info("> Current state: " + JSON.stringify(progress, null, 2));
 
     // There are 15 updates which need to be fired for a request to be
     // considered finished. The "requestPostData" packet isn't fired for
     // non-POST requests.
     if (genericEvents == (aGetRequests + aPostRequests) * 13 &&
         postEvents == aPostRequests * 2) {
--- a/browser/devtools/performance/test/browser_perf-refresh.js
+++ b/browser/devtools/performance/test/browser_perf-refresh.js
@@ -4,39 +4,48 @@
 /**
  * Rough test that the recording still continues after a refresh.
  */
 function spawnTest () {
   let { panel, target } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, PerformanceController } = panel.panelWin;
 
   // Enable memory to test all the overview graphs.
-  Services.prefs.setBoolPref(MEMORY_PREF, true);
+  // TODO intermittent bug 1157523 is tracking a leak in this test
+  // disable memory to see if that narrows down the leaking.
+  // Services.prefs.setBoolPref(MEMORY_PREF, true);
+  Services.prefs.setBoolPref(MEMORY_PREF, false);
+  // TODO intermittent bug 1157523 also disable framerate ticks to further narrow it down
+  Services.prefs.setBoolPref(FRAMERATE_PREF, false);
 
   yield startRecording(panel);
 
-  yield reload(target)
+  yield reload(target);
 
   let rec = PerformanceController.getCurrentRecording();
   let { markers, memory, ticks } = rec.getAllData();
   // Store current length of items
   let markersLength = markers.length;
   let memoryLength = memory.length;
   let ticksLength = ticks.length;
 
   ok(rec.isRecording(), "RecordingModel should still be recording after reload");
 
   yield busyWait(100);
   yield waitUntil(() => rec.getMarkers().length > markersLength);
-  yield waitUntil(() => rec.getMemory().length > memoryLength);
-  yield waitUntil(() => rec.getTicks().length > ticksLength);
+  // TODO bug 1157523
+  // yield waitUntil(() => rec.getMemory().length > memoryLength);
+  // TODO bug 1157523
+  // yield waitUntil(() => rec.getTicks().length > ticksLength);
   ok("Markers, memory and ticks continue after reload");
 
   yield stopRecording(panel);
 
   let { allocations, profile, frames } = rec.getAllData();
-  ok(allocations, "allocations exist after refresh");
+  // TODO bug 1157523
+  // ok(allocations, "allocations exist after refresh");
   ok(profile, "profile exists after refresh");
-  ok(frames, "frames exist after refresh");
+  // TODO bug 1157523
+  // ok(frames, "frames exist after refresh");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/projecteditor/test/browser.ini
+++ b/browser/devtools/projecteditor/test/browser.ini
@@ -7,16 +7,17 @@ support-files =
   helper_edits.js
 
 [browser_projecteditor_app_options.js]
 skip-if = buildapp == 'mulet'
 [browser_projecteditor_confirm_unsaved.js]
 [browser_projecteditor_contextmenu_01.js]
 [browser_projecteditor_contextmenu_02.js]
 [browser_projecteditor_delete_file.js]
+skip-if = e10s # Frequent failures in e10s - Bug 1020027
 [browser_projecteditor_rename_file.js]
 skip-if = buildapp == 'mulet'
 [browser_projecteditor_editing_01.js]
 skip-if = buildapp == 'mulet'
 [browser_projecteditor_editors_image.js]
 [browser_projecteditor_external_change.js]
 [browser_projecteditor_immediate_destroy.js]
 [browser_projecteditor_init.js]
--- a/browser/devtools/shared/test/browser_outputparser.js
+++ b/browser/devtools/shared/test/browser_outputparser.js
@@ -13,90 +13,121 @@ add_task(function*() {
 
 function* performTest() {
   let [host, win, doc] = yield createHost("bottom", "data:text/html," +
     "<h1>browser_outputParser.js</h1><div></div>");
 
   let parser = new OutputParser();
   testParseCssProperty(doc, parser);
   testParseCssVar(doc, parser);
-  testParseHTMLAttribute(doc, parser);
-  testParseNonCssHTMLAttribute(doc, parser);
 
   host.destroy();
 }
 
+// Class name used in color swatch.
+let COLOR_TEST_CLASS = 'test-class';
+
+// Create a new CSS color-parsing test.  |name| is the name of the CSS
+// property.  |value| is the CSS text to use.  |segments| is an array
+// describing the expected result.  If an element of |segments| is a
+// string, it is simply appended to the expected string.  Otherwise,
+// it must be an object with a |value| property and a |name| property.
+// These describe the color and are both used in the generated
+// expected output -- |name| is the color name as it appears in the
+// input (e.g., "red"); and |value| is the hash-style numeric value
+// for the color, which parseCssProperty emits in some spots (e.g.,
+// "#F00").
+//
+// This approach is taken to reduce boilerplate and to make it simpler
+// to modify the test when the parseCssProperty output changes.
+function makeColorTest(name, value, segments) {
+  let result = {
+    name,
+    value,
+    expected: ''
+  };
+
+  for (let segment of segments) {
+    if (typeof(segment) === 'string') {
+      result.expected += segment;
+    } else {
+      result.expected += '<span data-color="' + segment.value + '">' +
+        '<span style="background-color:' + segment.name +
+        '" class="' + COLOR_TEST_CLASS + '"></span><span>' +
+        segment.value + '</span></span>';
+    }
+  }
+
+  result.desc = "Testing " + name + ": " + value;
+
+  return result;
+}
+
 function testParseCssProperty(doc, parser) {
-  let frag = parser.parseCssProperty("border", "1px solid red", {
-    colorSwatchClass: "test-colorswatch"
-  });
+  let tests = [
+    makeColorTest("border", "1px solid red",
+                  ["1px solid ", {name: "red", value: "#F00"}]),
+
+    makeColorTest("background-image",
+                  "linear-gradient(to right, #F60 10%, rgba(0,0,0,1))",
+                  ["linear-gradient(to right, ", {name: "#F60", value: "#F60"},
+                   " 10%, ", {name: "rgba(0,0,0,1)", value: "#000"},
+                   ")"]),
+
+    // In "arial black", "black" is a font, not a color.
+    makeColorTest("font-family", "arial black", ["arial black"]),
+
+    makeColorTest("box-shadow", "0 0 1em red",
+                  ["0 0 1em ", {name: "red", value: "#F00"}]),
+
+    makeColorTest("box-shadow",
+                  "0 0 1em red, 2px 2px 0 0 rgba(0,0,0,.5)",
+                  ["0 0 1em ", {name: "red", value: "#F00"},
+                   ", 2px 2px 0 0 ",
+                   {name: "rgba(0,0,0,.5)", value: "rgba(0,0,0,.5)"}]),
+
+    makeColorTest("content", '"red"', ['"red"']),
+
+    // Invalid property names should not cause exceptions.
+    makeColorTest("hellothere", "'red'", ["'red'"]),
+
+    // This requires better parsing than we currently have available.
+    // See bug 1158288.
+    // makeColorTest("filter",
+    //               "blur(1px) drop-shadow(0 0 0 blue) url(red.svg#blue)",
+    //               ["blur(1px) drop-shadow(0 0 0 ",
+    //                {name: "blue", value: "#00F"},
+    //                ") url(red.svg#blue)"])
+
+  ];
 
   let target = doc.querySelector("div");
   ok(target, "captain, we have the div");
-  target.appendChild(frag);
+
+  for (let test of tests) {
+    info(test.desc);
 
-  is(target.innerHTML,
-     '1px solid <span data-color="#F00"><span style="background-color:red" class="test-colorswatch"></span><span>#F00</span></span>',
-     "CSS property correctly parsed");
-
-  target.innerHTML = "";
+    let frag = parser.parseCssProperty(test.name, test.value, {
+      colorSwatchClass: COLOR_TEST_CLASS
+    });
 
-  frag = parser.parseCssProperty("background-image", "linear-gradient(to right, #F60 10%, rgba(0,0,0,1))", {
-    colorSwatchClass: "test-colorswatch",
-    colorClass: "test-color"
-  });
-  target.appendChild(frag);
-  is(target.innerHTML,
-     'linear-gradient(to right, <span data-color="#F60"><span style="background-color:#F60" class="test-colorswatch"></span><span class="test-color">#F60</span></span> 10%, ' +
-     '<span data-color="#000"><span style="background-color:rgba(0,0,0,1)" class="test-colorswatch"></span><span class="test-color">#000</span></span>)',
-     "Gradient CSS property correctly parsed");
+    target.appendChild(frag);
 
-  target.innerHTML = "";
+    is(target.innerHTML, test.expected,
+       "CSS property correctly parsed for " + test.name + ": " + test.value);
+
+    target.innerHTML = "";
+  }
 }
 
 function testParseCssVar(doc, parser) {
   let frag = parser.parseCssProperty("color", "var(--some-kind-of-green)", {
     colorSwatchClass: "test-colorswatch"
   });
 
   let target = doc.querySelector("div");
   ok(target, "captain, we have the div");
   target.appendChild(frag);
 
   is(target.innerHTML, "var(--some-kind-of-green)", "CSS property correctly parsed");
 
   target.innerHTML = "";
 }
-
-function testParseHTMLAttribute(doc, parser) {
-  let attrib = "color:red; font-size: 12px; background-image: " +
-               "url(chrome://branding/content/about-logo.png)";
-  let frag = parser.parseHTMLAttribute(attrib, {
-    urlClass: "theme-link",
-    colorClass: "theme-color"
-  });
-
-  let target = doc.querySelector("div");
-  ok(target, "captain, we have the div");
-  target.appendChild(frag);
-
-  let expected = 'color:<span data-color="#F00"><span class="theme-color">#F00</span></span>; font-size: 12px; ' +
-                 'background-image: url("<a href="chrome://branding/content/about-logo.png" ' +
-                 'class="theme-link" ' +
-                 'target="_blank">chrome://branding/content/about-logo.png</a>")';
-
-  is(target.innerHTML, expected, "HTML Attribute correctly parsed");
-  target.innerHTML = "";
-}
-
-function testParseNonCssHTMLAttribute(doc, parser) {
-  let attrib = "someclass background someotherclass red";
-  let frag = parser.parseHTMLAttribute(attrib);
-
-  let target = doc.querySelector("div");
-  ok(target, "captain, we have the div");
-  target.appendChild(frag);
-
-  let expected = 'someclass background someotherclass red';
-
-  is(target.innerHTML, expected, "Non-CSS HTML Attribute correctly parsed");
-  target.innerHTML = "";
-}
deleted file mode 100644
--- a/browser/locales/en-US/chrome/browser/syncProgress.dtd
+++ /dev/null
@@ -1,15 +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/. -->
-
-<!ENTITY % brandDTD
-    SYSTEM "chrome://branding/locale/brand.dtd">
-    %brandDTD;
-
-<!-- These strings are used in the sync progress upload page -->
-<!ENTITY syncProgress.pageTitle    "Your First Sync">
-<!ENTITY syncProgress.textBlurb    "Your data is now being encrypted and uploaded in the background. You can close this tab and continue using &brandShortName;.">
-<!ENTITY syncProgress.closeButton  "Close">
-<!ENTITY syncProgress.logoAltText  "&brandShortName; logo">
-<!ENTITY syncProgress.diffText     "&brandShortName; will now automatically sync in the background.  You can close this tab and continue using &brandShortName;.">
-
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -15,17 +15,16 @@
     locale/browser/aboutHome.dtd                   (%chrome/browser/aboutHome.dtd)
     locale/browser/accounts.properties             (%chrome/browser/accounts.properties)
 #ifdef MOZ_SERVICES_HEALTHREPORT
     locale/browser/aboutHealthReport.dtd           (%chrome/browser/aboutHealthReport.dtd)
 #endif
     locale/browser/aboutSessionRestore.dtd         (%chrome/browser/aboutSessionRestore.dtd)
     locale/browser/aboutTabCrashed.dtd             (%chrome/browser/aboutTabCrashed.dtd)
 #ifdef MOZ_SERVICES_SYNC
-    locale/browser/syncProgress.dtd                (%chrome/browser/syncProgress.dtd)
     locale/browser/syncCustomize.dtd               (%chrome/browser/syncCustomize.dtd)
     locale/browser/aboutSyncTabs.dtd               (%chrome/browser/aboutSyncTabs.dtd)
 #endif
     locale/browser/browser.dtd                     (%chrome/browser/browser.dtd)
     locale/browser/baseMenuOverlay.dtd             (%chrome/browser/baseMenuOverlay.dtd)
     locale/browser/browser.properties              (%chrome/browser/browser.properties)
     locale/browser/customizableui/customizableWidgets.properties (%chrome/browser/customizableui/customizableWidgets.properties)
     locale/browser/devtools/animationinspector.dtd    (%chrome/browser/devtools/animationinspector.dtd)
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -418,17 +418,16 @@ browser.jar:
   skin/classic/browser/sync-mobileIcon.png
   skin/classic/browser/sync-notification-24.png
   skin/classic/browser/syncProgress-menuPanel.png
   skin/classic/browser/syncProgress-toolbar.png
   skin/classic/browser/syncProgress-toolbar-inverted.png
   skin/classic/browser/syncSetup.css
   skin/classic/browser/syncCommon.css
   skin/classic/browser/syncQuota.css
-  skin/classic/browser/syncProgress.css
   skin/classic/browser/syncProgress-horizontalbar.png
 #endif
   skin/classic/browser/notification-pluginNormal.png  (../shared/plugins/notification-pluginNormal.png)
   skin/classic/browser/notification-pluginAlert.png   (../shared/plugins/notification-pluginAlert.png)
   skin/classic/browser/notification-pluginBlocked.png (../shared/plugins/notification-pluginBlocked.png)
   skin/classic/browser/devtools/tooltip/arrow-horizontal-dark.png   (../shared/devtools/tooltip/arrow-horizontal-dark.png)
   skin/classic/browser/devtools/tooltip/arrow-horizontal-dark@2x.png   (../shared/devtools/tooltip/arrow-horizontal-dark@2x.png)
   skin/classic/browser/devtools/tooltip/arrow-vertical-dark.png   (../shared/devtools/tooltip/arrow-vertical-dark.png)
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -114,28 +114,32 @@ searchbar[oneoffui] .search-go-button:-m
 }
 
 
 .search-panel-current-engine {
   border-top: none !important;
   -moz-box-align: center;
 }
 
-.search-panel-current-engine:not([showonlysettings]) {
+.search-panel-current-engine {
   border-bottom: 1px solid #ccc;
 }
 
 .search-panel-header {
   font-weight: normal;
   background-color: rgb(245, 245, 245);
   border-top: 1px solid #ccc;
   padding: 3px 5px;
   color: #666;
 }
 
+.search-panel-tree[collapsed=true] + .search-panel-header {
+  border-top: none;
+}
+
 .search-panel-current-input > label {
   margin: 0 0 !important;
 }
 
 .search-panel-input-value {
   color: black;
 }
 
deleted file mode 100644
--- a/browser/themes/linux/syncProgress.css
+++ /dev/null
@@ -1,46 +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/. */
-@import url(chrome://global/skin/inContentUI.css);
-
-:root {
-  height: 100%;
-  width: 100%;
-  padding: 0;
-}
-
-body {
-  margin: 0;
-  padding: 0 2em;
-}
-
-#floatingBox {
-  margin: 4em auto;
-  max-width: 40em;
-  min-width: 23em;
-  padding: 1em 1.5em;
-  position: relative;
-  text-align: center;
-}
-
-#successLogo {
-  margin: 1em 2em;
-}
-
-#loadingText {
-  margin: 2em 6em;
-}
-
-#progressBar {
-  margin: 2em 10em;
-}
-
-#uploadProgressBar{
-  width: 100%;
-}
-
-#bottomRow {
-  margin-top: 2em;
-  padding: 0;
-  text-align: end;
-}
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -549,17 +549,16 @@ browser.jar:
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-horizontalbar.png
   skin/classic/browser/sync-horizontalbar@2x.png
   skin/classic/browser/sync-mobileIcon.png
   skin/classic/browser/sync-notification-24.png
   skin/classic/browser/syncSetup.css
   skin/classic/browser/syncCommon.css
   skin/classic/browser/syncQuota.css
-  skin/classic/browser/syncProgress.css
   skin/classic/browser/syncProgress-horizontalbar.png
   skin/classic/browser/syncProgress-horizontalbar@2x.png
   skin/classic/browser/syncProgress-menuPanel.png
   skin/classic/browser/syncProgress-menuPanel@2x.png
   skin/classic/browser/syncProgress-toolbar.png
   skin/classic/browser/syncProgress-toolbar@2x.png
   skin/classic/browser/syncProgress-toolbar-inverted.png
   skin/classic/browser/syncProgress-toolbar-inverted@2x.png
--- a/browser/themes/osx/searchbar.css
+++ b/browser/themes/osx/searchbar.css
@@ -137,30 +137,34 @@ searchbar[oneoffui] .search-go-button:-m
   }
 }
 
 .search-panel-current-engine {
   border-top: none !important;
   border-radius: 4px 4px 0 0;
 }
 
-.search-panel-current-engine:not([showonlysettings]) {
+.search-panel-current-engine {
   border-bottom: 1px solid #ccc;
 }
 
 .search-panel-header {
   font-size: 10px;
   font-weight: normal;
   background-color: rgb(245, 245, 245);
   border-top: 1px solid #ccc;
   margin: 0;
   padding: 3px 6px;
   color: #666;
 }
 
+.search-panel-tree[collapsed=true] + .search-panel-header {
+  border-top: none;
+}
+
 .search-panel-current-input > label {
   margin: 0 0 !important;
 }
 
 .search-panel-input-value {
   color: black;
 }
 
deleted file mode 100644
--- a/browser/themes/osx/syncProgress.css
+++ /dev/null
@@ -1,46 +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/. */
-@import url(chrome://global/skin/inContentUI.css);
-
-:root {
-  height: 100%;
-  width: 100%;
-  padding: 0;
-}
-
-body {
-  margin: 0;
-  padding: 0 2em;
-}
-
-#floatingBox {
-  margin: 4em auto;
-  max-width: 40em;
-  min-width: 23em;
-  padding: 1em 1.5em;
-  position: relative;
-  text-align: center;
-}
-
-#successLogo {
-  margin: 1em 2em;
-}
-
-#loadingText {
-  margin: 2em 6em;
-}
-
-#progressBar {
-  margin: 2em 10em;
-}
-
-#uploadProgressBar{
-  width: 100%;
-}
-
-#bottomRow {
-  margin-top: 2em;
-  padding: 0;
-  text-align: end;
-}
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -491,17 +491,16 @@ browser.jar:
         skin/classic/browser/sync-desktopIcon.png
         skin/classic/browser/sync-horizontalbar.png
         skin/classic/browser/sync-horizontalbar-XPVista7.png
         skin/classic/browser/sync-mobileIcon.png
         skin/classic/browser/sync-notification-24.png
         skin/classic/browser/syncSetup.css
         skin/classic/browser/syncCommon.css
         skin/classic/browser/syncQuota.css
-        skin/classic/browser/syncProgress.css
         skin/classic/browser/syncProgress-horizontalbar.png
         skin/classic/browser/syncProgress-horizontalbar-XPVista7.png
         skin/classic/browser/syncProgress-menuPanel.png
         skin/classic/browser/syncProgress-toolbar.png
         skin/classic/browser/syncProgress-toolbar@2x.png
         skin/classic/browser/syncProgress-toolbar-inverted.png
         skin/classic/browser/syncProgress-toolbar-inverted@2x.png
         skin/classic/browser/syncProgress-toolbar-XPVista7.png
--- a/browser/themes/windows/searchbar.css
+++ b/browser/themes/windows/searchbar.css
@@ -126,29 +126,33 @@ searchbar[oneoffui] .search-go-button:-m
 }
 
 
 .search-panel-current-engine {
   border-top: none !important;
   -moz-box-align: center;
 }
 
-.search-panel-current-engine:not([showonlysettings]) {
+.search-panel-current-engine {
   border-bottom: 1px solid #ccc;
 }
 
 .search-panel-header {
   font-weight: normal;
   background-color: rgb(245, 245, 245);
   border-top: 1px solid #ccc;
   margin: 0;
   padding: 3px 6px;
   color: #666;
 }
 
+.search-panel-tree[collapsed=true] + .search-panel-header {
+  border-top: none;
+}
+
 .search-panel-current-input > label {
   margin: 0 0 !important;
 }
 
 .search-panel-input-value {
   color: black;
 }
 
deleted file mode 100644
--- a/browser/themes/windows/syncProgress.css
+++ /dev/null
@@ -1,46 +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/. */
-@import url(chrome://global/skin/inContentUI.css);
-
-:root {
-  height: 100%;
-  width: 100%;
-  padding: 0;
-}
-
-body {
-  margin: 0;
-  padding: 0 2em;
-}
-
-#floatingBox {
-  margin: 4em auto;
-  max-width: 40em;
-  min-width: 23em;
-  padding: 1em 1.5em;
-  position: relative;
-  text-align: center;
-}
-
-#successLogo {
-  margin: 1em 2em;
-}
-
-#loadingText {
-  margin: 2em 6em;
-}
-
-#progressBar {
-  margin: 2em 10em;
-}
-
-#uploadProgressBar{
-  width: 100%;
-}
-
-#bottomRow {
-  margin-top: 2em;
-  padding: 0;
-  text-align: end;
-}
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2913,26 +2913,17 @@ ContentChild::RecvInvokeDragSession(nsTA
              do_CreateInstance(NS_VARIANT_CONTRACTID);
           NS_ENSURE_TRUE(variant, false);
           if (item.data().type() == IPCDataTransferData::TnsString) {
             const nsString& data = item.data().get_nsString();
             variant->SetAsAString(data);
           } else if (item.data().type() == IPCDataTransferData::TPBlobChild) {
             BlobChild* blob = static_cast<BlobChild*>(item.data().get_PBlobChild());
             nsRefPtr<FileImpl> fileImpl = blob->GetBlobImpl();
-            nsString path;
-            ErrorResult result;
-            fileImpl->GetMozFullPathInternal(path, result);
-            if (result.Failed()) {
-              variant->SetAsISupports(fileImpl);
-            } else {
-              nsCOMPtr<nsIFile> file;
-              NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, getter_AddRefs(file));
-              variant->SetAsISupports(file);
-            }
+            variant->SetAsISupports(fileImpl);
           } else {
             continue;
           }
           dataTransfer->SetDataWithPrincipal(NS_ConvertUTF8toUTF16(item.flavor()),
                                              variant, i,
                                              nsContentUtils::GetSystemPrincipal());
         }
       }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -15,17 +15,16 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
-import android.provider.Browser;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.gfx.BitmapUtils;
@@ -1431,28 +1430,28 @@ public abstract class GeckoApp
             Tabs.getInstance().loadUrl(AboutPages.HOME, flags);
         }
     }
 
     /**
      * Loads the initial tab at Fennec startup. This tab will load with the given
      * external URL. If that URL is invalid, about:home will be loaded.
      *
-     * @param url External URL to load.
-     * @param extraApplicationId Identifies the calling application; delivered with the URL
+     * @param url    External URL to load.
+     * @param intent External intent whose extras modify the request
+     * @param flags  Flags used to load the load
      */
-    protected void loadStartupTabWithExternalUrl(final String url, final String extraApplicationId,
-            final int flags) {
+    protected void loadStartupTab(final String url, final SafeIntent intent, final int flags) {
         // Invalid url
         if (url == null) {
             loadStartupTabWithAboutHome(flags);
             return;
         }
 
-        Tabs.getInstance().loadUrl(url, extraApplicationId, flags);
+        Tabs.getInstance().loadUrlWithIntentExtras(url, intent, flags);
     }
 
     private void initialize() {
         mInitialized = true;
 
         final SafeIntent intent = new SafeIntent(getIntent());
         final String action = intent.getAction();
 
@@ -1509,18 +1508,17 @@ public abstract class GeckoApp
         if (isExternalURL) {
             // Restore tabs before opening an external URL so that the new tab
             // is animated properly.
             Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
             int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_USER_ENTERED | Tabs.LOADURL_EXTERNAL;
             if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
                 flags |= Tabs.LOADURL_PINNED;
             }
-            final String extraApplicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
-            loadStartupTabWithExternalUrl(passedUri, extraApplicationId, flags);
+            loadStartupTab(passedUri, intent, flags);
         } else {
             if (!mIsRestoringActivity) {
                 loadStartupTabWithAboutHome(Tabs.LOADURL_NEW_TAB);
             }
 
             Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
         }
 
@@ -1824,21 +1822,20 @@ public abstract class GeckoApp
                                                                             Tabs.LOADURL_USER_ENTERED |
                                                                             Tabs.LOADURL_EXTERNAL);
                                 }
                             }
                         }, "Tabs:TabsOpened");
 
                         TabQueueHelper.openQueuedUrls(GeckoApp.this, mProfile, TabQueueHelper.FILE_NAME, true);
                     } else {
-                        String uri = intent.getDataString();
-                        final String extraApplicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
-                        Tabs.getInstance().loadUrl(uri, extraApplicationId, Tabs.LOADURL_NEW_TAB |
-                                                                            Tabs.LOADURL_USER_ENTERED |
-                                                                            Tabs.LOADURL_EXTERNAL);
+                        final String url = intent.getDataString();
+                        Tabs.getInstance().loadUrlWithIntentExtras(url, intent, Tabs.LOADURL_NEW_TAB |
+                                Tabs.LOADURL_USER_ENTERED |
+                                Tabs.LOADURL_EXTERNAL);
                     }
                 }
             });
         } else if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
             String uri = getURIFromIntent(intent);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBookmarkLoadEvent(uri));
         } else if (Intent.ACTION_SEARCH.equals(action)) {
             String uri = getURIFromIntent(intent);
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -8,34 +8,38 @@ package org.mozilla.gecko;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.json.JSONException;
 import org.json.JSONObject;
+
+import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
+import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
 import org.mozilla.gecko.mozglue.JNITarget;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.graphics.Color;
 import android.net.Uri;
 import android.os.Handler;
+import android.provider.Browser;
 import android.util.Log;
 
 public class Tabs implements GeckoEventListener {
     private static final String LOGTAG = "GeckoTabs";
 
     // mOrder and mTabs are always of the same cardinality, and contain the same values.
     private final CopyOnWriteArrayList<Tab> mOrder = new CopyOnWriteArrayList<Tab>();
 
@@ -808,38 +812,40 @@ public class Tabs implements GeckoEventL
      * @param flags flags used to load tab
      *
      * @return      the Tab if a new one was created; null otherwise
      */
     public Tab loadUrl(String url, int flags) {
         return loadUrl(url, null, -1, null, flags);
     }
 
-    public Tab loadUrl(final String url, final String applicationId, final int flags) {
-        return loadUrl(url, null, -1, applicationId, flags);
+    public Tab loadUrlWithIntentExtras(final String url, final SafeIntent intent, final int flags) {
+        // Note: we don't get the URL from the intent so the calling
+        // method has the opportunity to change the URL if applicable.
+        return loadUrl(url, null, -1, intent, flags);
     }
 
     public Tab loadUrl(final String url, final String searchEngine, final int parentId, final int flags) {
         return loadUrl(url, searchEngine, parentId, null, flags);
     }
 
     /**
      * Loads a tab with the given URL.
      *
-     * @param url           URL of page to load, or search term used if searchEngine is given
-     * @param searchEngine  if given, the search engine with this name is used
-     *                      to search for the url string; if null, the URL is loaded directly
-     * @param parentId      ID of this tab's parent, or -1 if it has no parent
-     * @param applicationId Identity of the calling application
-     * @param flags         flags used to load tab
+     * @param url          URL of page to load, or search term used if searchEngine is given
+     * @param searchEngine if given, the search engine with this name is used
+     *                     to search for the url string; if null, the URL is loaded directly
+     * @param parentId     ID of this tab's parent, or -1 if it has no parent
+     * @param intent       an intent whose extras are used to modify the request
+     * @param flags        flags used to load tab
      *
-     * @return              the Tab if a new one was created; null otherwise
+     * @return             the Tab if a new one was created; null otherwise
      */
     public Tab loadUrl(final String url, final String searchEngine, final int parentId,
-                   final String applicationId, final int flags) {
+                   final SafeIntent intent, final int flags) {
         JSONObject args = new JSONObject();
         Tab tabToSelect = null;
         boolean delayLoad = (flags & LOADURL_DELAY_LOAD) != 0;
 
         // delayLoad implies background tab
         boolean background = delayLoad || (flags & LOADURL_BACKGROUND) != 0;
 
         try {
@@ -852,21 +858,26 @@ public class Tabs implements GeckoEventL
             args.put("engine", searchEngine);
             args.put("parentId", parentId);
             args.put("userEntered", userEntered);
             args.put("isPrivate", isPrivate);
             args.put("pinned", (flags & LOADURL_PINNED) != 0);
             args.put("desktopMode", desktopMode);
 
             final boolean needsNewTab;
+            final String applicationId = (intent == null) ? null :
+                    intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
             if (applicationId == null) {
                 needsNewTab = (flags & LOADURL_NEW_TAB) != 0;
             } else {
+                // If you modify this code, be careful that intent != null.
+                final boolean extraCreateNewTab = (Versions.feature12Plus) ?
+                        intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false) : false;
                 final Tab applicationTab = getTabForApplicationId(applicationId);
-                if (applicationTab == null) {
+                if (applicationTab == null || extraCreateNewTab) {
                     needsNewTab = true;
                 } else {
                     needsNewTab = false;
                     delayLoad = false;
                     background = false;
 
                     tabToSelect = applicationTab;
                     final int tabToSelectId = tabToSelect.getId();
--- a/mobile/android/base/webapp/WebappImpl.java
+++ b/mobile/android/base/webapp/WebappImpl.java
@@ -181,28 +181,27 @@ public class WebappImpl extends GeckoApp
         // the URI from the intent data.  Otherwise, we should have been able
         // to get it from the APK resources.  So we should never get here.
         Log.wtf(LOGTAG, "Couldn't get URI from intent nor APK resources");
         return null;
     }
 
     @Override
     protected void loadStartupTabWithAboutHome(final int flags) {
-        loadStartupTabWithExternalUrl(null, null, flags);
+        loadStartupTab(null, null, flags);
     }
 
-    // Note: there is no support for applicationId in Webapps at
-    // the moment because I don't have time to debug/test.
+    // Note: there is no support for loading with intent extras in
+    // Webapps at the moment because I don't have time to debug/test.
     @Override
-    protected void loadStartupTabWithExternalUrl(final String uri, final String applicationId,
-            int flags) {
+    protected void loadStartupTab(final String uri, final SafeIntent unusedIntent, int flags) {
         // Load a tab so it's available for any code that assumes a tab
         // before the app tab itself is loaded in BrowserApp._loadWebapp.
         flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_USER_ENTERED | Tabs.LOADURL_EXTERNAL;
-        super.loadStartupTabWithExternalUrl("about:blank", null, flags);
+        super.loadStartupTab("about:blank", null, flags);
     }
 
     private void showSplash() {
 
         // get the favicon dominant color, stored when the app was installed
         int dominantColor = Allocator.getInstance().getColor(getIndex());
 
         setBackgroundGradient(dominantColor);
--- a/mobile/android/chrome/content/Reader.js
+++ b/mobile/android/chrome/content/Reader.js
@@ -283,17 +283,17 @@ let Reader = {
       return article;
     }
 
     // Article hasn't been found in the cache, we need to
     // download the page and parse the article out of it.
     return yield ReaderMode.downloadAndParseDocument(url).catch(e => {
       Cu.reportError("Error downloading and parsing document: " + e);
       return null;
-    });;
+    });
   }),
 
   _getSavedArticle: function(browser) {
     return new Promise((resolve, reject) => {
       let mm = browser.messageManager;
       let listener = (message) => {
         mm.removeMessageListener("Reader:SavedArticleData", listener);
         resolve(message.data.article);
--- a/mobile/android/themes/core/aboutPasswords.css
+++ b/mobile/android/themes/core/aboutPasswords.css
@@ -79,17 +79,17 @@
   background-size: 12px 12px;
   background-repeat: no-repeat;
   height: 32px;
   width: 32px;
   margin: 0 5px;
 }
 
 .icon {
-  background-image: url("resource://android/res/drawable-mdpi-v4/favicon.png");
+  background-image: url("resource://android/res/drawable-mdpi-v4/favicon_globe.png");
   background-position: center;
   background-size: 32px 32px;
   background-repeat: no-repeat;
   height: 32px;
   width: 32px;
   visibility: hidden;
 }
 
@@ -98,25 +98,25 @@
     background-image: url("resource://android/res/drawable-hdpi-v4/ab_search.png");
   }
 
   #filter-clear {
     background-image: url("resource://android/res/drawable-hdpi-v4/close_edit_mode_light.png");
   }
 
   .icon {
-    background-image: url("resource://android/res/drawable-hdpi-v4/favicon.png");
+    background-image: url("resource://android/res/drawable-hdpi-v4/favicon_globe.png");
   }
 }
 
 @media screen and (min-resolution: 2dppx) {
   #filter-button {
     background-image: url("resource://android/res/drawable-xhdpi-v4/ab_search.png");
   }
 
   #filter-clear {
     background-image: url("resource://android/res/drawable-hdpi-v4/close_edit_mode_light.png");
   }
 
   .icon {
-    background-image: url("resource://android/res/drawable-xhdpi-v4/favicon.png");
+    background-image: url("resource://android/res/drawable-xhdpi-v4/favicon_globe.png");
   }
 }
--- a/toolkit/components/search/nsSearchSuggestions.js
+++ b/toolkit/components/search/nsSearchSuggestions.js
@@ -69,23 +69,16 @@ SuggestAutoComplete.prototype = {
       // "comments" column values for suggestions starts as empty strings
       let comments = new Array(results.remote.length).fill("", 1);
       comments[0] = this._suggestionLabel;
       // now put the history results above the suggestions
       finalResults = finalResults.concat(results.remote);
       finalComments = finalComments.concat(comments);
     }
 
-    // If no result, add the search term so that the panel of the new UI is shown anyway.
-    if (!finalResults.length &&
-        Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
-      finalResults.push(results.term);
-      finalComments.push("");
-    }
-
     // Notify the FE of our new results
     this.onResultsReady(results.term, finalResults, finalComments, results.formHistoryResult);
   },
 
   /**
    * Notifies the front end of new results.
    * @param searchString  the user's query string
    * @param results       an array of results to the search
--- a/toolkit/components/viewsource/content/viewPartialSource.js
+++ b/toolkit/components/viewsource/content/viewPartialSource.js
@@ -169,24 +169,24 @@ function viewPartialSourceForSelection(s
   // before drawing the selection.
   if (canDrawSelection) {
     window.document.getElementById("content").addEventListener("load", drawSelection, true);
   }
 
   // all our content is held by the data:URI and URIs are internally stored as utf-8 (see nsIURI.idl)
   var loadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE;
   var referrerPolicy = Components.interfaces.nsIHttpChannel.REFERRER_POLICY_DEFAULT;
-  getWebNavigation().loadURIWithOptions((isHTML ?
-                                         "view-source:data:text/html;charset=utf-8," :
-                                         "view-source:data:application/xml;charset=utf-8,")
-                                        + encodeURIComponent(tmpNode.innerHTML),
-                                        loadFlags,
-                                        null, referrerPolicy,  // referrer
-                                        null, null,  // postData, headers
-                                        Services.io.newURI(doc.baseURI, null, null));
+  ViewSourceChrome.webNav.loadURIWithOptions((isHTML ?
+                                              "view-source:data:text/html;charset=utf-8," :
+                                              "view-source:data:application/xml;charset=utf-8,")
+                                             + encodeURIComponent(tmpNode.innerHTML),
+                                             loadFlags,
+                                             null, referrerPolicy,  // referrer
+                                             null, null,  // postData, headers
+                                             Services.io.newURI(doc.baseURI, null, null));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // helper to get a path like FIXptr, but with an array instead of the "tumbler" notation
 // see FIXptr: http://lists.w3.org/Archives/Public/www-xml-linking-comments/2001AprJun/att-0074/01-NOTE-FIXptr-20010425.htm
 function getPath(ancestor, node)
 {
   var n = node;
--- a/toolkit/components/viewsource/content/viewPartialSource.xul
+++ b/toolkit/components/viewsource/content/viewPartialSource.xul
@@ -45,19 +45,19 @@
   <command id="cmd_close" oncommand="window.close();"/>
   <commandset id="editMenuCommands"/>
   <command id="cmd_find"
            oncommand="document.getElementById('FindToolbar').onFindCommand();"/>
   <command id="cmd_findAgain"
            oncommand="document.getElementById('FindToolbar').onFindAgainCommand(false);"/>
   <command id="cmd_findPrevious"
            oncommand="document.getElementById('FindToolbar').onFindAgainCommand(true);"/>
-  <command id="cmd_goToLine" oncommand="ViewSourceGoToLine();" disabled="true"/>
-  <command id="cmd_highlightSyntax" oncommand="highlightSyntax();"/>
-  <command id="cmd_wrapLongLines" oncommand="wrapLongLines()"/>
+  <command id="cmd_goToLine" oncommand="ViewSourceChrome.promptAndGoToLine();" disabled="true"/>
+  <command id="cmd_highlightSyntax" oncommand="ViewSourceChrome.toggleSyntaxHighlighting();"/>
+  <command id="cmd_wrapLongLines" oncommand="ViewSourceChrome.toggleWrapping();"/>
   <command id="cmd_textZoomReduce" oncommand="ZoomManager.reduce();"/>
   <command id="cmd_textZoomEnlarge" oncommand="ZoomManager.enlarge();"/>
   <command id="cmd_textZoomReset" oncommand="ZoomManager.reset();"/>
 
   <keyset id="editMenuKeys"/>
   <keyset id="viewSourceKeys">
     <key id="key_savePage" key="&savePageCmd.commandkey;" modifiers="accel" command="cmd_savePage"/>
     <key id="key_print" key="&printCmd.commandkey;" modifiers="accel" command="cmd_print"/>
@@ -68,29 +68,28 @@
     <key id="key_textZoomEnlarge2" key="&textEnlarge.commandkey2;" command="cmd_textZoomEnlarge" modifiers="accel"/>
     <key id="key_textZoomEnlarge3" key="&textEnlarge.commandkey3;" command="cmd_textZoomEnlarge" modifiers="accel"/>
     <key id="key_textZoomReduce"  key="&textReduce.commandkey;" command="cmd_textZoomReduce" modifiers="accel"/>
     <key id="key_textZoomReduce2"  key="&textReduce.commandkey2;" command="cmd_textZoomReduce" modifiers="accel"/>
     <key id="key_textZoomReset" key="&textReset.commandkey;" command="cmd_textZoomReset" modifiers="accel"/>
     <key id="key_textZoomReset2" key="&textReset.commandkey2;" command="cmd_textZoomReset" modifiers="accel"/>
   </keyset>
 
-  <menupopup id="viewSourceContextMenu"
-             onpopupshowing="contextMenuShowing();">
+  <menupopup id="viewSourceContextMenu">
     <menuitem id="cMenu_findAgain"/>
     <menuseparator/>
     <menuitem id="cMenu_copy"/>
     <menuitem id="context-copyLink"
               label="&copyLinkCmd.label;"
               accesskey="&copyLinkCmd.accesskey;"
-              oncommand="contextMenuCopyLinkOrEmail();"/>
+              oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
     <menuitem id="context-copyEmail"
               label="&copyEmailCmd.label;"
               accesskey="&copyEmailCmd.accesskey;"
-              oncommand="contextMenuCopyLinkOrEmail();"/>
+              oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
     <menuseparator/>
     <menuitem id="cMenu_selectAll"/>
   </menupopup>
 
   <!-- Menu --> 
   <toolbox id="viewSource-toolbox">
     <menubar id="viewSource-main-menubar">
 
@@ -152,13 +151,13 @@
                     label="&menu_highlightSyntax.label;" accesskey="&menu_highlightSyntax.accesskey;"/>
         </menupopup>
       </menu>
     </menubar>  
   </toolbox>
 
   <vbox id="appcontent" flex="1">
     <browser id="content" type="content-primary" name="content" src="about:blank" flex="1"
-             disablehistory="true" context="viewSourceContextMenu"/>
+             disablehistory="true" context="viewSourceContextMenu" />
     <findbar id="FindToolbar" browserid="content"/>
   </vbox>
 
 </window>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viewsource/content/viewSource-content.js
@@ -0,0 +1,703 @@
+/* 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/. */
+
+const { utils: Cu, interfaces: Ci, classes: Cc } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
+  "resource://gre/modules/BrowserUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
+  "resource://gre/modules/DeferredTask.jsm");
+
+const BUNDLE_URL = "chrome://global/locale/viewSource.properties";
+
+let global = this;
+
+/**
+ * ViewSourceContent should be loaded in the <xul:browser> of the
+ * view source window, and initialized as soon as it has loaded.
+ */
+let ViewSourceContent = {
+  /**
+   * We'll act as an nsISelectionListener as well so that we can send
+   * updates to the view source window's status bar.
+   */
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISelectionListener]),
+
+  /**
+   * These are the messages that ViewSourceContent is prepared to listen
+   * for. If you need ViewSourceContent to handle more messages, add them
+   * here.
+   */
+  messages: [
+    "ViewSource:LoadSource",
+    "ViewSource:LoadSourceDeprecated",
+    "ViewSource:GoToLine",
+    "ViewSource:ToggleWrapping",
+    "ViewSource:ToggleSyntaxHighlighting",
+    "ViewSource:SetCharacterSet",
+  ],
+
+  /**
+   * ViewSourceContent is attached as an nsISelectionListener on pageshow,
+   * and removed on pagehide. When the initial about:blank is transitioned
+   * away from, a pagehide is fired without us having attached ourselves
+   * first. We use this boolean to keep track of whether or not we're
+   * attached, so we don't attempt to remove our listener when it's not
+   * yet there (which throws).
+   */
+  selectionListenerAttached: false,
+
+  /**
+   * This should be called as soon as this frame script has loaded.
+   */
+  init() {
+    this.messages.forEach((msgName) => {
+      addMessageListener(msgName, this);
+    });
+
+    addEventListener("pagehide", this, true);
+    addEventListener("pageshow", this, true);
+    addEventListener("click", this);
+    addEventListener("unload", this);
+    Services.els.addSystemEventListener(global, "contextmenu", this, false);
+  },
+
+  /**
+   * This should be called when the frame script is being unloaded,
+   * and the browser is tearing down.
+   */
+  uninit() {
+    this.messages.forEach((msgName) => {
+      removeMessageListener(msgName, this);
+    });
+
+    removeEventListener("pagehide", this, true);
+    removeEventListener("pageshow", this, true);
+    removeEventListener("click", this);
+    removeEventListener("unload", this);
+
+    Services.els.removeSystemEventListener(global, "contextmenu", this, false);
+
+    // Cancel any pending toolbar updates.
+    if (this.updateStatusTask) {
+      this.updateStatusTask.disarm();
+    }
+  },
+
+  /**
+   * Anything added to the messages array will get handled here, and should
+   * get dispatched to a specific function for the message name.
+   */
+  receiveMessage(msg) {
+    let data = msg.data;
+    let objects = msg.objects;
+    switch(msg.name) {
+      case "ViewSource:LoadSource":
+        this.viewSource(data.URL, data.outerWindowID, data.lineNumber,
+                        data.shouldWrap);
+        break;
+      case "ViewSource:LoadSourceDeprecated":
+        this.viewSourceDeprecated(data.URL, objects.pageDescriptor, data.lineNumber,
+                                  data.forcedCharSet);
+        break;
+      case "ViewSource:GoToLine":
+        this.goToLine(data.lineNumber);
+        break;
+      case "ViewSource:ToggleWrapping":
+        this.toggleWrapping();
+        break;
+      case "ViewSource:ToggleSyntaxHighlighting":
+        this.toggleSyntaxHighlighting();
+        break;
+      case "ViewSource:SetCharacterSet":
+        this.setCharacterSet(data.charset, data.doPageLoad);
+        break;
+    }
+  },
+
+  /**
+   * Any events should get handled here, and should get dispatched to
+   * a specific function for the event type.
+   */
+  handleEvent(event) {
+    switch(event.type) {
+      case "pagehide":
+        this.onPageHide(event);
+        break;
+      case "pageshow":
+        this.onPageShow(event);
+        break;
+      case "click":
+        this.onClick(event);
+        break;
+      case "unload":
+        this.uninit();
+        break;
+      case "contextmenu":
+        this.onContextMenu(event);
+        break;
+    }
+  },
+
+  /**
+   * A getter for the view source string bundle.
+   */
+  get bundle() {
+    delete this.bundle;
+    this.bundle = Services.strings.createBundle(BUNDLE_URL);
+    return this.bundle;
+  },
+
+  /**
+   * A shortcut to the nsISelectionController for the content.
+   */
+  get selectionController() {
+    return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsISelectionDisplay)
+                   .QueryInterface(Ci.nsISelectionController);
+  },
+
+  /**
+   * Called when the parent sends a message to view some source code.
+   *
+   * @param URL (required)
+   *        The URL string of the source to be shown.
+   * @param outerWindowID (optional)
+   *        The outerWindowID of the content window that has hosted
+   *        the document, in case we want to retrieve it from the network
+   *        cache.
+   * @param lineNumber (optional)
+   *        The line number to focus as soon as the source has finished
+   *        loading.
+   */
+  viewSource(URL, outerWindowID, lineNumber) {
+    let pageDescriptor, forcedCharSet;
+
+    if (outerWindowID) {
+      let contentWindow = Services.wm.getOuterWindowWithId(outerWindowID);
+      let requestor = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor);
+
+      try {
+        let otherWebNav = requestor.getInterface(Ci.nsIWebNavigation);
+        pageDescriptor = otherWebNav.QueryInterface(Ci.nsIWebPageDescriptor)
+                                    .currentDescriptor;
+      } catch(e) {
+        // We couldn't get the page descriptor, so we'll probably end up re-retrieving
+        // this document off of the network.
+      }
+
+      let utils = requestor.getInterface(Ci.nsIDOMWindowUtils);
+      let doc = contentWindow.document;
+      let forcedCharSet = utils.docCharsetIsForced ? doc.characterSet
+                                                   : null;
+    }
+
+    this.loadSource(URL, pageDescriptor, lineNumber, forcedCharSet);
+  },
+
+  /**
+   * Called when the parent is using the deprecated API for viewSource.xul.
+   * This function will throw if it's called on a remote browser.
+   *
+   * @param URL (required)
+   *        The URL string of the source to be shown.
+   * @param pageDescriptor (optional)
+   *        The currentDescriptor off of an nsIWebPageDescriptor, in the
+   *        event that the caller wants to try to load the source out of
+   *        the network cache.
+   * @param lineNumber (optional)
+   *        The line number to focus as soon as the source has finished
+   *        loading.
+   * @param forcedCharSet (optional)
+   *        The document character set to use instead of the default one.
+   */
+  viewSourceDeprecated(URL, pageDescriptor, lineNumber, forcedCharSet) {
+    // This should not be called if this frame script is running
+    // in a content process!
+    if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT) {
+      throw new Error("ViewSource deprecated API should not be used with " +
+                      "remote browsers.");
+    }
+
+    this.loadSource(URL, pageDescriptor, lineNumber, forcedCharSet);
+  },
+
+  /**
+   * Common utility function used by both the current and deprecated APIs
+   * for loading source.
+   *
+   * @param URL (required)
+   *        The URL string of the source to be shown.
+   * @param pageDescriptor (optional)
+   *        The currentDescriptor off of an nsIWebPageDescriptor, in the
+   *        event that the caller wants to try to load the source out of
+   *        the network cache.
+   * @param lineNumber (optional)
+   *        The line number to focus as soon as the source has finished
+   *        loading.
+   * @param forcedCharSet (optional)
+   *        The document character set to use instead of the default one.
+   */
+  loadSource(URL, pageDescriptor, lineNumber, forcedCharSet) {
+    const viewSrcURL = "view-source:" + URL;
+    let loadFromURL = false;
+
+    if (forcedCharSet) {
+      docShell.charset = forcedCharSet;
+    }
+
+    if (lineNumber) {
+      let doneLoading = (event) => {
+        this.goToLine(lineNumber);
+        removeEventListener("pageshow", doneLoading);
+      };
+
+      addEventListener("pageshow", doneLoading);
+    }
+
+    if (!pageDescriptor) {
+      this.loadSourceFromURL(viewSrcURL);
+      return;
+    }
+
+    try {
+      let pageLoader = docShell.QueryInterface(Ci.nsIWebPageDescriptor);
+      pageLoader.loadPage(pageDescriptor,
+                          Ci.nsIWebPageDescriptor.DISPLAY_AS_SOURCE);
+    } catch(e) {
+      // We were not able to load the source from the network cache.
+      this.loadSourceFromURL(viewSrcURL);
+      return;
+    }
+
+    let shEntrySource = pageDescriptor.QueryInterface(Ci.nsISHEntry);
+    let shEntry = Cc["@mozilla.org/browser/session-history-entry;1"]
+                    .createInstance(Ci.nsISHEntry);
+    shEntry.setURI(BrowserUtils.makeURI(viewSrcURL, null, null));
+    shEntry.setTitle(viewSrcURL);
+    shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
+    shEntry.cacheKey = shEntrySource.cacheKey;
+    docShell.QueryInterface(Ci.nsIWebNavigation)
+            .sessionHistory
+            .QueryInterface(Ci.nsISHistoryInternal)
+            .addEntry(shEntry, true);
+  },
+
+  /**
+   * Load some URL in the browser.
+   *
+   * @param URL
+   *        The URL string to load.
+   */
+  loadSourceFromURL(URL) {
+    let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+    let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
+    webNav.loadURI(URL, loadFlags, null, null, null);
+  },
+
+  /**
+   * This handler is specifically for click events bubbling up from
+   * error page content, which can show up if the user attempts to
+   * view the source of an attack page.
+   */
+  onClick(event) {
+    // Don't trust synthetic events
+    if (!event.isTrusted || event.target.localName != "button")
+      return;
+
+    let target = event.originalTarget;
+    let errorDoc = target.ownerDocument;
+
+    if (/^about:blocked/.test(errorDoc.documentURI)) {
+      // The event came from a button on a malware/phishing block page
+
+      if (target == errorDoc.getElementById("getMeOutButton")) {
+        // Instead of loading some safe page, just close the window
+        sendAsyncMessage("ViewSource:Close");
+      } else if (target == errorDoc.getElementById("reportButton")) {
+        // This is the "Why is this site blocked" button. We redirect
+        // to the generic page describing phishing/malware protection.
+        let URL = Services.urlFormatter.formatURLPref("app.support.baseURL");
+        sendAsyncMessage("ViewSource:OpenURL", { URL })
+      } else if (target == errorDoc.getElementById("ignoreWarningButton")) {
+        // Allow users to override and continue through to the site
+        docShell.QueryInterface(Ci.nsIWebNavigation)
+                .loadURIWithOptions(content.location.href,
+                                    Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
+                                    null, Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT,
+                                    null, null, null);
+      }
+    }
+  },
+
+  /**
+   * Handler for the pageshow event.
+   *
+   * @param event
+   *        The pageshow event being handled.
+   */
+  onPageShow(event) {
+    content.getSelection()
+           .QueryInterface(Ci.nsISelectionPrivate)
+           .addSelectionListener(this);
+    this.selectionListenerAttached = true;
+
+    content.focus();
+    sendAsyncMessage("ViewSource:SourceLoaded");
+  },
+
+  /**
+   * Handler for the pagehide event.
+   *
+   * @param event
+   *        The pagehide event being handled.
+   */
+  onPageHide(event) {
+    // The initial about:blank will fire pagehide before we
+    // ever set a selectionListener, so we have a boolean around
+    // to keep track of when the listener is attached.
+    if (this.selectionListenerAttached) {
+      content.getSelection()
+             .QueryInterface(Ci.nsISelectionPrivate)
+             .removeSelectionListener(this);
+      this.selectionListenerAttached = false;
+    }
+    sendAsyncMessage("ViewSource:SourceUnloaded");
+  },
+
+  onContextMenu(event) {
+    let addonInfo = {};
+    let subject = {
+      event: event,
+      addonInfo: addonInfo,
+    };
+
+    subject.wrappedJSObject = subject;
+    Services.obs.notifyObservers(subject, "content-contextmenu", null);
+
+    let node = event.target;
+
+    let result = {
+      isEmail: false,
+      isLink: false,
+      href: "",
+      // We have to pass these in the event that we're running in
+      // a remote browser, so that ViewSourceChrome knows where to
+      // open the context menu.
+      screenX: event.screenX,
+      screenY: event.screenY,
+    };
+
+    if (node && node.localName == "a") {
+      result.isLink = node.href.startsWith("view-source:");
+      result.isEmail = node.href.startsWith("mailto:");
+      result.href = node.href.substring(node.href.indexOf(":") + 1);
+    }
+
+    sendSyncMessage("ViewSource:ContextMenuOpening", result);
+  },
+
+  /**
+   * Attempts to go to a particular line in the source code being
+   * shown. If it succeeds in finding the line, it will fire a
+   * "ViewSource:GoToLine:Success" message, passing up an object
+   * with the lineNumber we just went to. If it cannot find the line,
+   * it will fire a "ViewSource:GoToLine:Failed" message.
+   *
+   * @param lineNumber
+   *        The line number to attempt to go to.
+   */
+  goToLine(lineNumber) {
+    let body = content.document.body;
+
+    // The source document is made up of a number of pre elements with
+    // id attributes in the format <pre id="line123">, meaning that
+    // the first line in the pre element is number 123.
+    // Do binary search to find the pre element containing the line.
+    // However, in the plain text case, we have only one pre without an
+    // attribute, so assume it begins on line 1.
+    let pre;
+    for (let lbound = 0, ubound = body.childNodes.length; ; ) {
+      let middle = (lbound + ubound) >> 1;
+      pre = body.childNodes[middle];
+
+      let firstLine = pre.id ? parseInt(pre.id.substring(4)) : 1;
+
+      if (lbound == ubound - 1) {
+        break;
+      }
+
+      if (lineNumber >= firstLine) {
+        lbound = middle;
+      } else {
+        ubound = middle;
+      }
+    }
+
+    let result = {};
+    let found = this.findLocation(pre, lineNumber, null, -1, false, result);
+
+    if (!found) {
+      sendAsyncMessage("ViewSource:GoToLine:Failed");
+      return;
+    }
+
+    let selection = content.getSelection();
+    selection.removeAllRanges();
+
+    // In our case, the range's startOffset is after "\n" on the previous line.
+    // Tune the selection at the beginning of the next line and do some tweaking
+    // to position the focusNode and the caret at the beginning of the line.
+    selection.QueryInterface(Ci.nsISelectionPrivate)
+      .interlinePosition = true;
+
+    selection.addRange(result.range);
+
+    if (!selection.isCollapsed) {
+      selection.collapseToEnd();
+
+      let offset = result.range.startOffset;
+      let node = result.range.startContainer;
+      if (offset < node.data.length) {
+        // The same text node spans across the "\n", just focus where we were.
+        selection.extend(node, offset);
+      }
+      else {
+        // There is another tag just after the "\n", hook there. We need
+        // to focus a safe point because there are edgy cases such as
+        // <span>...\n</span><span>...</span> vs.
+        // <span>...\n<span>...</span></span><span>...</span>
+        node = node.nextSibling ? node.nextSibling : node.parentNode.nextSibling;
+        selection.extend(node, 0);
+      }
+    }
+
+    let selCon = this.selectionController;
+    selCon.setDisplaySelection(Ci.nsISelectionController.SELECTION_ON);
+    selCon.setCaretVisibilityDuringSelection(true);
+
+    // Scroll the beginning of the line into view.
+    selCon.scrollSelectionIntoView(
+      Ci.nsISelectionController.SELECTION_NORMAL,
+      Ci.nsISelectionController.SELECTION_FOCUS_REGION,
+      true);
+
+    sendAsyncMessage("ViewSource:GoToLine:Success", { lineNumber });
+  },
+
+
+  /**
+   * Some old code from the original view source implementation. Original
+   * documentation follows:
+   *
+   * "Loops through the text lines in the pre element. The arguments are either
+   *  (pre, line) or (node, offset, interlinePosition). result is an out
+   *  argument. If (pre, line) are specified (and node == null), result.range is
+   *  a range spanning the specified line. If the (node, offset,
+   *  interlinePosition) are specified, result.line and result.col are the line
+   *  and column number of the specified offset in the specified node relative to
+   *  the whole file."
+   */
+  findLocation(pre, lineNumber, node, offset, interlinePosition, result) {
+    if (node && !pre) {
+      // Look upwards to find the current pre element.
+      for (pre = node;
+           pre.nodeName != "PRE";
+           pre = pre.parentNode);
+    }
+
+    // The source document is made up of a number of pre elements with
+    // id attributes in the format <pre id="line123">, meaning that
+    // the first line in the pre element is number 123.
+    // However, in the plain text case, there is only one <pre> without an id,
+    // so assume line 1.
+    let curLine = pre.id ? parseInt(pre.id.substring(4)) : 1;
+
+    // Walk through each of the text nodes and count newlines.
+    let treewalker = content.document
+        .createTreeWalker(pre, Ci.nsIDOMNodeFilter.SHOW_TEXT, null);
+
+    // The column number of the first character in the current text node.
+    let firstCol = 1;
+
+    let found = false;
+    for (let textNode = treewalker.firstChild();
+         textNode && !found;
+         textNode = treewalker.nextNode()) {
+
+      // \r is not a valid character in the DOM, so we only check for \n.
+      let lineArray = textNode.data.split(/\n/);
+      let lastLineInNode = curLine + lineArray.length - 1;
+
+      // Check if we can skip the text node without further inspection.
+      if (node ? (textNode != node) : (lastLineInNode < lineNumber)) {
+        if (lineArray.length > 1) {
+          firstCol = 1;
+        }
+        firstCol += lineArray[lineArray.length - 1].length;
+        curLine = lastLineInNode;
+        continue;
+      }
+
+      // curPos is the offset within the current text node of the first
+      // character in the current line.
+      for (var i = 0, curPos = 0;
+           i < lineArray.length;
+           curPos += lineArray[i++].length + 1) {
+
+        if (i > 0) {
+          curLine++;
+        }
+
+        if (node) {
+          if (offset >= curPos && offset <= curPos + lineArray[i].length) {
+            // If we are right after the \n of a line and interlinePosition is
+            // false, the caret looks as if it were at the end of the previous
+            // line, so we display that line and column instead.
+
+            if (i > 0 && offset == curPos && !interlinePosition) {
+              result.line = curLine - 1;
+              var prevPos = curPos - lineArray[i - 1].length;
+              result.col = (i == 1 ? firstCol : 1) + offset - prevPos;
+            } else {
+              result.line = curLine;
+              result.col = (i == 0 ? firstCol : 1) + offset - curPos;
+            }
+            found = true;
+
+            break;
+          }
+
+        } else {
+          if (curLine == lineNumber && !("range" in result)) {
+            result.range = content.document.createRange();
+            result.range.setStart(textNode, curPos);
+
+            // This will always be overridden later, except when we look for
+            // the very last line in the file (this is the only line that does
+            // not end with \n).
+            result.range.setEndAfter(pre.lastChild);
+
+          } else if (curLine == lineNumber + 1) {
+            result.range.setEnd(textNode, curPos - 1);
+            found = true;
+            break;
+          }
+        }
+      }
+    }
+
+    return found || ("range" in result);
+  },
+
+  /**
+   * Toggles the "wrap" class on the document body, which sets whether
+   * or not long lines are wrapped.
+   */
+  toggleWrapping() {
+    let body = content.document.body;
+    body.classList.toggle("wrap");
+  },
+
+  /**
+   * Called when the parent has changed the syntax highlighting pref.
+   */
+  toggleSyntaxHighlighting() {
+    // The parent process should have set the view_source.syntax_highlight
+    // pref to the desired value. The reload brings that setting into
+    // effect.
+    this.reload();
+  },
+
+  /**
+   * Called when the parent has changed the character set to view the
+   * source with.
+   *
+   * @param charset
+   *        The character set to use.
+   * @param doPageLoad
+   *        Whether or not we should reload the page ourselves with the
+   *        nsIWebPageDescriptor. Part of a workaround for bug 136322.
+   */
+  setCharacterSet(charset, doPageLoad) {
+    docShell.charset = charset;
+    if (doPageLoad) {
+      this.reload();
+    }
+  },
+
+  /**
+   * Reloads the content.
+   */
+  reload() {
+    let pageLoader = docShell.QueryInterface(Ci.nsIWebPageDescriptor);
+    try {
+      pageLoader.loadPage(pageLoader.currentDescriptor,
+                          Ci.nsIWebPageDescriptor.DISPLAY_NORMAL);
+    } catch(e) {
+      let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
+      webNav.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
+    }
+  },
+
+  /**
+   * A reference to a DeferredTask that is armed every time the
+   * selection changes.
+   */
+  updateStatusTask: null,
+
+  /**
+   * Called once the DeferredTask fires. Sends a message up to the
+   * parent to update the status bar text.
+   */
+  updateStatus() {
+    let selection = content.getSelection();
+
+    if (!selection.focusNode) {
+      sendAsyncMessage("ViewSource:UpdateStatus", { label: "" });
+      return;
+    }
+    if (selection.focusNode.nodeType != Ci.nsIDOMNode.TEXT_NODE) {
+      return;
+    }
+
+    let selCon = this.selectionController;
+    selCon.setDisplaySelection(Ci.nsISelectionController.SELECTION_ON);
+    selCon.setCaretVisibilityDuringSelection(true);
+
+    let interlinePosition = selection.QueryInterface(Ci.nsISelectionPrivate)
+                                     .interlinePosition;
+
+    let result = {};
+    this.findLocation(null, -1,
+        selection.focusNode, selection.focusOffset, interlinePosition, result);
+
+    let label = this.bundle.formatStringFromName("statusBarLineCol",
+                                                 [result.line, result.col], 2);
+    sendAsyncMessage("ViewSource:UpdateStatus", { label });
+  },
+
+  /**
+   * nsISelectionListener
+   */
+
+  /**
+   * Gets called every time the selection is changed. Coalesces frequent
+   * changes, and calls updateStatus after 100ms of no selection change
+   * activity.
+   */
+  notifySelectionChanged(doc, sel, reason) {
+    if (!this.updateStatusTask) {
+      this.updateStatusTask = new DeferredTask(() => {
+        this.updateStatus();
+      }, 100);
+    }
+
+    this.updateStatusTask.arm();
+  },
+};
+ViewSourceContent.init();
--- a/toolkit/components/viewsource/content/viewSource.css
+++ b/toolkit/components/viewsource/content/viewSource.css
@@ -1,7 +1,11 @@
 /* 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/. */
 
 toolbar[printpreview="true"] {
   -moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
 }
+
+browser[remote="true"] {
+  -moz-binding: url("chrome://global/content/bindings/remote-browser.xml#remote-browser");
+}
\ No newline at end of file
--- a/toolkit/components/viewsource/content/viewSource.js
+++ b/toolkit/components/viewsource/content/viewSource.js
@@ -1,703 +1,935 @@
 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 
 /* 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/. */
 
-Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.import("resource://gre/modules/CharsetMenu.jsm");
+const { utils: Cu, interfaces: Ci, classes: Cc } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-var gLastLineFound = '';
-var gGoToLine = 0;
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
+  "resource://gre/modules/CharsetMenu.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
+  "resource://gre/modules/Deprecated.jsm");
 
 [
   ["gBrowser",          "content"],
   ["gViewSourceBundle", "viewSourceBundle"],
   ["gContextMenu",      "viewSourceContextMenu"]
 ].forEach(function ([name, id]) {
   window.__defineGetter__(name, function () {
     var element = document.getElementById(id);
     if (!element)
       return null;
     delete window[name];
     return window[name] = element;
   });
 });
 
-// viewZoomOverlay.js uses this
-function getBrowser() {
-  return gBrowser;
-}
+/**
+ * ViewSourceChrome is the primary interface for interacting with
+ * the view source browser. It initializes itself on script load.
+ */
+let ViewSourceChrome = {
+  /**
+   * Holds the value of the last line found via the "Go to line"
+   * command, to pre-populate the prompt the next time it is
+   * opened.
+   */
+  lastLineFound: null,
+
+  /**
+   * The context menu, when opened from the content process, sends
+   * up a chunk of serialized data describing the items that the
+   * context menu is being opened on. This allows us to avoid using
+   * CPOWs.
+   */
+  contextMenuData: {},
 
-this.__defineGetter__("gPageLoader", function () {
-  var webnav = getWebNavigation();
-  if (!webnav)
-    return null;
-  delete this.gPageLoader;
-  return this.gPageLoader = webnav.QueryInterface(Ci.nsIWebPageDescriptor);
-});
+  /**
+   * These are the messages that ViewSourceChrome will listen for
+   * from the frame script it injects. Any message names added here
+   * will automatically have ViewSourceChrome listen for those messages,
+   * and remove the listeners on teardown.
+   */
+  messages: [
+    "ViewSource:SourceLoaded",
+    "ViewSource:SourceUnloaded",
+    "ViewSource:Close",
+    "ViewSource:OpenURL",
+    "ViewSource:GoToLine:Success",
+    "ViewSource:GoToLine:Failed",
+    "ViewSource:UpdateStatus",
+    "ViewSource:ContextMenuOpening",
+  ],
+
+  /**
+   * This should be called as soon as the script loads. When this function
+   * executes, we can assume the DOM content has not yet loaded.
+   */
+  init() {
+    // We use the window message manager so that if we switch remoteness of the
+    // browser (which we might do if we're attempting to load the document
+    // source out of the network cache), we automatically re-load the frame
+    // script.
+    let wMM = window.messageManager;
+    wMM.loadFrameScript("chrome://global/content/viewSource-content.js", true);
+    this.messages.forEach((msgName) => {
+      wMM.addMessageListener(msgName, this);
+    });
+
+    this.shouldWrap = Services.prefs.getBoolPref("view_source.wrap_long_lines");
+    this.shouldHighlight =
+      Services.prefs.getBoolPref("view_source.syntax_highlight");
 
-var gSelectionListener = {
-  timeout: 0,
-  attached: false,
-  notifySelectionChanged: function(doc, sel, reason)
-  {
-    // Coalesce notifications within 100ms intervals.
-    if (!this.timeout)
-      this.timeout = setTimeout(updateStatusBar, 100);
-  }
-}
+    addEventListener("load", this);
+    addEventListener("unload", this);
+    addEventListener("AppCommand", this, true);
+    addEventListener("MozSwipeGesture", this, true);
+  },
+
+  /**
+   * This should be called when the window is closing. This function should
+   * clean up event and message listeners.
+   */
+  uninit() {
+    let wMM = window.messageManager;
+    this.messages.forEach((msgName) => {
+      wMM.removeMessageListener(msgName, this);
+    });
+
+    // "load" event listener is removed in its handler, to
+    // ensure we only fire it once.
+    removeEventListener("unload", this);
+    removeEventListener("AppCommand", this, true);
+    removeEventListener("MozSwipeGesture", this, true);
+    gContextMenu.removeEventListener("popupshowing", this);
+    gContextMenu.removeEventListener("popuphidden", this);
+    Services.els.removeSystemEventListener(gBrowser, "dragover", this, true);
+    Services.els.removeSystemEventListener(gBrowser, "drop", this, true);
+  },
 
-function onLoadViewSource() 
-{
-  viewSource(window.arguments[0]);
-  document.commandDispatcher.focusedWindow = content;
-  gBrowser.droppedLinkHandler = function (event, url, name) {
-    viewSource(url)
-    event.preventDefault();
-  }
+  /**
+   * Anything added to the messages array will get handled here, and should
+   * get dispatched to a specific function for the message name.
+   */
+  receiveMessage(message) {
+    let data = message.data;
 
-  if (!isHistoryEnabled()) {
-    // Disable the BACK and FORWARD commands and hide the related menu items.
-    var viewSourceNavigation = document.getElementById("viewSourceNavigation");
-    viewSourceNavigation.setAttribute("disabled", "true");
-    viewSourceNavigation.setAttribute("hidden", "true");
-  }
-}
-
-function isHistoryEnabled() {
-  return !gBrowser.hasAttribute("disablehistory");
-}
+    switch(message.name) {
+      case "ViewSource:SourceLoaded":
+        this.onSourceLoaded();
+        break;
+      case "ViewSource:SourceUnloaded":
+        this.onSourceUnloaded();
+        break;
+      case "ViewSource:Close":
+        this.close();
+        break;
+      case "ViewSource:OpenURL":
+        this.openURL(data.URL);
+        break;
+      case "ViewSource:GoToLine:Failed":
+        this.onGoToLineFailed();
+        break;
+      case "ViewSource:GoToLine:Success":
+        this.onGoToLineSuccess(data.lineNumber);
+        break;
+      case "ViewSource:UpdateStatus":
+        this.updateStatus(data.label);
+        break;
+      case "ViewSource:ContextMenuOpening":
+        this.onContextMenuOpening(data.isLink, data.isEmail, data.href);
+        if (gBrowser.isRemoteBrowser) {
+          this.openContextMenu(data.screenX, data.screenY);
+        }
+        break;
+    }
+  },
 
-function getSelectionController() {
-  return gBrowser.docShell
-                 .QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsISelectionDisplay)
-                 .QueryInterface(Ci.nsISelectionController);
-}
+  /**
+   * Any events should get handled here, and should get dispatched to
+   * a specific function for the event type.
+   */
+  handleEvent(event) {
+    switch(event.type) {
+      case "unload":
+        this.uninit();
+        break;
+      case "load":
+        this.onXULLoaded();
+        break;
+      case "AppCommand":
+        this.onAppCommand(event);
+        break;
+      case "MozSwipeGesture":
+        this.onSwipeGesture(event);
+        break;
+      case "popupshowing":
+        this.onContextMenuShowing(event);
+        break;
+      case "popuphidden":
+        this.onContextMenuHidden(event);
+        break;
+      case "dragover":
+        this.onDragOver(event);
+        break;
+      case "drop":
+        this.onDrop(event);
+        break;
+    }
+  },
 
-function viewSource(url)
-{
-  if (!url)
-    return; // throw Components.results.NS_ERROR_FAILURE;
-    
-  var viewSrcUrl = "view-source:" + url;
+  /**
+   * Getter that returns whether or not the view source browser
+   * has history enabled on it.
+   */
+  get historyEnabled() {
+    return !gBrowser.hasAttribute("disablehistory");
+  },
 
-  gBrowser.addEventListener("pagehide", onUnloadContent, true);
-  gBrowser.addEventListener("pageshow", onLoadContent, true);
-  gBrowser.addEventListener("click", onClickContent, false);
+  /**
+   * Getter for the message manager of the view source browser.
+   */
+  get mm() {
+    return gBrowser.messageManager;
+  },
 
-  var loadFromURL = true;
+  /**
+   * Getter for the nsIWebNavigation of the view source browser.
+   */
+  get webNav() {
+    return gBrowser.webNavigation;
+  },
+
+  /**
+   * Send the browser forward in its history.
+   */
+  goForward() {
+    gBrowser.goForward();
+  },
 
-  // Parse the 'arguments' supplied with the dialog.
-  //    arg[0] - URL string.
-  //    arg[1] - Charset value in the form 'charset=xxx'.
-  //    arg[2] - Page descriptor used to load content from the cache.
-  //    arg[3] - Line number to go to.
-  //    arg[4] - Whether charset was forced by the user
-
-  if ("arguments" in window) {
-    var arg;
+  /**
+   * Send the browser backward in its history.
+   */
+  goBack() {
+    gBrowser.goBack();
+  },
 
-    // Set the charset of the viewsource window...
-    var charset;
-    if (window.arguments.length >= 2) {
-      arg = window.arguments[1];
+  /**
+   * This should be called once when the DOM has finished loading. Here we
+   * set the state of various menu items, and add event listeners to
+   * DOM nodes.
+   *
+   * This is also the place where we handle any arguments that have been
+   * passed to viewSource.xul.
+   *
+   * Modern consumers should pass a single object argument to viewSource.xul:
+   *
+   *   URL (required):
+   *     A string URL for the page we'd like to view the source of.
+   *   browser:
+   *     The browser containing the document that we would like to view the
+   *     source of. This argument is optional if outerWindowID is not passed.
+   *   outerWindowID (optional):
+   *     The outerWindowID of the content window containing the document that
+   *     we want to view the source of. This is the only way of attempting to
+   *     load the source out of the network cache.
+   *   lineNumber (optional):
+   *     The line number to focus on once the source is loaded.
+   *
+   * The deprecated API has the opener pass in a number of arguments:
+   *
+   * arg[0] - URL string.
+   * arg[1] - Charset value string in the form 'charset=xxx'.
+   * arg[2] - Page descriptor from nsIWebPageDescriptor used to load content
+   *          from the cache.
+   * arg[3] - Line number to go to.
+   * arg[4] - Boolean for whether charset was forced by the user
+   */
+  onXULLoaded() {
+    // This handler should only ever run the first time the XUL is loaded.
+    removeEventListener("load", this);
 
-      try {
-        if (typeof(arg) == "string" && arg.indexOf('charset=') != -1) {
-          var arrayArgComponents = arg.split('=');
-          if (arrayArgComponents) {
-            // Remember the charset here so that it can be used below in case
-            // the document had a forced charset.
-            charset = arrayArgComponents[1];
-          }
-        }
-      } catch (ex) {
-        // Ignore the failure and keep processing arguments...
+    let wrapMenuItem = document.getElementById("menu_wrapLongLines");
+    if (this.shouldWrap) {
+      wrapMenuItem.setAttribute("checked", "true");
+    }
+
+    let highlightMenuItem = document.getElementById("menu_highlightSyntax");
+    if (this.shouldHighlight) {
+      highlightMenuItem.setAttribute("checked", "true");
+    }
+
+    gContextMenu.addEventListener("popupshowing", this);
+    gContextMenu.addEventListener("popuphidden", this);
+
+    Services.els.addSystemEventListener(gBrowser, "dragover", this, true);
+    Services.els.addSystemEventListener(gBrowser, "drop", this, true);
+
+    if (!this.historyEnabled) {
+      // Disable the BACK and FORWARD commands and hide the related menu items.
+      let viewSourceNavigation = document.getElementById("viewSourceNavigation");
+      if (viewSourceNavigation) {
+        viewSourceNavigation.setAttribute("disabled", "true");
+        viewSourceNavigation.setAttribute("hidden", "true");
       }
     }
-    // If the document had a forced charset, set it here also
-    if (window.arguments.length >= 5) {
-      arg = window.arguments[4];
+
+    // This will only work with non-remote browsers. See bug 1158377.
+    gBrowser.droppedLinkHandler = function (event, url, name) {
+      ViewSourceChrome.loadURL(url);
+      event.preventDefault();
+    };
+
+    // We require the first argument to do any loading of source.
+    // otherwise, we're done.
+    if (!window.arguments[0]) {
+      return;
+    }
 
-      try {
-        if (arg === true) {
-          gBrowser.docShell.charset = charset;
-        }
-      } catch (ex) {
-        // Ignore the failure and keep processing arguments...
+    if (typeof window.arguments[0] == "string") {
+      // We're using the deprecated API
+      return ViewSourceChrome._loadViewSourceDeprecated();
+    }
+
+    // We're using the modern API, which allows us to view the
+    // source of documents from out of process browsers.
+    let args = window.arguments[0];
+
+    if (!args.URL) {
+      throw new Error("Must supply a URL when opening view source.");
+    }
+
+    if (args.browser) {
+      // If we're dealing with a remote browser, then the browser
+      // for view source needs to be remote as well.
+      this.updateBrowserRemoteness(args.browser.isRemoteBrowser);
+    } else {
+      if (args.outerWindowID) {
+        throw new Error("Must supply the browser if passing the outerWindowID");
       }
     }
 
-    // Get any specified line to jump to.
-    if (window.arguments.length >= 4) {
-      arg = window.arguments[3];
-      gGoToLine = parseInt(arg);
+    this.mm.sendAsyncMessage("ViewSource:LoadSource", {
+      URL: args.URL,
+      outerWindowID: args.outerWindowID,
+      lineNumber: args.lineNumber,
+    });
+  },
+
+  /**
+   * This is the deprecated API for viewSource.xul, for old-timer consumers.
+   * This API might eventually go away.
+   */
+  _loadViewSourceDeprecated() {
+    Deprecated.warning("The arguments you're passing to viewSource.xul " +
+                       "are using an out-of-date API.",
+                       "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+    // Parse the 'arguments' supplied with the dialog.
+    //    arg[0] - URL string.
+    //    arg[1] - Charset value in the form 'charset=xxx'.
+    //    arg[2] - Page descriptor used to load content from the cache.
+    //    arg[3] - Line number to go to.
+    //    arg[4] - Whether charset was forced by the user
+
+    if (window.arguments[3] == "selection" ||
+        window.arguments[3] == "mathml") {
+      // viewPartialSource.js will take care of loading the content.
+      return;
+    }
+
+    if (window.arguments[2]) {
+      let pageDescriptor = window.arguments[2];
+      if (Cu.isCrossProcessWrapper(pageDescriptor)) {
+        throw new Error("Cannot pass a CPOW as the page descriptor to viewSource.xul.");
+      }
+    }
+
+    if (gBrowser.isRemoteBrowser) {
+      throw new Error("Deprecated view source API should not use a remote browser.");
+    }
+
+    let forcedCharSet;
+    if (window.arguments[4] && window.arguments[1].startsWith("charset=")) {
+      forcedCharSet = window.arguments[1].split("=")[1];
     }
 
-    // Use the page descriptor to load the content from the cache (if
-    // available).
-    if (window.arguments.length >= 3) {
-      arg = window.arguments[2];
+    gBrowser.messageManager.sendAsyncMessage("ViewSource:LoadSourceDeprecated", {
+      URL: window.arguments[0],
+      lineNumber: window.arguments[3],
+      forcedCharSet,
+    }, {
+      pageDescriptor: window.arguments[2],
+    });
+  },
 
-      try {
-        if (typeof(arg) == "object" && arg != null) {
-          // Load the page using the page descriptor rather than the URL.
-          // This allows the content to be fetched from the cache (if
-          // possible) rather than the network...
-          gPageLoader.loadPage(arg, gPageLoader.DISPLAY_AS_SOURCE);
-
-          // The content was successfully loaded.
-          loadFromURL = false;
+  /**
+   * Handler for the AppCommand event.
+   *
+   * @param event
+   *        The AppCommand event being handled.
+   */
+  onAppCommand(event) {
+    event.stopPropagation();
+    switch (event.command) {
+      case "Back":
+        this.goBack();
+        break;
+      case "Forward":
+        this.goForward();
+        break;
+    }
+  },
 
-          // Record the page load in the session history so <back> will work.
-          var shEntrySource = arg.QueryInterface(Ci.nsISHEntry);
-          var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].createInstance(Ci.nsISHEntry);
-          shEntry.setURI(makeURI(viewSrcUrl, null, null));
-          shEntry.setTitle(viewSrcUrl);
-          shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
-          shEntry.cacheKey = shEntrySource.cacheKey;
-          gBrowser.sessionHistory
-                  .QueryInterface(Ci.nsISHistoryInternal)
-                  .addEntry(shEntry, true);
-        }
-      } catch(ex) {
-        // Ignore the failure.  The content will be loaded via the URL
-        // that was supplied in arg[0].
+  /**
+   * Handler for the MozSwipeGesture event.
+   *
+   * @param event
+   *        The MozSwipeGesture event being handled.
+   */
+  onSwipeGesture(event) {
+    event.stopPropagation();
+    switch (event.direction) {
+      case SimpleGestureEvent.DIRECTION_LEFT:
+        this.goBack();
+        break;
+      case SimpleGestureEvent.DIRECTION_RIGHT:
+        this.goForward();
+        break;
+      case SimpleGestureEvent.DIRECTION_UP:
+        goDoCommand("cmd_scrollTop");
+        break;
+      case SimpleGestureEvent.DIRECTION_DOWN:
+        goDoCommand("cmd_scrollBottom");
+        break;
+    }
+  },
+
+  /**
+   * Called as soon as the frame script reports that some source
+   * code has been loaded in the browser.
+   */
+  onSourceLoaded() {
+    document.getElementById("cmd_goToLine").removeAttribute("disabled");
+
+    if (this.historyEnabled) {
+      this.updateCommands();
+    }
+
+    gBrowser.focus();
+  },
+
+  /**
+   * Called as soon as the frame script reports that some source
+   * code has been unloaded from the browser.
+   */
+  onSourceUnloaded() {
+    // Disable "go to line" while reloading due to e.g. change of charset
+    // or toggling of syntax highlighting.
+    document.getElementById("cmd_goToLine").setAttribute("disabled", "true");
+  },
+
+  /**
+   * Called by clicks on a menu populated by CharsetMenu.jsm to
+   * change the selected character set.
+   *
+   * @param event
+   *        The click event on a character set menuitem.
+   */
+  onSetCharacterSet(event) {
+    if (event.target.hasAttribute("charset")) {
+      let charset = event.target.getAttribute("charset");
+
+      // If we don't have history enabled, we have to do a reload in order to
+      // show the character set change. See bug 136322.
+      this.mm.sendAsyncMessage("ViewSource:SetCharacterSet", {
+        charset: charset,
+        doPageLoad: this.historyEnabled,
+      });
+
+      if (this.historyEnabled) {
+        gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
       }
     }
-  }
-
-  if (loadFromURL) {
-    // Currently, an exception is thrown if the URL load fails...
-    var loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
-    getWebNavigation().loadURI(viewSrcUrl, loadFlags, null, null, null);
-  }
-
-  // Check the view_source.wrap_long_lines pref and set the menuitem's checked
-  // attribute accordingly.
-  var wraplonglinesPrefValue = Services.prefs.getBoolPref("view_source.wrap_long_lines");
-
-  if (wraplonglinesPrefValue)
-    document.getElementById("menu_wrapLongLines").setAttribute("checked", "true");
+  },
 
-  document.getElementById("menu_highlightSyntax")
-          .setAttribute("checked",
-                        Services.prefs.getBoolPref("view_source.syntax_highlight"));
-
-  window.addEventListener("AppCommand", HandleAppCommandEvent, true);
-  window.addEventListener("MozSwipeGesture", HandleSwipeGesture, true);
-  window.content.focus();
-}
-
-function onLoadContent()
-{
-  // If the view source was opened with a "go to line" argument.
-  if (gGoToLine > 0) {
-    goToLine(gGoToLine);
-    gGoToLine = 0;
-  }
-  document.getElementById('cmd_goToLine').removeAttribute('disabled');
+  /**
+   * Called from the frame script when the context menu is about to
+   * open. This tells ViewSourceChrome things about the item that
+   * the context menu is being opened on. This should be called before
+   * the popupshowing event handler fires.
+   */
+  onContextMenuOpening(isLink, isEmail, href) {
+    this.contextMenuData = { isLink, isEmail, href, isOpen: true };
+  },
 
-  // Register a listener so that we can show the caret position on the status bar.
-  window.content.getSelection()
-   .QueryInterface(Ci.nsISelectionPrivate)
-   .addSelectionListener(gSelectionListener);
-  gSelectionListener.attached = true;
-
-  if (isHistoryEnabled())
-    UpdateBackForwardCommands();
-}
-
-function onUnloadContent()
-{
-  // Disable "go to line" while reloading due to e.g. change of charset
-  // or toggling of syntax highlighting.
-  document.getElementById('cmd_goToLine').setAttribute('disabled', 'true');
+  /**
+   * Event handler for the popupshowing event on the context menu.
+   * This handler is responsible for setting the state on various
+   * menu items in the context menu, and reads values that were sent
+   * up from the frame script and stashed into this.contextMenuData.
+   *
+   * @param event
+   *        The popupshowing event for the context menu.
+   */
+  onContextMenuShowing(event) {
+    let copyLinkMenuItem = document.getElementById("context-copyLink");
+    copyLinkMenuItem.hidden = !this.contextMenuData.isLink;
 
-  // If we're not just unloading the initial "about:blank" which doesn't have
-  // a selection listener, get rid of it so it doesn't try to fire after the
-  // window has gone away.
-  if (gSelectionListener.attached) {
-    window.content.getSelection().QueryInterface(Ci.nsISelectionPrivate)
-          .removeSelectionListener(gSelectionListener);
-    gSelectionListener.attached = false;
-  }
-}
-
-/**
- * Handle click events bubbling up from error page content
- */
-function onClickContent(event) {
-  // Don't trust synthetic events
-  if (!event.isTrusted || event.target.localName != "button")
-    return;
-
-  var target = event.originalTarget;
-  var errorDoc = target.ownerDocument;
-
-  if (/^about:blocked/.test(errorDoc.documentURI)) {
-    // The event came from a button on a malware/phishing block page
+    let copyEmailMenuItem = document.getElementById("context-copyEmail");
+    copyEmailMenuItem.hidden = !this.contextMenuData.isEmail;
+  },
 
-    if (target == errorDoc.getElementById('getMeOutButton')) {
-      // Instead of loading some safe page, just close the window
-      window.close();
-    } else if (target == errorDoc.getElementById('reportButton')) {
-      // This is the "Why is this site blocked" button. We redirect
-      // to the generic page describing phishing/malware protection.
-      let url = Services.urlFormatter.formatURLPref("app.support.baseURL");
-      openURL(url + "phishing-malware");
-    } else if (target == errorDoc.getElementById('ignoreWarningButton')) {
-      // Allow users to override and continue through to the site
-      gBrowser.loadURIWithFlags(content.location.href,
-                                Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
-                                null, null, null);
+  /**
+   * Called when the user chooses the "Copy Link" or "Copy Email"
+   * menu items in the context menu. Copies the relevant selection
+   * into the system clipboard.
+   */
+  onContextMenuCopyLinkOrEmail() {
+    // It doesn't make any sense to call this if the context menu
+    // isn't open...
+    if (!this.contextMenuData.isOpen) {
+      return;
     }
-  }
-}
 
-function HandleAppCommandEvent(evt)
-{
-  evt.stopPropagation();
-  switch (evt.command) {
-    case "Back":
-      BrowserBack();
-      break;
-    case "Forward":
-      BrowserForward();
-      break;
-  }
-}
+    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+                      .getService(Ci.nsIClipboardHelper);
+    clipboard.copyString(this.contextMenuData.href, document);
+  },
 
-function HandleSwipeGesture(evt) {
-  evt.stopPropagation();
-  switch (evt.direction) {
-    case SimpleGestureEvent.DIRECTION_LEFT:
-      BrowserBack();
-      break;
-    case SimpleGestureEvent.DIRECTION_RIGHT:
-      BrowserForward();
-      break;
-    case SimpleGestureEvent.DIRECTION_UP:
-      goDoCommand("cmd_scrollTop");
-      break;
-    case SimpleGestureEvent.DIRECTION_DOWN:
-      goDoCommand("cmd_scrollBottom");
-      break;
-  }
-}
-
-function ViewSourceClose()
-{
-  window.close();
-}
+  /**
+   * Called when the context menu closes, and invalidates any data
+   * that the frame script might have sent up about what the context
+   * menu was opened on.
+   */
+  onContextMenuHidden(event) {
+    this.contextMenuData = {
+      isOpen: false,
+    };
+  },
 
-function ViewSourceReload()
-{
-  gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY |
-                           Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
-}
-
-// Strips the |view-source:| for internalSave()
-function ViewSourceSavePage()
-{
-  internalSave(window.content.location.href.replace(/^view-source:/i, ""),
-               null, null, null, null, null, "SaveLinkTitle",
-               null, null, window.content.document, null, gPageLoader);
-}
-
-var PrintPreviewListener = {
-  getPrintPreviewBrowser: function () {
-    var browser = document.getElementById("ppBrowser");
-    if (!browser) {
-      browser = document.createElement("browser");
-      browser.setAttribute("id", "ppBrowser");
-      browser.setAttribute("flex", "1");
-      browser.setAttribute("type", "content");
-      document.getElementById("appcontent").
-        insertBefore(browser, document.getElementById("FindToolbar"));
+  /**
+   * Called when the user drags something over the content browser.
+   */
+  onDragOver(event) {
+    // For drags that appear to be internal text (for example, tab drags),
+    // set the dropEffect to 'none'. This prevents the drop even if some
+    // other listener cancelled the event.
+    let types = event.dataTransfer.types;
+    if (types.contains("text/x-moz-text-internal") && !types.contains("text/plain")) {
+        event.dataTransfer.dropEffect = "none";
+        event.stopPropagation();
+        event.preventDefault();
     }
 
-    return browser;
-  },
-  getSourceBrowser: function () {
-    return gBrowser;
+    let linkHandler = Cc["@mozilla.org/content/dropped-link-handler;1"]
+                        .getService(Ci.nsIDroppedLinkHandler);
+
+    if (linkHandler.canDropLink(event, false)) {
+      event.preventDefault();
+    }
   },
-  getNavToolbox: function () {
-    return document.getElementById("appcontent");
+
+  /**
+   * Called twhen the user drops something onto the content browser.
+   */
+  onDrop(event) {
+    if (event.defaultPrevented)
+      return;
+
+    let name = { };
+    let linkHandler = Cc["@mozilla.org/content/dropped-link-handler;1"]
+                        .getService(Ci.nsIDroppedLinkHandler);
+    let uri;
+    try {
+      // Pass true to prevent the dropping of javascript:/data: URIs
+      uri = linkHandler.dropLink(event, name, true);
+    } catch (e) {
+      return;
+    }
+
+    if (uri) {
+      this.loadURL(uri);
+    }
   },
-  onEnter: function () {
-    var toolbox = document.getElementById("viewSource-toolbox");
-    toolbox.hidden = true;
-    gBrowser.collapsed = true;
+
+  /**
+   * For remote browsers, the contextmenu event is received in the
+   * content process, and a message is sent up from the frame script
+   * to ViewSourceChrome, but then it stops. The event does not bubble
+   * up to the point that the popup is opened in the parent process.
+   * ViewSourceChrome is responsible for opening up the context menu in
+   * that case. This is called when we receive the contextmenu message
+   * from the child, and we know that the browser is currently remote.
+   *
+   * @param screenX
+   *        The screenX coordinate to open the popup at.
+   * @param screenY
+   *        The screenY coordinate to open the popup at.
+   */
+  openContextMenu(screenX, screenY) {
+    gContextMenu.openPopupAtScreen(screenX, screenY, true);
   },
-  onExit: function () {
-    document.getElementById("ppBrowser").collapsed = true;
-    gBrowser.collapsed = false;
-    document.getElementById("viewSource-toolbox").hidden = false;
-  }
-}
+
+  /**
+   * Loads the source of a URL. This will probably end up hitting the
+   * network.
+   *
+   * @param URL
+   *        A URL string to be opened in the view source browser.
+   */
+  loadURL(URL) {
+    this.mm.sendAsyncMessage("ViewSource:LoadSource", { URL });
+  },
+
+  /**
+   * Updates any commands that are dependant on command broadcasters.
+   */
+  updateCommands() {
+    let backBroadcaster = document.getElementById("Browser:Back");
+    let forwardBroadcaster = document.getElementById("Browser:Forward");
 
-function getWebNavigation()
-{
-  try {
-    return gBrowser.webNavigation;
-  } catch (e) {
-    return null;
-  }
-}
+    if (this.webNav.canGoBack) {
+      backBroadcaster.removeAttribute("disabled");
+    } else {
+      backBroadcaster.setAttribute("disabled", "true");
+    }
+    if (this.webNav.canGoForward) {
+      forwardBroadcaster.removeAttribute("disabled");
+    } else {
+      forwardBroadcaster.setAttribute("disabled", "true");
+    }
+  },
 
-function ViewSourceGoToLine()
-{
-  var input = {value:gLastLineFound};
-  for (;;) {
-    var ok = Services.prompt.prompt(
+  /**
+   * Updates the status displayed in the status bar of the view source window.
+   *
+   * @param label
+   *        The string to be displayed in the statusbar-lin-col element.
+   */
+  updateStatus(label) {
+    let statusBarField = document.getElementById("statusbar-line-col");
+    if (statusBarField) {
+      statusBarField.label = label;
+    }
+  },
+
+  /**
+   * Opens the "Go to line" prompt for a user to hop to a particular line
+   * of the source code they're viewing. This will keep prompting until the
+   * user either cancels out of the prompt, or enters a valid line number.
+   */
+  promptAndGoToLine() {
+    let input = { value: this.lastLineFound };
+
+    let ok = Services.prompt.prompt(
         window,
         gViewSourceBundle.getString("goToLineTitle"),
         gViewSourceBundle.getString("goToLineText"),
         input,
         null,
         {value:0});
 
     if (!ok)
       return;
 
-    var line = parseInt(input.value, 10);
+    let line = parseInt(input.value, 10);
 
     if (!(line > 0)) {
       Services.prompt.alert(window,
                             gViewSourceBundle.getString("invalidInputTitle"),
                             gViewSourceBundle.getString("invalidInputText"));
+      this.promptAndGoToLine();
+    } else {
+      this.goToLine(line);
+    }
+  },
 
-      continue;
-    }
+  /**
+   * Go to a particular line of the source code. This act is asynchronous.
+   *
+   * @param lineNumber
+   *        The line number to try to go to to.
+   */
+  goToLine(lineNumber) {
+    this.mm.sendAsyncMessage("ViewSource:GoToLine", { lineNumber });
+  },
 
-    var found = goToLine(line);
+  /**
+   * Called when the frame script reports that a line was successfully gotten
+   * to.
+   *
+   * @param lineNumber
+   *        The line number that we successfully got to.
+   */
+  onGoToLineSuccess(lineNumber) {
+    // We'll pre-populate the "Go to line" prompt with this value the next
+    // time it comes up.
+    this.lastLineFound = lineNumber;
+    document.getElementById("statusbar-line-col").label =
+      gViewSourceBundle.getFormattedString("statusBarLineCol", [lineNumber, 1]);
+  },
 
-    if (found)
-      break;
-
+  /**
+   * Called when the frame script reports that we failed to go to a particular
+   * line. This informs the user that their selection was likely out of range,
+   * and then reprompts the user to try again.
+   */
+  onGoToLineFailed() {
     Services.prompt.alert(window,
                           gViewSourceBundle.getString("outOfRangeTitle"),
                           gViewSourceBundle.getString("outOfRangeText"));
+    this.promptAndGoToLine();
+  },
+
+  /**
+   * Reloads the browser, bypassing the network cache.
+   */
+  reload() {
+    gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY |
+                             Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+  },
+
+  /**
+   * Closes the view source window.
+   */
+  close() {
+    window.close();
+  },
+
+  /**
+   * Called when the user clicks on the "Wrap Long Lines" menu item, and
+   * flips the user preference for wrapping long lines in the view source
+   * browser.
+   */
+  toggleWrapping() {
+    this.shouldWrap = !this.shouldWrap;
+    Services.prefs.setBoolPref("view_source.wrap_long_lines",
+                               this.shouldWrap);
+    this.mm.sendAsyncMessage("ViewSource:ToggleWrapping");
+  },
+
+  /**
+   * Called when the user clicks on the "Syntax Highlighting" menu item, and
+   * flips the user preference for wrapping long lines in the view source
+   * browser.
+   */
+  toggleSyntaxHighlighting() {
+    this.shouldHighlight = !this.shouldHighlight;
+    // We can't flip this value in the child, since prefs are read-only there.
+    // We flip it here, and then cause a reload in the child to make the change
+    // occur.
+    Services.prefs.setBoolPref("view_source.syntax_highlight",
+                               this.shouldHighlight);
+    this.mm.sendAsyncMessage("ViewSource:ToggleSyntaxHighlighting");
+  },
+
+  /**
+   * Updates the "remote" attribute of the view source browser. This
+   * will remove the browser from the DOM, and then re-add it in the
+   * same place it was taken from.
+   *
+   * @param shouldBeRemote
+   *        True if the browser should be made remote. If the browsers
+   *        remoteness already matches this value, this function does
+   *        nothing.
+   */
+  updateBrowserRemoteness(shouldBeRemote) {
+    if (gBrowser.isRemoteBrowser == shouldBeRemote) {
+      return;
+    }
+
+    let parentNode = gBrowser.parentNode;
+    let nextSibling = gBrowser.nextSibling;
+
+    gBrowser.remove();
+    if (shouldBeRemote) {
+      gBrowser.setAttribute("remote", "true");
+    } else {
+      gBrowser.removeAttribute("remote");
+    }
+    // If nextSibling was null, this will put the browser at
+    // the end of the list.
+    parentNode.insertBefore(gBrowser, nextSibling);
+
+    if (shouldBeRemote) {
+      // We're going to send a message down to the remote browser
+      // to load the source content - however, in order for the
+      // contentWindowAsCPOW and contentDocumentAsCPOW values on
+      // the remote browser to be set, we must set up the
+      // RemoteWebProgress, which is lazily loaded. We only need
+      // contentWindowAsCPOW for the printing support, and this
+      // should go away once bug 1146454 is fixed, since we can
+      // then just pass the outerWindowID of the gBrowser to
+      // PrintUtils.
+      gBrowser.webProgress;
+    }
+  },
+};
+
+ViewSourceChrome.init();
+
+/**
+ * PrintUtils uses this to make Print Preview work.
+ */
+let PrintPreviewListener = {
+  getPrintPreviewBrowser() {
+    let browser = document.getElementById("ppBrowser");
+    if (!browser) {
+      browser = document.createElement("browser");
+      browser.setAttribute("id", "ppBrowser");
+      browser.setAttribute("flex", "1");
+      browser.setAttribute("type", "content");
+
+      let findBar = document.getElementById("FindToolbar");
+      document.getElementById("appcontent")
+              .insertBefore(browser, findBar);
+    }
+
+    return browser;
+  },
+
+  getSourceBrowser() {
+    return gBrowser;
+  },
+
+  getNavToolbox() {
+    return document.getElementById("appcontent");
+  },
+
+  onEnter() {
+    let toolbox = document.getElementById("viewSource-toolbox");
+    toolbox.hidden = true;
+    gBrowser.collapsed = true;
+  },
+
+  onExit() {
+    document.getElementById("ppBrowser").collapsed = true;
+    gBrowser.collapsed = false;
+    document.getElementById("viewSource-toolbox").hidden = false;
+  },
+};
+
+// viewZoomOverlay.js uses this
+function getBrowser() {
+  return gBrowser;
+}
+
+this.__defineGetter__("gPageLoader", function () {
+  var webnav = ViewSourceChrome.webNav;
+  if (!webnav)
+    return null;
+  delete this.gPageLoader;
+  this.gPageLoader = (webnav instanceof Ci.nsIWebPageDescriptor) ? webnav
+                                                                 : null;
+  return this.gPageLoader;
+});
+
+// Strips the |view-source:| for internalSave()
+function ViewSourceSavePage()
+{
+  internalSave(gBrowser.currentURI.spec.replace(/^view-source:/i, ""),
+               null, null, null, null, null, "SaveLinkTitle",
+               null, null, gBrowser.contentDocumentAsCPOW, null,
+               gPageLoader);
+}
+
+// Below are old deprecated functions and variables left behind for
+// compatibility reasons. These will be removed soon via bug 1159293.
+
+this.__defineGetter__("gLastLineFound", function () {
+  Deprecated.warning("gLastLineFound is deprecated - please use " +
+                     "ViewSourceChrome.lastLineFound instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  return ViewSourceChrome.lastLineFound;
+});
+
+function onLoadViewSource() {
+  Deprecated.warning("onLoadViewSource() is deprecated - please use " +
+                     "ViewSourceChrome.onXULLoaded() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.onXULLoaded();
+}
+
+function isHistoryEnabled() {
+  Deprecated.warning("isHistoryEnabled() is deprecated - please use " +
+                     "ViewSourceChrome.historyEnabled instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  return ViewSourceChrome.historyEnabled;
+}
+
+function ViewSourceClose() {
+  Deprecated.warning("ViewSourceClose() is deprecated - please use " +
+                     "ViewSourceChrome.close() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.close();
+}
+
+function ViewSourceReload() {
+  Deprecated.warning("ViewSourceReload() is deprecated - please use " +
+                     "ViewSourceChrome.reload() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.reload();
+}
+
+function getWebNavigation()
+{
+  Deprecated.warning("getWebNavigation() is deprecated - please use " +
+                     "ViewSourceChrome.webNav instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  // The original implementation returned null if anything threw during
+  // the getting of the webNavigation.
+  try {
+    return ViewSourceChrome.webNav;
+  } catch (e) {
+    return null;
   }
 }
 
+function viewSource(url) {
+  Deprecated.warning("viewSource() is deprecated - please use " +
+                     "ViewSourceChrome.loadURL() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.loadURL(url);
+}
+
+function ViewSourceGoToLine()
+{
+  Deprecated.warning("ViewSourceGoToLine() is deprecated - please use " +
+                     "ViewSourceChrome.promptAndGoToLine() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.promptAndGoToLine();
+}
+
 function goToLine(line)
 {
-  var viewsource = window.content.document.body;
-
-  // The source document is made up of a number of pre elements with
-  // id attributes in the format <pre id="line123">, meaning that
-  // the first line in the pre element is number 123.
-  // Do binary search to find the pre element containing the line.
-  // However, in the plain text case, we have only one pre without an
-  // attribute, so assume it begins on line 1.
-
-  var pre;
-  for (var lbound = 0, ubound = viewsource.childNodes.length; ; ) {
-    var middle = (lbound + ubound) >> 1;
-    pre = viewsource.childNodes[middle];
-
-    var firstLine = pre.id ? parseInt(pre.id.substring(4)) : 1;
-
-    if (lbound == ubound - 1) {
-      break;
-    }
-
-    if (line >= firstLine) {
-      lbound = middle;
-    } else {
-      ubound = middle;
-    }
-  }
-
-  var result = {};
-  var found = findLocation(pre, line, null, -1, false, result);
-
-  if (!found) {
-    return false;
-  }
-
-  var selection = window.content.getSelection();
-  selection.removeAllRanges();
-
-  // In our case, the range's startOffset is after "\n" on the previous line.
-  // Tune the selection at the beginning of the next line and do some tweaking
-  // to position the focusNode and the caret at the beginning of the line.
-
-  selection.QueryInterface(Ci.nsISelectionPrivate)
-    .interlinePosition = true;
-
-  selection.addRange(result.range);
-
-  if (!selection.isCollapsed) {
-    selection.collapseToEnd();
-
-    var offset = result.range.startOffset;
-    var node = result.range.startContainer;
-    if (offset < node.data.length) {
-      // The same text node spans across the "\n", just focus where we were.
-      selection.extend(node, offset);
-    }
-    else {
-      // There is another tag just after the "\n", hook there. We need
-      // to focus a safe point because there are edgy cases such as
-      // <span>...\n</span><span>...</span> vs.
-      // <span>...\n<span>...</span></span><span>...</span>
-      node = node.nextSibling ? node.nextSibling : node.parentNode.nextSibling;
-      selection.extend(node, 0);
-    }
-  }
-
-  var selCon = getSelectionController();
-  selCon.setDisplaySelection(Ci.nsISelectionController.SELECTION_ON);
-  selCon.setCaretVisibilityDuringSelection(true);
-
-  // Scroll the beginning of the line into view.
-  selCon.scrollSelectionIntoView(
-    Ci.nsISelectionController.SELECTION_NORMAL,
-    Ci.nsISelectionController.SELECTION_FOCUS_REGION,
-    true);
-
-  gLastLineFound = line;
-
-  document.getElementById("statusbar-line-col").label =
-    gViewSourceBundle.getFormattedString("statusBarLineCol", [line, 1]);
-
-  return true;
-}
-
-function updateStatusBar()
-{
-  // Reset the coalesce flag.
-  gSelectionListener.timeout = 0;
-
-  var statusBarField = document.getElementById("statusbar-line-col");
-
-  var selection = window.content.getSelection();
-  if (!selection.focusNode) {
-    statusBarField.label = '';
-    return;
-  }
-  if (selection.focusNode.nodeType != Node.TEXT_NODE) {
-    return;
-  }
-
-  var selCon = getSelectionController();
-  selCon.setDisplaySelection(Ci.nsISelectionController.SELECTION_ON);
-  selCon.setCaretVisibilityDuringSelection(true);
-
-  var interlinePosition = selection.QueryInterface(Ci.nsISelectionPrivate)
-                                   .interlinePosition;
-
-  var result = {};
-  findLocation(null, -1, 
-      selection.focusNode, selection.focusOffset, interlinePosition, result);
-
-  statusBarField.label = gViewSourceBundle.getFormattedString(
-                           "statusBarLineCol", [result.line, result.col]);
-}
-
-// Loops through the text lines in the pre element. The arguments are either
-// (pre, line) or (node, offset, interlinePosition). result is an out
-// argument. If (pre, line) are specified (and node == null), result.range is
-// a range spanning the specified line. If the (node, offset,
-// interlinePosition) are specified, result.line and result.col are the line
-// and column number of the specified offset in the specified node relative to
-// the whole file.
-function findLocation(pre, line, node, offset, interlinePosition, result)
-{
-  if (node && !pre) {
-    // Look upwards to find the current pre element.
-    for (pre = node;
-         pre.nodeName != "PRE";
-         pre = pre.parentNode);
-  }
-
-  // The source document is made up of a number of pre elements with
-  // id attributes in the format <pre id="line123">, meaning that
-  // the first line in the pre element is number 123.
-  // However, in the plain text case, there is only one <pre> without an id,
-  // so assume line 1.
-  var curLine = pre.id ? parseInt(pre.id.substring(4)) : 1;
-
-  // Walk through each of the text nodes and count newlines.
-  var treewalker = window.content.document
-      .createTreeWalker(pre, NodeFilter.SHOW_TEXT, null);
-
-  // The column number of the first character in the current text node.
-  var firstCol = 1;
-
-  var found = false;
-  for (var textNode = treewalker.firstChild();
-       textNode && !found;
-       textNode = treewalker.nextNode()) {
-
-    // \r is not a valid character in the DOM, so we only check for \n.
-    var lineArray = textNode.data.split(/\n/);
-    var lastLineInNode = curLine + lineArray.length - 1;
-
-    // Check if we can skip the text node without further inspection.
-    if (node ? (textNode != node) : (lastLineInNode < line)) {
-      if (lineArray.length > 1) {
-        firstCol = 1;
-      }
-      firstCol += lineArray[lineArray.length - 1].length;
-      curLine = lastLineInNode;
-      continue;
-    }
-
-    // curPos is the offset within the current text node of the first
-    // character in the current line.
-    for (var i = 0, curPos = 0;
-         i < lineArray.length;
-         curPos += lineArray[i++].length + 1) {
-
-      if (i > 0) {
-        curLine++;
-      }
-
-      if (node) {
-        if (offset >= curPos && offset <= curPos + lineArray[i].length) {
-          // If we are right after the \n of a line and interlinePosition is
-          // false, the caret looks as if it were at the end of the previous
-          // line, so we display that line and column instead.
-
-          if (i > 0 && offset == curPos && !interlinePosition) {
-            result.line = curLine - 1;
-            var prevPos = curPos - lineArray[i - 1].length;
-            result.col = (i == 1 ? firstCol : 1) + offset - prevPos;
-          } else {
-            result.line = curLine;
-            result.col = (i == 0 ? firstCol : 1) + offset - curPos;
-          }
-          found = true;
-
-          break;
-        }
-
-      } else {
-        if (curLine == line && !("range" in result)) {
-          result.range = document.createRange();
-          result.range.setStart(textNode, curPos);
-
-          // This will always be overridden later, except when we look for
-          // the very last line in the file (this is the only line that does
-          // not end with \n).
-          result.range.setEndAfter(pre.lastChild);
-
-        } else if (curLine == line + 1) {
-          result.range.setEnd(textNode, curPos - 1);
-          found = true;
-          break;
-        }
-      }
-    }
-  }
-
-  return found || ("range" in result);
-}
-
-// Toggle long-line wrapping and sets the view_source.wrap_long_lines
-// pref to persist the last state.
-function wrapLongLines()
-{
-  var myWrap = window.content.document.body;
-  myWrap.classList.toggle("wrap");
-
-  // Since multiple viewsource windows are possible, another window could have
-  // affected the pref, so instead of determining the new pref value via the current
-  // pref value, we use myWrap.classList.
-  Services.prefs.setBoolPref("view_source.wrap_long_lines", myWrap.classList.contains("wrap"));
-}
-
-// Toggles syntax highlighting and sets the view_source.syntax_highlight
-// pref to persist the last state.
-function highlightSyntax()
-{
-  var highlightSyntaxMenu = document.getElementById("menu_highlightSyntax");
-  var highlightSyntax = (highlightSyntaxMenu.getAttribute("checked") == "true");
-  Services.prefs.setBoolPref("view_source.syntax_highlight", highlightSyntax);
-
-  gPageLoader.loadPage(gPageLoader.currentDescriptor, gPageLoader.DISPLAY_NORMAL);
-}
-
-// Reload after change to character encoding or autodetection
-//
-// Fix for bug 136322: this function overrides the function in
-// browser.js to call PageLoader.loadPage() instead of BrowserReloadWithFlags()
-function BrowserCharsetReload()
-{
-  if (isHistoryEnabled()) {
-    gPageLoader.loadPage(gPageLoader.currentDescriptor,
-                         gPageLoader.DISPLAY_NORMAL);
-  } else {
-    gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
-  }
-}
-
-function BrowserSetCharacterSet(aEvent)
-{
-  if (aEvent.target.hasAttribute("charset"))
-    gBrowser.docShell.charset = aEvent.target.getAttribute("charset");
-  BrowserCharsetReload();
+  Deprecated.warning("goToLine() is deprecated - please use " +
+                     "ViewSourceChrome.goToLine() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.goToLine(line);
 }
 
 function BrowserForward(aEvent) {
-  try {
-    gBrowser.goForward();
-  }
-  catch(ex) {
-  }
+  Deprecated.warning("BrowserForward() is deprecated - please use " +
+                     "ViewSourceChrome.goForward() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.goForward();
 }
 
 function BrowserBack(aEvent) {
-  try {
-    gBrowser.goBack();
-  }
-  catch(ex) {
-  }
+  Deprecated.warning("BrowserBack() is deprecated - please use " +
+                     "ViewSourceChrome.goBack() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.goBack();
 }
 
 function UpdateBackForwardCommands() {
-  var backBroadcaster = document.getElementById("Browser:Back");
-  var forwardBroadcaster = document.getElementById("Browser:Forward");
-
-  if (getWebNavigation().canGoBack)
-    backBroadcaster.removeAttribute("disabled");
-  else
-    backBroadcaster.setAttribute("disabled", "true");
-
-  if (getWebNavigation().canGoForward)
-    forwardBroadcaster.removeAttribute("disabled");
-  else
-    forwardBroadcaster.setAttribute("disabled", "true");
+  Deprecated.warning("UpdateBackForwardCommands() is deprecated - please use " +
+                     "ViewSourceChrome.updateCommands() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.updateCommands();
 }
-
-function contextMenuShowing() {
-  var isLink = false;
-  var isEmail = false;
-  if (gContextMenu.triggerNode && gContextMenu.triggerNode.localName == 'a') {
-    if (gContextMenu.triggerNode.href.indexOf('view-source:') == 0)
-      isLink = true;
-    if (gContextMenu.triggerNode.href.indexOf('mailto:') == 0)
-      isEmail = true;
-  }
-  document.getElementById('context-copyLink').hidden = !isLink;
-  document.getElementById('context-copyEmail').hidden = !isEmail;
-}
-
-function contextMenuCopyLinkOrEmail() {
-  if (!gContextMenu.triggerNode)
-    return;
-
-  var href = gContextMenu.triggerNode.href;
-  var clipboard = Cc['@mozilla.org/widget/clipboardhelper;1'].
-                  getService(Ci.nsIClipboardHelper);
-  clipboard.copyString(href.substring(href.indexOf(':') + 1), document);
-}
--- a/toolkit/components/viewsource/content/viewSource.xul
+++ b/toolkit/components/viewsource/content/viewSource.xul
@@ -15,17 +15,16 @@
 <!ENTITY % sourceDTD SYSTEM "chrome://global/locale/viewSource.dtd" >
 %sourceDTD;
 <!ENTITY % charsetDTD SYSTEM "chrome://global/locale/charsetMenu.dtd" >
 %charsetDTD;
 ]>
 
 <window id="viewSource"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        onload="onLoadViewSource();"
         contenttitlesetting="true"
         title="&mainWindow.title;" 
         titlemodifier="&mainWindow.titlemodifier;" 
         titlepreface="&mainWindow.preface;"
         titlemenuseparator ="&mainWindow.titlemodifierseparator;"  
         windowtype="navigator:view-source"
         width="640" height="480"
         screenX="10" screenY="10"
@@ -35,41 +34,41 @@
   <script type="application/javascript" src="chrome://global/content/printUtils.js"/>
   <script type="application/javascript" src="chrome://global/content/viewSource.js"/>
   <script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/>
   <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
   
   <stringbundle id="viewSourceBundle" src="chrome://global/locale/viewSource.properties"/>
 
   <command id="cmd_savePage" oncommand="ViewSourceSavePage();"/>
-  <command id="cmd_print" oncommand="PrintUtils.print();"/>
+  <command id="cmd_print" oncommand="PrintUtils.print(gBrowser.contentWindowAsCPOW, gBrowser);"/>
   <command id="cmd_printpreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
   <command id="cmd_pagesetup" oncommand="PrintUtils.showPageSetup();"/>
   <command id="cmd_close" oncommand="window.close();"/>
   <commandset id="editMenuCommands"/>
   <command id="cmd_find"
            oncommand="document.getElementById('FindToolbar').onFindCommand();"/>
   <command id="cmd_findAgain"
            oncommand="document.getElementById('FindToolbar').onFindAgainCommand(false);"/>
   <command id="cmd_findPrevious"
            oncommand="document.getElementById('FindToolbar').onFindAgainCommand(true);"/>
 #ifdef XP_MACOSX
   <command id="cmd_findSelection"
            oncommand="document.getElementById('FindToolbar').onFindSelectionCommand();"/>
 #endif
-  <command id="cmd_reload" oncommand="ViewSourceReload();"/>
-  <command id="cmd_goToLine" oncommand="ViewSourceGoToLine();" disabled="true"/>
-  <command id="cmd_highlightSyntax" oncommand="highlightSyntax();"/>
-  <command id="cmd_wrapLongLines" oncommand="wrapLongLines()"/>
+  <command id="cmd_reload" oncommand="ViewSourceChrome.reload();"/>
+  <command id="cmd_goToLine" oncommand="ViewSourceChrome.promptAndGoToLine();" disabled="true"/>
+  <command id="cmd_highlightSyntax" oncommand="ViewSourceChrome.toggleSyntaxHighlighting();"/>
+  <command id="cmd_wrapLongLines" oncommand="ViewSourceChrome.toggleWrapping();"/>
   <command id="cmd_textZoomReduce" oncommand="ZoomManager.reduce();"/>
   <command id="cmd_textZoomEnlarge" oncommand="ZoomManager.enlarge();"/>
   <command id="cmd_textZoomReset" oncommand="ZoomManager.reset();"/>
 
-  <command id="Browser:Back" oncommand="BrowserBack();" observes="viewSourceNavigation"/>
-  <command id="Browser:Forward" oncommand="BrowserForward();" observes="viewSourceNavigation"/>
+  <command id="Browser:Back" oncommand="ViewSourceChrome.goBack()" observes="viewSourceNavigation"/>
+  <command id="Browser:Forward" oncommand="ViewSourceChrome.goForward()" observes="viewSourceNavigation"/>
 
   <broadcaster id="viewSourceNavigation"/>
 
   <keyset id="editMenuKeys"/>
   <keyset id="viewSourceKeys">
     <key id="key_savePage" key="&savePageCmd.commandkey;" modifiers="accel" command="cmd_savePage"/>
     <key id="key_print" key="&printCmd.commandkey;" modifiers="accel" command="cmd_print"/>
     <key id="key_close" key="&closeCmd.commandkey;" modifiers="accel" command="cmd_close"/>
@@ -109,18 +108,17 @@
     <key id="goBackKb2" key="&goBackCmd.commandKey;" command="Browser:Back" modifiers="accel"/>
     <key id="goForwardKb2" key="&goForwardCmd.commandKey;" command="Browser:Forward" modifiers="accel"/>
 #endif
 
   </keyset>
   
   <tooltip id="aHTMLTooltip" page="true"/>
 
-  <menupopup id="viewSourceContextMenu"
-             onpopupshowing="contextMenuShowing();">
+  <menupopup id="viewSourceContextMenu">
     <menuitem id="context-back"
               label="&backCmd.label;"
               accesskey="&backCmd.accesskey;"
               command="Browser:Back"
               observes="viewSourceNavigation"/>
     <menuitem id="context-forward"
               label="&forwardCmd.label;"
               accesskey="&forwardCmd.accesskey;"
@@ -128,21 +126,21 @@
               observes="viewSourceNavigation"/>
     <menuseparator observes="viewSourceNavigation"/>
     <menuitem id="cMenu_findAgain"/>
     <menuseparator/>
     <menuitem id="cMenu_copy"/>
     <menuitem id="context-copyLink"
               label="&copyLinkCmd.label;"
               accesskey="&copyLinkCmd.accesskey;"
-              oncommand="contextMenuCopyLinkOrEmail();"/>
+              oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
     <menuitem id="context-copyEmail"
               label="&copyEmailCmd.label;"
               accesskey="&copyEmailCmd.accesskey;"
-              oncommand="contextMenuCopyLinkOrEmail();"/>
+              oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
     <menuseparator/>
     <menuitem id="cMenu_selectAll"/>
   </menupopup>
 
   <!-- Menu --> 
   <toolbox id="viewSource-toolbox">
     <menubar id="viewSource-main-menubar">
 
@@ -203,17 +201,17 @@
                         key="key_textZoomReset"/>
             </menupopup>
           </menu>
 
           <!-- Charset Menu -->
           <menu id="charsetMenu"
                 label="&charsetMenu2.label;"
                 accesskey="&charsetMenu2.accesskey;"
-                oncommand="BrowserSetCharacterSet(event);"
+                oncommand="ViewSourceChrome.onSetCharacterSet(event);"
                 onpopupshowing="CharsetMenu.build(event.target);"
                 onpopupshown="CharsetMenu.update(event.target, content.document.characterSet);">
             <menupopup/>
           </menu>
           <menuseparator/>
           <menuitem id="menu_wrapLongLines" type="checkbox" command="cmd_wrapLongLines"
                     label="&menu_wrapLongLines.title;" accesskey="&menu_wrapLongLines.accesskey;"/>
           <menuitem type="checkbox" id="menu_highlightSyntax" command="cmd_highlightSyntax"
@@ -221,17 +219,17 @@
         </menupopup>
       </menu>
     </menubar>  
   </toolbox>
 
   <vbox id="appcontent" flex="1">
 
     <browser id="content" type="content-primary" name="content" src="about:blank" flex="1"
-             context="viewSourceContextMenu" showcaret="true" tooltip="aHTMLTooltip"/>
+             context="viewSourceContextMenu" showcaret="true" tooltip="aHTMLTooltip" />
     <findbar id="FindToolbar" browserid="content"/>
   </vbox> 
 
   <statusbar id="status-bar" class="chromeclass-status">
     <statusbarpanel id="statusbar-line-col" label="" flex="1"/>
   </statusbar>
 
 </window>
--- a/toolkit/components/viewsource/content/viewSourceUtils.js
+++ b/toolkit/components/viewsource/content/viewSourceUtils.js
@@ -13,48 +13,83 @@
  */
 
 var gViewSourceUtils = {
 
   mnsIWebBrowserPersist: Components.interfaces.nsIWebBrowserPersist,
   mnsIWebProgress: Components.interfaces.nsIWebProgress,
   mnsIWebPageDescriptor: Components.interfaces.nsIWebPageDescriptor,
 
-  // Opens view source
-  viewSource: function(aURL, aPageDescriptor, aDocument, aLineNumber)
+  /**
+   * Opens the view source window.
+   *
+   * @param aArgsOrURL (required)
+   *        This is either an Object containing parameters, or a string
+   *        URL for the page we want to view the source of. In the latter
+   *        case we will be paying attention to the other parameters, as
+   *        we will be supporting the old API for this method.
+   *        If aArgsOrURL is an Object, the other parameters will be ignored.
+   *        aArgsOrURL as an Object can include the following properties:
+   *
+   *        URL (required):
+   *          A string URL for the page we'd like to view the source of.
+   *        browser (optional):
+   *          The browser containing the document that we would like to view the
+   *          source of. This is required if outerWindowID is passed.
+   *        outerWindowID (optional):
+   *          The outerWindowID of the content window containing the document that
+   *          we want to view the source of. Pass this if you want to attempt to
+   *          load the document source out of the network cache.
+   *        lineNumber (optional):
+   *          The line number to focus on once the source is loaded.
+   *
+   * @param aPageDescriptor (deprecated, optional)
+   *        Accepted for compatibility reasons, but is otherwise ignored.
+   * @param aDocument (deprecated, optional)
+   *        The content document we would like to view the source of. This
+   *        function will throw if aDocument is a CPOW.
+   * @param aLineNumber (deprecated, optional)
+   *        The line number to focus on once the source is loaded.
+   */
+  viewSource: function(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber)
   {
     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                           .getService(Components.interfaces.nsIPrefBranch);
-    if (prefs.getBoolPref("view_source.editor.external"))
-      this.openInExternalEditor(aURL, aPageDescriptor, aDocument, aLineNumber);
-    else
-      this.openInInternalViewer(aURL, aPageDescriptor, aDocument, aLineNumber);
+    if (prefs.getBoolPref("view_source.editor.external")) {
+      this.openInExternalEditor(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber);
+    } else {
+      this._openInInternalViewer(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber);
+    }
   },
 
   // Opens the interval view source viewer
-  openInInternalViewer: function(aURL, aPageDescriptor, aDocument, aLineNumber)
+  _openInInternalViewer: function(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber)
   {
     // try to open a view-source window while inheriting the charset (if any)
     var charset = null;
     var isForcedCharset = false;
     if (aDocument) {
+      if (Components.utils.isCrossProcessWrapper(aDocument)) {
+        throw new Error("View Source cannot accept a CPOW as a document.");
+      }
+
       charset = "charset=" + aDocument.characterSet;
-      try { 
+      try {
         isForcedCharset =
           aDocument.defaultView
                    .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIDOMWindowUtils)
                    .docCharsetIsForced;
       } catch (ex) {
       }
     }
     openDialog("chrome://global/content/viewSource.xul",
                "_blank",
                "all,dialog=no",
-               aURL, charset, aPageDescriptor, aLineNumber, isForcedCharset);
+               aArgsOrURL, charset, aPageDescriptor, aLineNumber, isForcedCharset);
   },
 
   buildEditorArgs: function(aPath, aLineNumber) {
     // Determine the command line arguments to pass to the editor.
     // We currently support a %LINE% placeholder which is set to the passed
     // line number (or to 0 if there's none)
     var editorArgs = [];
     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
--- a/toolkit/components/viewsource/jar.mn
+++ b/toolkit/components/viewsource/jar.mn
@@ -4,8 +4,9 @@
 
 toolkit.jar:
   content/global/viewSource.css             (content/viewSource.css)
   content/global/viewSource.js              (content/viewSource.js)
 * content/global/viewSource.xul             (content/viewSource.xul)
   content/global/viewPartialSource.js       (content/viewPartialSource.js)
 * content/global/viewPartialSource.xul      (content/viewPartialSource.xul)
   content/global/viewSourceUtils.js         (content/viewSourceUtils.js)
+  content/global/viewSource-content.js      (content/viewSource-content.js)
\ No newline at end of file
--- a/toolkit/components/viewsource/test/browser/browser.ini
+++ b/toolkit/components/viewsource/test/browser/browser.ini
@@ -5,9 +5,8 @@ support-files = head.js
 [browser_bug464222.js]
 [browser_bug699356.js]
 [browser_bug713810.js]
 [browser_contextmenu.js]
 skip-if = e10s # occasional timeouts with dead CPOW in console
 [browser_gotoline.js]
 [browser_viewsourceprefs.js]
 skip-if = e10s # Bug ?????? - obscure failure (Syntax highlighting off - Got true, expected false)
-[browser_viewsourceprefs_nonhtml.js]
--- a/toolkit/components/viewsource/test/browser/browser_contextmenu.js
+++ b/toolkit/components/viewsource/test/browser/browser_contextmenu.js
@@ -55,17 +55,17 @@ function checkMenuItems(popupNode, copyL
   popupNode.scrollIntoView();
 
   let cachedEvent = null;
   let mouseFn = function(event) {
     cachedEvent = event;
   };
 
   gViewSourceWindow.gBrowser.contentWindow.addEventListener("mousedown", mouseFn, false);
-  EventUtils.synthesizeMouseAtCenter(popupNode, { button: 2 }, gViewSourceWindow.gBrowser.contentWindow);
+  EventUtils.synthesizeMouseAtCenter(popupNode, { type: "contextmenu", button: 2 }, gViewSourceWindow.gBrowser.contentWindow);
   gViewSourceWindow.gBrowser.contentWindow.removeEventListener("mousedown", mouseFn, false);
 
   gContextMenu.openPopup(popupNode, "after_start", 0, 0, false, false, cachedEvent);
 
   is(gCopyLinkMenuItem.hidden, !copyLinkExpected, "Copy link menuitem is " + (copyLinkExpected ? "not hidden" : "hidden"));
   is(gCopyEmailMenuItem.hidden, !copyEmailExpected, "Copy email menuitem is " + (copyEmailExpected ? "not hidden" : "hidden"));
 
   if (!copyLinkExpected && !copyEmailExpected) {
--- a/toolkit/components/viewsource/test/browser/browser_gotoline.js
+++ b/toolkit/components/viewsource/test/browser/browser_gotoline.js
@@ -1,27 +1,39 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-let content = "line 1\nline 2\nline 3";
-let runningPlainText = false;
+Cu.import("resource://testing-common/ContentTaskUtils.jsm", this);
 
-function test() {
-  waitForExplicitFinish();
+let content = "line 1\nline 2\nline 3";
 
-  testViewSourceWindow("data:text/html," + encodeURIComponent(content), checkViewSource, function() {
-    testViewSourceWindow("data:text/plain," + encodeURIComponent(content), checkViewSource, finish);
-  });
-}
+add_task(function*() {
+  // First test with text with the text/html mimetype.
+  let win = yield loadViewSourceWindow("data:text/html," + encodeURIComponent(content));
+  yield checkViewSource(win);
+  yield BrowserTestUtils.closeWindow(win);
 
-function checkViewSource(aWindow) {
+  win = yield loadViewSourceWindow("data:text/plain," + encodeURIComponent(content));
+  yield checkViewSource(win);
+  yield BrowserTestUtils.closeWindow(win);
+});
+
+let checkViewSource = Task.async(function* (aWindow) {
   is(aWindow.gBrowser.contentDocument.body.textContent, content, "Correct content loaded");
-
   let selection = aWindow.gBrowser.contentWindow.getSelection();
   let statusPanel = aWindow.document.getElementById("statusbar-line-col");
   is(statusPanel.getAttribute("label"), "", "Correct status bar text");
+
   for (let i = 1; i <= 3; i++) {
-    aWindow.goToLine(i);
-    is(selection.toString(), "line " + i, "Correct text selected");
-    is(statusPanel.getAttribute("label"), "Line " + i + ", Col 1", "Correct status bar text");
+    aWindow.ViewSourceChrome.goToLine(i);
+    let result = yield ContentTask.spawn(aWindow.gBrowser, i, function*(i) {
+      let selection = content.getSelection();
+      return (selection.toString() == "line " + i);
+    });
+
+    ok(result, "Correct text selected");
+
+    yield ContentTaskUtils.waitForCondition(() => {
+      return (statusPanel.getAttribute("label") == "Line " + i + ", Col 1");
+    }, "Correct status bar text");
   }
-}
+});
--- a/toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js
+++ b/toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js
@@ -1,135 +1,134 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-const source = "about:mozilla";
-let mWindow, wrapMenuItem, syntaxMenuItem;
+let plaintextURL = "data:text/plain,hello+world";
+let htmlURL = "about:mozilla";
 
-// Check the default values are set.
-function test() {
-  waitForExplicitFinish();
-
+add_task(function* setup() {
   registerCleanupFunction(function() {
     SpecialPowers.clearUserPref("view_source.tab_size");
     SpecialPowers.clearUserPref("view_source.wrap_long_lines");
     SpecialPowers.clearUserPref("view_source.syntax_highlight");
   });
-
-  openViewSourceWindow(source, function(aWindow) {
-    mWindow = aWindow;
-    wrapMenuItem = aWindow.document.getElementById('menu_wrapLongLines');
-    syntaxMenuItem = aWindow.document.getElementById('menu_highlightSyntax');
+});
 
-    // Strip checked="false" attributes, since we're not interested in them.
-    if (wrapMenuItem.getAttribute("checked") == "false")
-      wrapMenuItem.removeAttribute("checked");
-    if (syntaxMenuItem.getAttribute("checked") == "false")
-      syntaxMenuItem.removeAttribute("checked");
+add_task(function*() {
+  yield exercisePrefs(plaintextURL, false);
+  yield exercisePrefs(htmlURL, true);
+});
+
+let exercisePrefs = Task.async(function* (source, highlightable) {
+  let win = yield loadViewSourceWindow(source);
+  let wrapMenuItem = win.document.getElementById("menu_wrapLongLines");
+  let syntaxMenuItem = win.document.getElementById("menu_highlightSyntax");
 
-    is(wrapMenuItem.hasAttribute("checked"), false, "Wrap menu item not checked by default");
-    is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked by default");
-    checkStyle(aWindow, "-moz-tab-size", 4);
-    checkStyle(aWindow, "white-space", "pre");
+  // Strip checked="false" attributes, since we're not interested in them.
+  if (wrapMenuItem.getAttribute("checked") == "false") {
+    wrapMenuItem.removeAttribute("checked");
+  }
+  if (syntaxMenuItem.getAttribute("checked") == "false") {
+    syntaxMenuItem.removeAttribute("checked");
+  }
 
-    test1();
-  });
-}
+  // Test the default states of these menu items.
+  is(wrapMenuItem.hasAttribute("checked"), false,
+     "Wrap menu item not checked by default");
+  is(syntaxMenuItem.hasAttribute("checked"), true,
+     "Syntax menu item checked by default");
 
-// Check that the Wrap Long Lines menu item works.
-function test1() {
+  yield checkStyle(win, "-moz-tab-size", 4);
+  yield checkStyle(win, "white-space", "pre");
+
+  // Next, test that the Wrap Long Lines menu item works.
   simulateClick(wrapMenuItem);
-
   is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
   is(SpecialPowers.getBoolPref("view_source.wrap_long_lines"), true, "Wrap pref set");
-  checkStyle(mWindow, "white-space", "pre-wrap");
-  test2();
-}
 
-function test2() {
+  yield checkStyle(win, "white-space", "pre-wrap");
+
   simulateClick(wrapMenuItem);
-
   is(wrapMenuItem.hasAttribute("checked"), false, "Wrap menu item unchecked");
   is(SpecialPowers.getBoolPref("view_source.wrap_long_lines"), false, "Wrap pref set");
-  checkStyle(mWindow, "white-space", "pre");
-  test3();
-}
+  yield checkStyle(win, "white-space", "pre");
 
-// Check that the Syntax Highlighting menu item works.
-function test3() {
-  mWindow.gBrowser.addEventListener("pageshow", function test3Handler() {
-    mWindow.gBrowser.removeEventListener("pageshow", test3Handler, false);
-    is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
-    is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), false, "Syntax highlighting pref set");
-
-    checkHighlight(mWindow, false);
-    test4();
-  }, false);
-
+  // Check that the Syntax Highlighting menu item works.
+  let pageShowPromise = BrowserTestUtils.waitForEvent(win.gBrowser, "pageshow");
   simulateClick(syntaxMenuItem);
-}
+  yield pageShowPromise;
 
-function test4() {
-  mWindow.gBrowser.addEventListener("pageshow", function test4Handler() {
-    mWindow.gBrowser.removeEventListener("pageshow", test4Handler, false);
-    is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked");
-    is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), true, "Syntax highlighting pref set");
+  is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
+  is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), false, "Syntax highlighting pref set");
+  yield checkHighlight(win, false);
 
-    checkHighlight(mWindow, true);
-    closeViewSourceWindow(mWindow, test5);
-  }, false);
-
+  pageShowPromise = BrowserTestUtils.waitForEvent(win.gBrowser, "pageshow");
   simulateClick(syntaxMenuItem);
-}
+  yield pageShowPromise;
+
+  is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked");
+  is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), true, "Syntax highlighting pref set");
 
-// Open a new view-source window to check prefs are obeyed.
-function test5() {
+  yield checkHighlight(win, highlightable);
+  yield BrowserTestUtils.closeWindow(win);
+
+  // Open a new view-source window to check that the prefs are obeyed.
   SpecialPowers.setIntPref("view_source.tab_size", 2);
   SpecialPowers.setBoolPref("view_source.wrap_long_lines", true);
   SpecialPowers.setBoolPref("view_source.syntax_highlight", false);
 
-  executeSoon(function() {
-    openViewSourceWindow(source, function(aWindow) {
-      wrapMenuItem = aWindow.document.getElementById('menu_wrapLongLines');
-      syntaxMenuItem = aWindow.document.getElementById('menu_highlightSyntax');
+  win = yield loadViewSourceWindow(source);
+  wrapMenuItem = win.document.getElementById("menu_wrapLongLines");
+  syntaxMenuItem = win.document.getElementById("menu_highlightSyntax");
+
+  // Strip checked="false" attributes, since we're not interested in them.
+  if (wrapMenuItem.getAttribute("checked") == "false") {
+    wrapMenuItem.removeAttribute("checked");
+  }
+  if (syntaxMenuItem.getAttribute("checked") == "false") {
+    syntaxMenuItem.removeAttribute("checked");
+  }
 
-      // Strip checked="false" attributes, since we're not interested in them.
-      if (wrapMenuItem.getAttribute("checked") == "false")
-        wrapMenuItem.removeAttribute("checked");
-      if (syntaxMenuItem.getAttribute("checked") == "false")
-        syntaxMenuItem.removeAttribute("checked");
+  is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
+  is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
+  yield checkStyle(win, "-moz-tab-size", 2);
+  yield checkStyle(win, "white-space", "pre-wrap");
+  yield checkHighlight(win, false);
 
-      is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
-      is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
-      checkStyle(aWindow, "-moz-tab-size", 2);
-      checkStyle(aWindow, "white-space", "pre-wrap");
-      checkHighlight(aWindow, false);
-      closeViewSourceWindow(aWindow, finish);
-    });
-  });
-}
+  SpecialPowers.clearUserPref("view_source.tab_size");
+  SpecialPowers.clearUserPref("view_source.wrap_long_lines");
+  SpecialPowers.clearUserPref("view_source.syntax_highlight");
+
+  yield BrowserTestUtils.closeWindow(win);
+});
 
 // Simulate a menu item click, including toggling the checked state.
 // This saves us from opening the menu and trying to click on the item,
 // which doesn't work on Mac OS X.
 function simulateClick(aMenuItem) {
   if (aMenuItem.hasAttribute("checked"))
     aMenuItem.removeAttribute("checked");
   else
     aMenuItem.setAttribute("checked", "true");
 
   aMenuItem.click();
 }
 
-function checkStyle(aWindow, aStyleProperty, aExpectedValue) {
-  let gBrowser = aWindow.gBrowser;
-  let computedStyle = gBrowser.contentWindow.getComputedStyle(gBrowser.contentDocument.body, null);
-
-  is(computedStyle.getPropertyValue(aStyleProperty), aExpectedValue, "Correct value of " + aStyleProperty);
-}
+let checkStyle = Task.async(function* (win, styleProperty, expected) {
+  let browser = win.gBrowser;
+  let value = yield ContentTask.spawn(browser, styleProperty, function* (styleProperty) {
+    let style = content.getComputedStyle(content.document.body, null);
+    return style.getPropertyValue(styleProperty);
+  });
+  is(value, expected, "Correct value of " + styleProperty);
+});
 
-function checkHighlight(aWindow, aExpected) {
-  let spans = aWindow.gBrowser.contentDocument.getElementsByTagName("span");
-  is(Array.some(spans, function(aSpan) {
-    return aSpan.className != "";
-  }), aExpected, "Syntax highlighting " + (aExpected ? "on" : "off"));
-}
+let checkHighlight = Task.async(function* (win, expected) {
+  let browser = win.gBrowser;
+  let highlighted = yield ContentTask.spawn(browser, {}, function* () {
+    let spans = content.document.getElementsByTagName("span");
+    return Array.some(spans, (span) => {
+      return span.className != "";
+    });
+  });
+  is(highlighted, expected, "Syntax highlighting " + (expected ? "on" : "off"));
+});
deleted file mode 100644
--- a/toolkit/components/viewsource/test/browser/browser_viewsourceprefs_nonhtml.js
+++ /dev/null
@@ -1,135 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-let source = "data:text/plain,hello+world";
-let mWindow, wrapMenuItem, syntaxMenuItem;
-
-// Check the default values are set.
-function test() {
-  waitForExplicitFinish();
-
-  registerCleanupFunction(function() {
-    SpecialPowers.clearUserPref("view_source.tab_size");
-    SpecialPowers.clearUserPref("view_source.wrap_long_lines");
-    SpecialPowers.clearUserPref("view_source.syntax_highlight");
-  });
-
-  openViewSourceWindow(source, function(aWindow) {
-    mWindow = aWindow;
-    wrapMenuItem = aWindow.document.getElementById('menu_wrapLongLines');
-    syntaxMenuItem = aWindow.document.getElementById('menu_highlightSyntax');
-
-    // Strip checked="false" attributes, since we're not interested in them.
-    if (wrapMenuItem.getAttribute("checked") == "false")
-      wrapMenuItem.removeAttribute("checked");
-    if (syntaxMenuItem.getAttribute("checked") == "false")
-      syntaxMenuItem.removeAttribute("checked");
-
-    is(wrapMenuItem.hasAttribute("checked"), false, "Wrap menu item not checked by default");
-    is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked by default");
-    checkStyle(aWindow, "-moz-tab-size", 4);
-    checkStyle(aWindow, "white-space", "pre");
-
-    test1();
-  });
-}
-
-// Check that the Wrap Long Lines menu item works.
-function test1() {
-  simulateClick(wrapMenuItem);
-
-  is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
-  is(SpecialPowers.getBoolPref("view_source.wrap_long_lines"), true, "Wrap pref set");
-  checkStyle(mWindow, "white-space", "pre-wrap");
-  test2();
-}
-
-function test2() {
-  simulateClick(wrapMenuItem);
-
-  is(wrapMenuItem.hasAttribute("checked"), false, "Wrap menu item unchecked");
-  is(SpecialPowers.getBoolPref("view_source.wrap_long_lines"), false, "Wrap pref set");
-  checkStyle(mWindow, "white-space", "pre");
-  test3();
-}
-
-// Check that the Syntax Highlighting menu item works.
-function test3() {
-  mWindow.gBrowser.addEventListener("pageshow", function test3Handler() {
-    mWindow.gBrowser.removeEventListener("pageshow", test3Handler, false);
-    is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
-    is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), false, "Syntax highlighting pref set");
-
-    checkHighlight(mWindow, false);
-    test4();
-  }, false);
-
-  simulateClick(syntaxMenuItem);
-}
-
-function test4() {
-  mWindow.gBrowser.addEventListener("pageshow", function test4Handler() {
-    mWindow.gBrowser.removeEventListener("pageshow", test4Handler, false);
-    is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked");
-    is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), true, "Syntax highlighting pref set");
-
-    checkHighlight(mWindow, false);
-    closeViewSourceWindow(mWindow, test5);
-  }, false);
-
-  simulateClick(syntaxMenuItem);
-}
-
-// Open a new view-source window to check prefs are obeyed.
-function test5() {
-  SpecialPowers.setIntPref("view_source.tab_size", 2);
-  SpecialPowers.setBoolPref("view_source.wrap_long_lines", true);
-  SpecialPowers.setBoolPref("view_source.syntax_highlight", false);
-
-  executeSoon(function() {
-    openViewSourceWindow(source, function(aWindow) {
-      wrapMenuItem = aWindow.document.getElementById('menu_wrapLongLines');
-      syntaxMenuItem = aWindow.document.getElementById('menu_highlightSyntax');
-
-      // Strip checked="false" attributes, since we're not interested in them.
-      if (wrapMenuItem.getAttribute("checked") == "false")
-        wrapMenuItem.removeAttribute("checked");
-      if (syntaxMenuItem.getAttribute("checked") == "false")
-        syntaxMenuItem.removeAttribute("checked");
-
-      is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
-      is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
-      checkStyle(aWindow, "-moz-tab-size", 2);
-      checkStyle(aWindow, "white-space", "pre-wrap");
-      checkHighlight(aWindow, false);
-      closeViewSourceWindow(aWindow, finish);
-    });
-  });
-}
-
-// Simulate a menu item click, including toggling the checked state.
-// This saves us from opening the menu and trying to click on the item,
-// which doesn't work on Mac OS X.
-function simulateClick(aMenuItem) {
-  if (aMenuItem.hasAttribute("checked"))
-    aMenuItem.removeAttribute("checked");
-  else
-    aMenuItem.setAttribute("checked", "true");
-
-  aMenuItem.click();
-}
-
-function checkStyle(aWindow, aStyleProperty, aExpectedValue) {
-  let gBrowser = aWindow.gBrowser;
-  let computedStyle = gBrowser.contentWindow.getComputedStyle(gBrowser.contentDocument.body, null);
-
-  is(computedStyle.getPropertyValue(aStyleProperty), aExpectedValue, "Correct value of " + aStyleProperty);
-}
-
-function checkHighlight(aWindow, aExpected) {
-  let spans = aWindow.gBrowser.contentDocument.getElementsByTagName("span");
-  is(Array.some(spans, function(aSpan) {
-    return aSpan.className != "";
-  }), aExpected, "Syntax highlighting " + (aExpected ? "on" : "off"));
-}
--- a/toolkit/components/viewsource/test/browser/head.js
+++ b/toolkit/components/viewsource/test/browser/head.js
@@ -9,16 +9,22 @@ function openViewSourceWindow(aURI, aCal
     if (event.target.location == "view-source:" + aURI) {
       info("View source window opened: " + event.target.location);
       viewSourceWindow.removeEventListener("pageshow", pageShowHandler, false);
       aCallback(viewSourceWindow);
     }
   }, false);
 }
 
+function loadViewSourceWindow(URL) {
+  return new Promise((resolve) => {
+    openViewSourceWindow(URL, resolve);
+  })
+}
+
 function closeViewSourceWindow(aWindow, aCallback) {
   Services.wm.addListener({
     onCloseWindow: function() {
       Services.wm.removeListener(this);
       executeSoon(aCallback);
     }
   });
   aWindow.close();
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -708,19 +708,19 @@
           if (!view)
             return;
           var rows = this.maxRows;
           if (!view.rowCount || (rows && view.rowCount < rows))
             rows = view.rowCount;
 
           var height = rows * bx.rowHeight;
 
-          if (height == 0)
+          if (height == 0) {
             this.tree.setAttribute("collapsed", "true");
-          else {
+          } else {
             if (this.tree.hasAttribute("collapsed"))
               this.tree.removeAttribute("collapsed");
 
             this.tree.setAttribute("height", height);
           }
           this.tree.setAttribute("hidescrollbar", view.rowCount <= rows);
         ]]>
         </body>
--- a/toolkit/devtools/output-parser.js
+++ b/toolkit/devtools/output-parser.js
@@ -34,51 +34,29 @@ const REGEX_CSS_VAR = /^\bvar\(\s*--[-_a
  */
 const REGEX_ALL_COLORS = /^#[0-9a-fA-F]{3}\b|^#[0-9a-fA-F]{6}\b|^hsl\(.*?\)|^hsla\(.*?\)|^rgba?\(.*?\)|^[a-zA-Z-]+/;
 
 loader.lazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
 /**
- * This regular expression catches all css property names with their trailing
- * spaces and semicolon. This is used to ensure a value is valid for a property
- * name within style="" attributes.
- */
-loader.lazyGetter(this, "REGEX_ALL_CSS_PROPERTIES", function () {
-  let names = DOMUtils.getCSSPropertyNames();
-    let pattern = "^(";
-
-    for (let i = 0; i < names.length; i++) {
-      if (i > 0) {
-        pattern += "|";
-      }
-      pattern += names[i];
-    }
-    pattern += ")\\s*:\\s*";
-
-    return new RegExp(pattern);
-});
-
-/**
  * This module is used to process text for output by developer tools. This means
  * linking JS files with the debugger, CSS files with the style editor, JS
  * functions with the debugger, placing color swatches next to colors and
  * adding doorhanger previews where possible (images, angles, lengths,
  * border radius, cubic-bezier etc.).
  *
  * Usage:
  *   const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
  *   const {OutputParser} = devtools.require("devtools/output-parser");
  *
  *   let parser = new OutputParser();
  *
  *   parser.parseCssProperty("color", "red"); // Returns document fragment.
- *   parser.parseHTMLAttribute("color:red; font-size: 12px;"); // Returns document
- *                                                             // fragment.
  */
 function OutputParser() {
   this.parsed = [];
   this.colorSwatches = new WeakMap();
   this._onSwatchMouseDown = this._onSwatchMouseDown.bind(this);
 }
 
 exports.OutputParser = OutputParser;
@@ -95,50 +73,32 @@ OutputParser.prototype = {
    *         Options object. For valid options and default values see
    *         _mergeOptions().
    * @return {DocumentFragment}
    *         A document fragment containing color swatches etc.
    */
   parseCssProperty: function(name, value, options={}) {
     options = this._mergeOptions(options);
 
-    // XXX: This is a quick fix that should stay until bug 977063 gets fixed.
-    // It avoids parsing "linear" as a timing-function in "linear-gradient(...)"
-    options.expectCubicBezier = ["transition", "transition-timing-function",
-      "animation", "animation-timing-function"].indexOf(name) !== -1;
-
+    options.expectCubicBezier =
+      safeCssPropertySupportsType(name, DOMUtils.TYPE_TIMING_FUNCTION);
     options.expectFilter = name === "filter";
+    options.supportsColor =
+      safeCssPropertySupportsType(name, DOMUtils.TYPE_COLOR) ||
+      safeCssPropertySupportsType(name, DOMUtils.TYPE_GRADIENT);
 
     if (this._cssPropertySupportsValue(name, value)) {
       return this._parse(value, options);
     }
     this._appendTextNode(value);
 
     return this._toDOM();
   },
 
   /**
-   * Parse a string.
-   *
-   * @param  {String} value
-   *         Text to parse.
-   * @param  {Object} [options]
-   *         Options object. For valid options and default values see
-   *         _mergeOptions().
-   * @return {DocumentFragment}
-   *         A document fragment. Colors will not be parsed.
-   */
-  parseHTMLAttribute: function(value, options={}) {
-    options.isHTMLAttribute = true;
-    options = this._mergeOptions(options);
-
-    return this._parse(value, options);
-  },
-
-  /**
    * Matches the beginning of the provided string to a css background-image url
    * and return both the whole url(...) match and the url itself.
    * This isn't handled via a regular expression to make sure we can match urls
    * that contain parenthesis easily
    */
   _matchBackgroundUrl: function(text) {
     let startToken = "url(";
     if (text.indexOf(startToken) !== 0) {
@@ -235,30 +195,17 @@ OutputParser.prototype = {
           let match = matched[0];
           text = this._trimMatchFromStart(text, match);
 
           this._appendCubicBezier(match, options);
           continue;
         }
       }
 
-      matched = text.match(REGEX_ALL_CSS_PROPERTIES);
-      if (matched) {
-        let [match] = matched;
-
-        text = this._trimMatchFromStart(text, match);
-        this._appendTextNode(match);
-
-        if (options.isHTMLAttribute) {
-          [text] = this._appendColorOnMatch(text, options);
-        }
-        continue;
-      }
-
-      if (!options.isHTMLAttribute) {
+      if (options.supportsColor) {
         let dirty;
 
         [text, dirty] = this._appendColorOnMatch(text, options);
 
         if (dirty) {
           continue;
         }
       }
@@ -583,43 +530,54 @@ OutputParser.prototype = {
    *           - defaultColorType: true // Convert colors to the default type
    *                                    // selected in the options panel.
    *           - colorSwatchClass: ""   // The class to use for color swatches.
    *           - colorClass: ""         // The class to use for the color value
    *                                    // that follows the swatch.
    *           - bezierSwatchClass: ""  // The class to use for bezier swatches.
    *           - bezierClass: ""        // The class to use for the bezier value
    *                                    // that follows the swatch.
-   *           - isHTMLAttribute: false // This property indicates whether we
-   *                                    // are parsing an HTML attribute value.
-   *                                    // When the value is passed in from an
-   *                                    // HTML attribute we need to check that
-   *                                    // any CSS property values are supported
-   *                                    // by the property name before
-   *                                    // processing the property value.
+   *           - supportsColor: false   // Does the CSS property support colors?
    *           - urlClass: ""           // The class to be used for url() links.
    *           - baseURI: ""            // A string or nsIURI used to resolve
    *                                    // relative links.
    * @return {Object}
    *         Overridden options object
    */
   _mergeOptions: function(overrides) {
     let defaults = {
       defaultColorType: true,
       colorSwatchClass: "",
       colorClass: "",
       bezierSwatchClass: "",
       bezierClass: "",
-      isHTMLAttribute: false,
+      supportsColor: false,
       urlClass: "",
       baseURI: ""
     };
 
     if (typeof overrides.baseURI === "string") {
       overrides.baseURI = Services.io.newURI(overrides.baseURI, null, null);
     }
 
     for (let item in overrides) {
       defaults[item] = overrides[item];
     }
     return defaults;
   }
 };
+
+/**
+ * A wrapper for DOMUtils.cssPropertySupportsType that ignores invalid
+ * properties.
+ *
+ * @param {String} name The property name.
+ * @param {number} type The type tested for support.
+ * @return {Boolean} Whether the property supports the type.
+ *        If the property is unknown, false is returned.
+ */
+function safeCssPropertySupportsType(name, type) {
+  try {
+    return DOMUtils.cssPropertySupportsType(name, type);
+  } catch(e) {
+    return false;
+  }
+}
--- a/toolkit/mozapps/extensions/internal/moz.build
+++ b/toolkit/mozapps/extensions/internal/moz.build
@@ -28,8 +28,12 @@ EXTRA_PP_JS_MODULES.addons += [
 
 # This is used in multiple places, so is defined here to avoid it getting
 # out of sync.
 DEFINES['MOZ_EXTENSIONS_DB_SCHEMA'] = 17
 
 # Additional debugging info is exposed in debug builds
 if CONFIG['MOZ_EM_DEBUG']:
     DEFINES['MOZ_EM_DEBUG'] = 1
+
+# Add-on signing cannot be preffed off in official beta, release or esr builds
+if CONFIG['MOZ_UPDATE_CHANNEL'] in ('beta', 'release', 'esr') and CONFIG['MOZ_OFFICIAL_BRANDING']:
+    DEFINES['MOZ_REQUIRE_SIGNING'] = 1
--- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
+++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
@@ -1797,17 +1797,17 @@ function setupAppFiles() {
   let appFiles = [ { relPath  : FILE_APP_BIN,
                      inGreDir : false },
                    { relPath  : FILE_APPLICATION_INI,
                      inGreDir : true },
                    { relPath  : "dependentlibs.list",
                      inGreDir : true } ];
 
   // On Linux the updater.png must also be copied
-  if (IS_UNIX && !IS_MACOSX) {
+  if (IS_UNIX && !IS_MACOSX && !IS_TOOLKIT_GONK) {
     appFiles.push( { relPath  : "icons/updater.png",
                      inGreDir : true } );
   }
 
   // Read the dependent libs file leafnames from the dependentlibs.list file
   // into the array.
   let deplibsFile = gGREDirOrig.clone();
   deplibsFile.append("dependentlibs.list");
@@ -1884,24 +1884,26 @@ function copyFileToTestAppDir(aFileRelPa
       if (pathParts[i]) {
         srcFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : ""));
         destFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : ""));
       }
     }
     fileRelPath = fileRelPath + ".app";
   }
 
-  Assert.ok(srcFile.exists(), MSG_SHOULD_EXIST);
+  Assert.ok(srcFile.exists(),
+            MSG_SHOULD_EXIST + ", leafName: " + srcFile.leafName);
 
   // Symlink libraries. Note that the XUL library on Mac OS X doesn't have a
   // file extension and shouldSymlink will always be false on Windows.
   let shouldSymlink = (pathParts[pathParts.length - 1] == "XUL" ||
                        fileRelPath.substr(fileRelPath.length - 3) == ".so" ||
                        fileRelPath.substr(fileRelPath.length - 6) == ".dylib");
-  if (!shouldSymlink) {
+  // The tests don't support symlinks on gonk.
+  if (!shouldSymlink || IS_TOOLKIT_GONK) {
     if (!destFile.exists()) {
       try {
         srcFile.copyToFollowingLinks(destFile.parent, destFile.leafName);
       } catch (e) {
         // Just in case it is partially copied
         if (destFile.exists()) {
           try {
             destFile.remove(true);
@@ -3205,17 +3207,17 @@ function overrideUpdatePrompt(aCallback)
 }
 
 function UpdatePrompt(aCallback) {
   this._callback = aCallback;
 
   let fns = ["checkForUpdates", "showUpdateAvailable", "showUpdateDownloaded",
              "showUpdateError", "showUpdateHistory", "showUpdateInstalled"];
 
-  fns.forEach(function(aPromptFn) {
+  fns.forEach(function UP_fns(aPromptFn) {
     UpdatePrompt.prototype[aPromptFn] = function() {
       if (!this._callback) {
         return;
       }
 
       let callback = this._callback[aPromptFn];
       if (!callback) {
         return;
--- a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js
@@ -12,17 +12,18 @@ function run_test() {
   gTestFiles[gTestFiles.length - 1].compareContents = "FromComplete\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR);
 
   createUpdaterINI(false);
   setAppBundleModTime();
 
-  if (IS_UNIX) {
+  // The tests don't support symlinks on gonk.
+  if (IS_UNIX && !IS_TOOLKIT_GONK) {
     removeSymlink();
     createSymlink();
     do_register_cleanup(removeSymlink);
     gTestFiles.splice(gTestFiles.length - 3, 0,
     {
       description      : "Readable symlink",
       fileName         : "link",
       relPathDir       : DIR_RESOURCES,
@@ -82,17 +83,18 @@ function checkUpdateApplied() {
  */
 function finishCheckUpdateApplied() {
   checkAppBundleModTime();
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, false);
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  if (IS_UNIX) {
+  // The tests don't support symlinks on gonk.
+  if (IS_UNIX && !IS_TOOLKIT_GONK) {
     checkSymlink();
   }
   checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
   standardInit();
   checkCallbackAppLog();
 }
 
 function runHelperProcess(args) {
--- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
@@ -24,17 +24,18 @@ function run_test() {
     writeFile(testFile, "test\n");
   }
 
   createUpdaterINI(false);
   setAppBundleModTime();
 
   // Don't test symlinks on Mac OS X in this test since it tends to timeout.
   // It is tested on Mac OS X in marAppInUseStageSuccessComplete_unix.js
-  if (IS_UNIX && !IS_MACOSX) {
+  // The tests don't support symlinks on gonk.
+  if (IS_UNIX && !IS_MACOSX && !IS_TOOLKIT_GONK) {
     removeSymlink();
     createSymlink();
     do_register_cleanup(removeSymlink);
     gTestFiles.splice(gTestFiles.length - 3, 0,
     {
       description      : "Readable symlink",
       fileName         : "link",
       relPathDir       : DIR_RESOURCES,
@@ -82,17 +83,18 @@ function finishCheckUpdateApplied() {
   checkPostUpdateRunningFile(true);
 
   if (IS_MACOSX) {
     let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
     Assert.ok(!distributionDir.exists(), MSG_SHOULD_NOT_EXIST);
     checkUpdateLogContains("removing old distribution directory");
   }
 
-  if (IS_UNIX && !IS_MACOSX) {
+  // The tests don't support symlinks on gonk.
+  if (IS_UNIX && !IS_MACOSX && !IS_TOOLKIT_GONK) {
     checkSymlink();
   }
   checkAppBundleModTime();
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, false);
   checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
   standardInit();
   checkCallbackAppLog();
 }
--- a/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
+++ b/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
@@ -10,36 +10,38 @@
 tags = appupdate
 head = head_update.js
 tail =
 
 [marSuccessComplete.js]
 [marSuccessPartial.js]
 [marFailurePartial.js]
 [marStageSuccessComplete.js]
-skip-if = toolkit == 'gonk'
-reason = bug 820380
 [marStageSuccessPartial.js]
 [marVersionDowngrade.js]
 skip-if = os != 'win' && os != 'mac'
+reason = mar signing
 [marWrongChannel.js]
 skip-if = os != 'win' && os != 'mac'
+reason = mar signing
 [marStageFailurePartial.js]
 [marCallbackAppSuccessComplete_win.js]
 skip-if = os != 'win'
 [marCallbackAppSuccessPartial_win.js]
 skip-if = os != 'win'
 [marCallbackAppStageSuccessComplete_win.js]
 skip-if = os != 'win'
 [marCallbackAppStageSuccessPartial_win.js]
 skip-if = os != 'win'
 [marAppInUseSuccessComplete.js]
-skip-if = toolkit == 'gonk'
+skip-if = debug == true && toolkit == 'gonk'
+reason = bug 1163355
 [marAppInUseStageSuccessComplete_unix.js]
-skip-if = !(os == 'linux' || os == 'sunos' || os == 'mac')
+skip-if = os == 'win' || debug == true && toolkit == 'gonk'
+reason = bug 1163355
 [marAppInUseStageFailureComplete_win.js]
 skip-if = os != 'win'
 [marAppInUseFallbackStageFailureComplete_win.js]
 skip-if = os != 'win'
 [marFileLockedFailureComplete_win.js]
 skip-if = os != 'win'
 [marFileLockedFailurePartial_win.js]
 skip-if = os != 'win'
@@ -71,18 +73,18 @@ skip-if = os != 'win'
 [marFileInUseFallbackStageFailureComplete_win.js]
 skip-if = os != 'win'
 [marFileInUseFallbackStageFailurePartial_win.js]
 skip-if = os != 'win'
 [marRMRFDirFileInUseFallbackStageFailureComplete_win.js]
 skip-if = os != 'win'
 [marRMRFDirFileInUseFallbackStageFailurePartial_win.js]
 skip-if = os != 'win'
-reason = bug 820380
 [marAppApplyDirLockedStageFailure_win.js]
 skip-if = os != 'win'
 [marAppApplyUpdateAppBinInUseStageSuccess_win.js]
 skip-if = os != 'win'
 [marAppApplyUpdateSuccess.js]
-skip-if = toolkit == 'gonk'
-reason = bug 820380
+skip-if = debug == true && toolkit == 'gonk'
+reason = bug 1163354
 [marAppApplyUpdateStageSuccess.js]
-skip-if = toolkit == 'gonk'
+skip-if = debug == true && toolkit == 'gonk'
+reason = bug 1163354
--- a/toolkit/themes/linux/global/in-content/common.css
+++ b/toolkit/themes/linux/global/in-content/common.css
@@ -78,17 +78,16 @@ xul|*.radio-label-box {
   -moz-appearance: none;
 }
 
 xul|*.numberbox-input-box {
   -moz-appearance: none;
   border-width: 0;
 }
 
-html|a:-moz-focusring,
 xul|*.text-link:-moz-focusring,
 xul|*.inline-link:-moz-focusring {
   border: 1px dotted -moz-DialogText;
 }
 
 xul|spinbuttons {
   -moz-appearance: none;
 }
--- a/toolkit/themes/osx/global/in-content/common.css
+++ b/toolkit/themes/osx/global/in-content/common.css
@@ -71,17 +71,16 @@ xul|*.numberbox-input-box {
   border-width: 0;
 }
 
 xul|description {
   font-size: 1.25rem;
   line-height: 22px;
 }
 
-html|a:-moz-focusring,
 xul|*.text-link:-moz-focusring,
 xul|*.inline-link:-moz-focusring {
   color: #ff9500;
   text-decoration: underline;
   box-shadow: none;
 }
 
 xul|button:-moz-focusring,
--- a/toolkit/themes/shared/extensions/extensions.inc.css
+++ b/toolkit/themes/shared/extensions/extensions.inc.css
@@ -52,21 +52,16 @@
   .global-warning .warning-icon {
     background-color: #fff;
     box-shadow: 0 0 2px 5px #fff;
     border-radius: 10px;
   }
 }
 
 /*** global informations ***/
-#addons-page .global-info-container {
-  background-color: #f3f7fb;
-  border-top-right-radius: 2px;
-  border-top-left-radius: 2px;
-}
 
 /* Plugins aren't yet disabled by safemode (bug 342333),
    so don't show that warning when viewing plugins. */
 #addons-page[warning="safemode"] .view-pane[type="plugin"] .global-warning-container,
 #addons-page[warning="safemode"] #detail-view[loading="true"] .global-warning-container {
   background-color: inherit;
   background-image: none;
 }
@@ -348,20 +343,28 @@
 }
 
 
 /*** list ***/
 
 .list {
   -moz-appearance: none;
   margin: 0;
-  border-color: transparent;
+  border-width: 0 !important;
   background-color: transparent;
 }
 
+.list > scrollbox > .scrollbox-innerbox {
+  border: 1px dotted transparent;
+}
+
+.list:-moz-focusring > scrollbox > .scrollbox-innerbox {
+  border-color: #0095dd;
+}
+
 .addon {
   color: #444;
   border-bottom: 1px solid #c1c1c1;
   padding: 5px;
   background-origin: border-box;
 }
 
 .addon:not(:only-child):last-child {
--- a/toolkit/themes/windows/global/in-content/common.css
+++ b/toolkit/themes/windows/global/in-content/common.css
@@ -51,17 +51,16 @@ xul|radio {
   }
 }
 
 xul|*.radio-icon,
 xul|*.checkbox-icon {
   -moz-margin-end: 0;
 }
 
-html|a:-moz-focusring,
 xul|*.text-link:-moz-focusring,
 xul|*.inline-link:-moz-focusring {
   border: 1px dotted -moz-DialogText;
 }
 
 /* Don't draw a transparent border for the focusring because when page
    colors are disabled, the border is drawn in -moz-DialogText */
 xul|*.text-link:not(:-moz-focusring),