Merge fx-team to m-c a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 29 Apr 2015 16:46:00 -0700
changeset 241647 4b9b12c248dc
parent 241616 a86ed85747d8 (current diff)
parent 241646 36d234c6d4b8 (diff)
child 241648 ef9aacc12777
child 241698 fba992940a64
child 241710 02dad33cf2a0
push id28666
push userkwierso@gmail.com
push date2015-04-29 23:46 +0000
treeherdermozilla-central@4b9b12c248dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
first release with
nightly linux32
4b9b12c248dc / 40.0a1 / 20150430030201 / files
nightly linux64
4b9b12c248dc / 40.0a1 / 20150430030201 / files
nightly mac
4b9b12c248dc / 40.0a1 / 20150430030201 / files
nightly win32
4b9b12c248dc / 40.0a1 / 20150430030201 / files
nightly win64
4b9b12c248dc / 40.0a1 / 20150430030201 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c a=merge CLOSED TREE
browser/devtools/shared/widgets/SideMenuWidget.jsm
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
mobile/android/base/tests/testReaderMode.java
mobile/android/chrome/content/browser.js
toolkit/components/places/PlacesUtils.jsm
toolkit/devtools/server/actors/script.js
--- a/browser/components/places/tests/browser/browser_bookmarksProperties.js
+++ b/browser/components/places/tests/browser/browser_bookmarksProperties.js
@@ -245,17 +245,16 @@ gTests.push({
     PlacesUtils.tagging.untagURI(PlacesUtils._uri(TEST_URL), ["testTag"]);
     PlacesUtils.bookmarks.removeItem(this._itemId);
   }
 });
 
 
 //------------------------------------------------------------------------------
 // Bug 475529 -  Add button in new folder dialog not default anymore
-/*
 gTests.push({
   desc: "Bug 475529 - Add button in new folder dialog not default anymore",
   sidebar: SIDEBAR_BOOKMARKS_ID,
   action: ACTION_ADD,
   itemType: TYPE_FOLDER,
   window: null,
   _itemId: null,
 
@@ -281,16 +280,17 @@ gTests.push({
       self.window.removeEventListener("unload", arguments.callee, false);
       executeSoon(function () {
         self.finish();
       });
     }, false);
 
     info("About to focus the namePicker field");
     namePicker.focus();
+    namePicker.select();
     EventUtils.synthesizeKey("n", {}, this.window);
     EventUtils.synthesizeKey("VK_RETURN", {}, this.window);
   },
 
   finish: function() {
     // Window is already closed.
     SidebarUI.hide();
     runNextTest();
@@ -300,17 +300,16 @@ gTests.push({
     // Check that folder name has been changed.
     is(PlacesUtils.bookmarks.getItemTitle(this._itemId), "n",
        "Folder name has been edited");
 
     // Cleanup.
     PlacesUtils.bookmarks.removeItem(this._itemId);
   }
 });
-*/
 
 //------------------------------------------------------------------------------
 // Bug 476020 - Pressing Esc while having the tag autocomplete open closes the bookmarks panel
 
 gTests.push({
   desc: "Bug 476020 - Pressing Esc while having the tag autocomplete open closes the bookmarks panel",
   sidebar: SIDEBAR_BOOKMARKS_ID,
   action: ACTION_EDIT,
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 Components.utils.import("resource://gre/modules/Downloads.jsm");
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource://gre/modules/Task.jsm");
+Components.utils.import("resource:///modules/TransientPrefs.jsm");
 
 var gMainPane = {
   /**
    * Initialization of this.
    */
   init: function ()
   {
     function setEventListener(aId, aEventType, aCallback)
@@ -42,16 +43,25 @@ var gMainPane = {
       let sysInfo = Cc["@mozilla.org/system-info;1"].
                     getService(Ci.nsIPropertyBag2);
       let ver = parseFloat(sysInfo.getProperty("version"));
       let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
       showTabsInTaskbar.hidden = ver < 6.1;
     } catch (ex) {}
 #endif
 
+    // The "closing multiple tabs" and "opening multiple tabs might slow down
+    // &brandShortName;" warnings provide options for not showing these
+    // warnings again. When the user disabled them, we provide checkboxes to
+    // re-enable the warnings.
+    if (!TransientPrefs.prefShouldBeVisible("browser.tabs.warnOnClose"))
+      document.getElementById("warnCloseMultiple").hidden = true;
+    if (!TransientPrefs.prefShouldBeVisible("browser.tabs.warnOnOpen"))
+      document.getElementById("warnOpenMany").hidden = true;
+
     setEventListener("browser.privatebrowsing.autostart", "change",
                      gMainPane.updateBrowserStartupLastSession);
     setEventListener("browser.download.dir", "change",
                      gMainPane.displayDownloadDirPref);
 #ifdef HAVE_SHELL_SERVICE
     setEventListener("setDefaultButton", "command",
                      gMainPane.setDefaultBrowser);
 #endif
--- a/browser/components/preferences/tabs.js
+++ b/browser/components/preferences/tabs.js
@@ -21,32 +21,41 @@ var gTabsPane = {
    *   allowed to cancel the action, false to just close the window
    * browser.tabs.warnOnOpen
    * - true if the user should be warned if he attempts to open a lot of tabs at
    *   once (e.g. a large folder of bookmarks), false otherwise
    * browser.taskbar.previews.enable
    * - true if tabs are to be shown in the Windows 7 taskbar
    */
 
+  init: function () {
 #ifdef XP_WIN
-  /**
-   * Initialize any platform-specific UI.
-   */
-  init: function () {
     const Cc = Components.classes;
     const Ci = Components.interfaces;
     try {
       let sysInfo = Cc["@mozilla.org/system-info;1"].
                     getService(Ci.nsIPropertyBag2);
       let ver = parseFloat(sysInfo.getProperty("version"));
       let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
       showTabsInTaskbar.hidden = ver < 6.1;
     } catch (ex) {}
+#endif
+
+    // The "closing multiple tabs" and "opening multiple tabs might slow down
+    // &brandShortName;" warnings provide options for not showing these
+    // warnings again. When the user disabled them, we provide checkboxes to
+    // re-enable the warnings.
+    let TransientPrefs =
+      Components.utils.import("resource:///modules/TransientPrefs.jsm", {})
+                .TransientPrefs;
+    if (!TransientPrefs.prefShouldBeVisible("browser.tabs.warnOnClose"))
+      document.getElementById("warnCloseMultiple").hidden = true;
+    if (!TransientPrefs.prefShouldBeVisible("browser.tabs.warnOnOpen"))
+      document.getElementById("warnOpenMany").hidden = true;
   },
-#endif
 
   /**
    * Determines where a link which opens a new window will open.
    *
    * @returns |true| if such links should be opened in new tabs
    */
   readLinkTarget: function() {
     var openNewWindow = document.getElementById("browser.link.open_newwindow");
--- a/browser/components/preferences/tabs.xul
+++ b/browser/components/preferences/tabs.xul
@@ -11,19 +11,17 @@
 <!ENTITY % tabsDTD SYSTEM "chrome://browser/locale/preferences/tabs.dtd">
 %tabsDTD;
 ]>
 
 <overlay id="TabsPaneOverlay"
          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <prefpane id="paneTabs"
-#ifdef XP_WIN
             onpaneload="gTabsPane.init();"
-#endif
             helpTopic="prefs-tabs">
 
     <preferences id="tabsPreferences">
       <preference id="browser.link.open_newwindow"     name="browser.link.open_newwindow"     type="int"/>
       <preference id="browser.tabs.loadInBackground"   name="browser.tabs.loadInBackground"   type="bool" inverted="true"/>
       <preference id="browser.tabs.warnOnClose"        name="browser.tabs.warnOnClose"        type="bool"/>
       <preference id="browser.tabs.warnOnOpen"         name="browser.tabs.warnOnOpen"         type="bool"/>
       <preference id="browser.sessionstore.restore_on_demand" name="browser.sessionstore.restore_on_demand" type="bool"/>
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu.js
@@ -96,42 +96,53 @@ function test() {
     ok(isCaretPos(gPanel, 9),
       "The editor location is correct before pausing.");
 
     sendMouseClickToTab(gTab, content.document.querySelector("button"));
 
     return finished;
   }
 
-  function initialChecks() {
+  let initialChecks = Task.async(function*() {
     for (let source of gSources) {
       for (let breakpoint of source) {
         ok(gBreakpoints._getAdded(breakpoint.attachment),
           "All breakpoint items should have corresponding promises (1).");
         ok(!gBreakpoints._getRemoving(breakpoint.attachment),
           "All breakpoint items should have corresponding promises (2).");
         ok(breakpoint.attachment.actor,
           "All breakpoint items should have corresponding promises (3).");
         is(!!breakpoint.attachment.disabled, false,
           "All breakpoints should initially be enabled.");
 
         let prefix = "bp-cMenu-"; // "breakpoints context menu"
         let identifier = gBreakpoints.getIdentifier(breakpoint.attachment);
         let enableSelfId = prefix + "enableSelf-" + identifier + "-menuitem";
         let disableSelfId = prefix + "disableSelf-" + identifier + "-menuitem";
 
+        // Check to make sure that only the bp context menu is shown when right clicking
+        // this node (Bug 1159276).
+        let menu = gDebugger.document.getElementById("bp-mPop-" + identifier);
+        let contextMenuShown = once(gDebugger.document, "popupshown");
+        EventUtils.synthesizeMouseAtCenter(breakpoint.prebuiltNode, {type: 'contextmenu', button: 2}, gDebugger);
+        let event = yield contextMenuShown;
+        is (event.originalTarget.id, menu.id, "The correct context menu was shown");
+        let contextMenuHidden = once(gDebugger.document, "popuphidden");
+        menu.hidePopup();
+        yield contextMenuHidden;
+
         is(gDebugger.document.getElementById(enableSelfId).getAttribute("hidden"), "true",
           "The 'Enable breakpoint' context menu item should initially be hidden'.");
         ok(!gDebugger.document.getElementById(disableSelfId).hasAttribute("hidden"),
           "The 'Disable breakpoint' context menu item should initially not be hidden'.");
         is(breakpoint.attachment.view.checkbox.getAttribute("checked"), "true",
           "All breakpoints should initially have a checked checkbox.");
       }
     }
-  }
+  });
 
   function checkBreakpointToggleSelf(aIndex) {
     let deferred = promise.defer();
 
     EventUtils.sendMouseEvent({ type: "click" },
       gDebugger.document.querySelectorAll(".dbg-breakpoint")[aIndex],
       gDebugger);
 
--- a/browser/devtools/shared/widgets/SideMenuWidget.jsm
+++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm
@@ -395,16 +395,25 @@ SideMenuWidget.prototype = {
   /**
    * Shows the contextMenu element.
    */
   _showContextMenu: function(e) {
     if (!this._contextMenu) {
       return;
     }
 
+    // Don't show the menu if a descendant node is going to be visible also.
+    let node = e.originalTarget;
+    while (node && node !== this._list) {
+      if (node.hasAttribute("contextmenu")) {
+        return;
+      }
+      node = node.parentNode;
+    }
+
     this._contextMenu.openPopupAtScreen(e.screenX, e.screenY, true);
   },
 
   window: null,
   document: null,
   _showArrows: false,
   _showItemCheckboxes: false,
   _showGroupCheckboxes: false,
--- a/browser/extensions/pdfjs/test/browser.ini
+++ b/browser/extensions/pdfjs/test/browser.ini
@@ -1,13 +1,9 @@
 [DEFAULT]
-skip-if = e10s # Bug 942707 - PDF viewer doesn't work with e10s.
+skip-if = e10s # Bug 1159385
 support-files = file_pdfjs_test.pdf
 
 [browser_pdfjs_main.js]
-skip-if = debug # bug 1058695
 [browser_pdfjs_navigation.js]
-skip-if = debug # bug 1058695
 [browser_pdfjs_savedialog.js]
 [browser_pdfjs_views.js]
-skip-if = debug # bug 1058695
 [browser_pdfjs_zoom.js]
-skip-if = debug # bug 1058695
new file mode 100644
--- /dev/null
+++ b/browser/modules/TransientPrefs.jsm
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["TransientPrefs"];
+
+Components.utils.import("resource://gre/modules/Preferences.jsm");
+
+let prefVisibility = new Map;
+
+/* Use for preferences that should only be visible when they've been modified.
+   When reset to their default state, they remain visible until restarting the
+   application. */
+
+this.TransientPrefs = {
+  prefShouldBeVisible: function (prefName) {
+    if (Preferences.isSet(prefName))
+      prefVisibility.set(prefName, true);
+
+    return !!prefVisibility.get(prefName);
+  }
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -34,16 +34,17 @@ EXTRA_JS_MODULES += [
     'PluginContent.jsm',
     'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
     'RecentWindow.jsm',
     'RemotePrompt.jsm',
     'SelfSupportBackend.jsm',
     'SitePermissions.jsm',
     'Social.jsm',
+    'TransientPrefs.jsm',
     'WebappManager.jsm',
     'webrtcUI.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'Windows8WindowFrameColor.jsm',
         'WindowsJumpLists.jsm',
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9410,71 +9410,84 @@ namespace {
 #ifdef MOZ_PLACES
 // Callback used by CopyFavicon to inform the favicon service that one URI
 // (mNewURI) has the same favicon URI (OnComplete's aFaviconURI) as another.
 class nsCopyFaviconCallback final : public nsIFaviconDataCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
-  nsCopyFaviconCallback(nsIURI* aNewURI, bool aInPrivateBrowsing)
-    : mNewURI(aNewURI)
+  nsCopyFaviconCallback(mozIAsyncFavicons* aSvc,
+                        nsIURI* aNewURI,
+                        bool aInPrivateBrowsing)
+    : mSvc(aSvc)
+    , mNewURI(aNewURI)
     , mInPrivateBrowsing(aInPrivateBrowsing)
   {
   }
 
   NS_IMETHODIMP
   OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
              const uint8_t* aData, const nsACString& aMimeType) override
   {
     // Continue only if there is an associated favicon.
     if (!aFaviconURI) {
       return NS_OK;
     }
 
-    NS_ASSERTION(aDataLen == 0,
-                 "We weren't expecting the callback to deliver data.");
-    nsCOMPtr<mozIAsyncFavicons> favSvc =
-      do_GetService("@mozilla.org/browser/favicon-service;1");
-    NS_ENSURE_STATE(favSvc);
-
-    return favSvc->SetAndFetchFaviconForPage(
+    MOZ_ASSERT(aDataLen == 0,
+               "We weren't expecting the callback to deliver data.");
+
+    return mSvc->SetAndFetchFaviconForPage(
       mNewURI, aFaviconURI, false,
       mInPrivateBrowsing ? nsIFaviconService::FAVICON_LOAD_PRIVATE :
                            nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
       nullptr);
   }
 
 private:
   ~nsCopyFaviconCallback() {}
 
+  nsCOMPtr<mozIAsyncFavicons> mSvc;
   nsCOMPtr<nsIURI> mNewURI;
   bool mInPrivateBrowsing;
 };
 
 NS_IMPL_ISUPPORTS(nsCopyFaviconCallback, nsIFaviconDataCallback)
 #endif
 
-// Tell the favicon service that aNewURI has the same favicon as aOldURI.
+} // anonymous namespace
+
 void
-CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI, bool aInPrivateBrowsing)
-{
+nsDocShell::CopyFavicon(nsIURI* aOldURI,
+                        nsIURI* aNewURI,
+                        bool aInPrivateBrowsing)
+{
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+    if (contentChild) {
+      mozilla::ipc::URIParams oldURI, newURI;
+      SerializeURI(aOldURI, oldURI);
+      SerializeURI(aNewURI, newURI);
+      contentChild->SendCopyFavicon(oldURI, newURI, aInPrivateBrowsing);
+    }
+    return;
+  }
+
 #ifdef MOZ_PLACES
   nsCOMPtr<mozIAsyncFavicons> favSvc =
     do_GetService("@mozilla.org/browser/favicon-service;1");
   if (favSvc) {
     nsCOMPtr<nsIFaviconDataCallback> callback =
-      new nsCopyFaviconCallback(aNewURI, aInPrivateBrowsing);
+      new nsCopyFaviconCallback(favSvc, aNewURI, aInPrivateBrowsing);
     favSvc->GetFaviconURLForPage(aOldURI, callback);
   }
 #endif
 }
 
-} // anonymous namespace
-
 class InternalLoadEvent : public nsRunnable
 {
 public:
   InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI,
                     nsIURI* aReferrer, uint32_t aReferrerPolicy,
                     nsISupports* aOwner, uint32_t aFlags,
                     const char* aTypeHint, nsIInputStream* aPostData,
                     nsIInputStream* aHeadersData, uint32_t aLoadType,
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -272,16 +272,21 @@ public:
   void AddProfileTimelineMarker(const char* aName,
                                 TracingMetadata aMetaData);
   void AddProfileTimelineMarker(mozilla::UniquePtr<TimelineMarker>&& aMarker);
 
   // Global counter for how many docShells are currently recording profile
   // timeline markers
   static unsigned long gProfileTimelineRecordingsCount;
 
+  // Tell the favicon service that aNewURI has the same favicon as aOldURI.
+  static void CopyFavicon(nsIURI* aOldURI,
+                          nsIURI* aNewURI,
+                          bool aInPrivateBrowsing);
+
 protected:
   // Object Management
   virtual ~nsDocShell();
   virtual void DestroyChildren() override;
 
   // Content Viewer Management
   nsresult EnsureContentViewer();
   // aPrincipal can be passed in if the caller wants.  If null is
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -149,16 +149,17 @@
 #include "SandboxHal.h"
 #include "ScreenManagerParent.h"
 #include "SourceSurfaceRawData.h"
 #include "StructuredCloneUtils.h"
 #include "TabParent.h"
 #include "URIUtils.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIDocShell.h"
+#include "nsDocShell.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "gfxPrefs.h"
 #include "prio.h"
 #include "private/pprio.h"
 #include "ContentProcessManager.h"
 #include "mozilla/psm/PSMContentListener.h"
 
 #include "nsIBidiKeyboard.h"
@@ -4565,16 +4566,34 @@ ContentParent::RecvNotifyKeywordSearchLo
             }
         }
     }
 #endif
     return true;
 }
 
 bool
+ContentParent::RecvCopyFavicon(const URIParams& aOldURI,
+                               const URIParams& aNewURI,
+                               const bool& aInPrivateBrowsing)
+{
+    nsCOMPtr<nsIURI> oldURI = DeserializeURI(aOldURI);
+    if (!oldURI) {
+        return true;
+    }
+    nsCOMPtr<nsIURI> newURI = DeserializeURI(aNewURI);
+    if (!newURI) {
+        return true;
+    }
+
+    nsDocShell::CopyFavicon(oldURI, newURI, aInPrivateBrowsing);
+    return true;
+}
+
+bool
 ContentParent::ShouldContinueFromReplyTimeout()
 {
     nsRefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
     return !monitor || !monitor->ShouldTimeOutCPOWs();
 }
 
 bool
 ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -788,16 +788,20 @@ private:
     virtual bool RecvKeywordToURI(const nsCString& aKeyword,
                                   nsString* aProviderName,
                                   OptionalInputStreamParams* aPostData,
                                   OptionalURIParams* aURI) override;
 
     virtual bool RecvNotifyKeywordSearchLoading(const nsString &aProvider,
                                                 const nsString &aKeyword) override;
 
+    virtual bool RecvCopyFavicon(const URIParams& aOldURI,
+                                 const URIParams& aNewURI,
+                                 const bool& aInPrivateBrowsing) override;
+
     virtual void ProcessingError(Result aCode, const char* aMsgName) override;
 
     virtual bool RecvAllocateLayerTreeId(uint64_t* aId) override;
     virtual bool RecvDeallocateLayerTreeId(const uint64_t& aId) override;
 
     virtual bool RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
                                               int32_t* aStatus,
                                               bool* aSuccess) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -893,16 +893,18 @@ parent:
     async CreateFakeVolume(nsString fsName, nsString mountPoint);
     async SetFakeVolumeState(nsString fsName, int32_t fsState);
 
     sync KeywordToURI(nsCString keyword)
         returns (nsString providerName, OptionalInputStreamParams postData, OptionalURIParams uri);
 
     sync NotifyKeywordSearchLoading(nsString providerName, nsString keyword);
 
+    async CopyFavicon(URIParams oldURI, URIParams newURI, bool isPrivate);
+
     // Tell the compositor to allocate a layer tree id for nested remote mozbrowsers.
     sync AllocateLayerTreeId()
         returns (uint64_t id);
     async DeallocateLayerTreeId(uint64_t id);
 
     sync SpeakerManagerForceSpeaker(bool aEnable);
 
     sync SpeakerManagerGetSpeakerStatus()
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -144,18 +144,16 @@
 <!ENTITY pref_search_restore_defaults_summary "Restore defaults">
 <!-- Localization note (pref_search_hint) : "TIP" as in "hint", "clue" etc. Displayed as an
      advisory message on the customise search providers settings page explaining how to add new
      search providers.
      The &formatI; in the string will be replaced by a small image of the icon described, and can be moved to wherever
      it is applicable. -->
 <!ENTITY pref_search_hint "TIP: Add any website to your list of search providers by long-pressing on its search field and then tapping the &formatI; icon.">
 <!ENTITY pref_category_devtools "Developer tools">
-<!ENTITY pref_category_devtools_summary "Remote debugging, paint flashing">
-<!ENTITY pref_developer_paint_flashing "Paint flashing">
 <!ENTITY pref_developer_remotedebugging "Remote debugging">
 <!ENTITY pref_category_logins "Logins">
 <!ENTITY pref_remember_signons "Remember passwords">
 <!ENTITY pref_manage_logins "Manage logins">
 
 <!ENTITY pref_category_home "Home">
 <!ENTITY pref_category_home_summary "Customize your homepage">
 <!ENTITY pref_category_home_panels "Panels">
--- a/mobile/android/base/resources/xml-v11/preferences.xml
+++ b/mobile/android/base/resources/xml-v11/preferences.xml
@@ -49,15 +49,15 @@
     <PreferenceScreen android:title="@string/pref_category_vendor"
                       android:summary="@string/pref_category_vendor_summary"
                       android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
         <extra android:name="resource"
                android:value="preferences_vendor"/>
     </PreferenceScreen>
 
     <PreferenceScreen android:title="@string/pref_category_devtools"
-                      android:summary="@string/pref_category_devtools_summary"
+                      android:summary="@string/pref_developer_remotedebugging"
                       android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
         <extra android:name="resource"
                android:value="preferences_devtools"/>
     </PreferenceScreen>
 
 </PreferenceScreen>
--- a/mobile/android/base/resources/xml/preferences.xml
+++ b/mobile/android/base/resources/xml/preferences.xml
@@ -67,17 +67,17 @@
                 android:targetPackage="@string/android_package_name"
                 android:targetClass="org.mozilla.gecko.preferences.GeckoPreferences" >
             <extra
                 android:name="resource"
                 android:value="preferences_vendor" />
         </intent>
     </PreferenceScreen>
     <PreferenceScreen android:title="@string/pref_category_devtools"
-                      android:summary="@string/pref_category_devtools_summary" >
+                      android:summary="@string/pref_developer_remotedebugging" >
         <intent android:action="android.intent.action.VIEW"
                 android:targetPackage="@string/android_package_name"
                 android:targetClass="org.mozilla.gecko.preferences.GeckoPreferences" >
             <extra
                 android:name="resource"
                 android:value="preferences_devtools" />
         </intent>
     </PreferenceScreen>
--- a/mobile/android/base/resources/xml/preferences_devtools.xml
+++ b/mobile/android/base/resources/xml/preferences_devtools.xml
@@ -3,20 +3,16 @@
    - 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/. -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                   xmlns:gecko="http://schemas.android.com/apk/res-auto"
                   android:title="@string/pref_category_devtools"
                   android:enabled="false">
 
-       <org.mozilla.gecko.preferences.CustomCheckBoxPreference
-                           android:key="nglayout.debug.paint_flashing"
-                           android:title="@string/pref_developer_paint_flashing" />
-
        <CheckBoxPreference android:key="devtools.debugger.remote-enabled"
                            android:title="@string/pref_developer_remotedebugging" />
 
        <org.mozilla.gecko.preferences.AlignRightLinkPreference android:key="android.not_a_preference.remote_debugging.link"
                                                                android:title="@string/pref_learn_more"
                                                                android:persistent="false"
                                                                url="https://developer.mozilla.org/docs/Tools/Remote_Debugging/Debugging_Firefox_for_Android_with_WebIDE" />
 
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -145,18 +145,16 @@
   <string name="pref_search_hint">&pref_search_hint;</string>
 
   <string name="pref_category_language">&pref_category_language;</string>
   <string name="pref_category_language_summary">&pref_category_language_summary;</string>
   <string name="pref_browser_locale">&pref_browser_locale;</string>
   <string name="locale_system_default">&locale_system_default;</string>
 
   <string name="pref_category_devtools">&pref_category_devtools;</string>
-  <string name="pref_category_devtools_summary">&pref_category_devtools_summary;</string>
-  <string name="pref_developer_paint_flashing">&pref_developer_paint_flashing;</string>
   <string name="pref_developer_remotedebugging">&pref_developer_remotedebugging;</string>
 
   <string name="pref_category_home">&pref_category_home;</string>
   <string name="pref_category_home_summary">&pref_category_home_summary;</string>
   <string name="pref_category_home_panels">&pref_category_home_panels;</string>
   <string name="home_add_panel_title">&home_add_panel_title;</string>
   <string name="home_add_panel_empty">&home_add_panel_empty;</string>
   <string name="home_add_panel_installed">&home_add_panel_installed;</string>
--- a/mobile/android/base/tests/StringHelper.java
+++ b/mobile/android/base/tests/StringHelper.java
@@ -154,17 +154,16 @@ public class StringHelper {
     public final String ABOUT_LABEL = "About " + BRAND_NAME ;
     public final String FAQS_LABEL;
     public final String FEEDBACK_LABEL;
     public final String LOCATION_SERVICES_LABEL = "Mozilla Location Service";
     public final String HEALTH_REPORT_LABEL = BRAND_NAME + " Health Report";
     public final String MY_HEALTH_REPORT_LABEL;
 
     // Developer tools
-    public final String PAINT_FLASHING_LABEL;
     public final String REMOTE_DEBUGGING_LABEL;
     public final String LEARN_MORE_LABEL;
 
     // Labels for the about:home tabs
     public final String HISTORY_LABEL;
     public final String TOP_SITES_LABEL;
     public final String BOOKMARKS_LABEL;
     public final String READING_LIST_LABEL;
@@ -331,17 +330,16 @@ public class StringHelper {
         CLEAR_PRIVATE_DATA_LABEL = res.getString(R.string.pref_clear_private_data);
 
         // Mozilla
         FAQS_LABEL = res.getString(R.string.pref_vendor_faqs);
         FEEDBACK_LABEL = res.getString(R.string.pref_vendor_feedback);
         MY_HEALTH_REPORT_LABEL = res.getString(R.string.datareporting_abouthr_title);
 
         // Developer tools
-        PAINT_FLASHING_LABEL = res.getString(R.string.pref_developer_paint_flashing);
         REMOTE_DEBUGGING_LABEL = res.getString(R.string.pref_developer_remotedebugging);
         LEARN_MORE_LABEL = res.getString(R.string.pref_learn_more);
 
         // Labels for the about:home tabs
         HISTORY_LABEL = res.getString(R.string.home_history_title);
         TOP_SITES_LABEL = res.getString(R.string.home_top_sites_title);
         BOOKMARKS_LABEL = res.getString(R.string.bookmarks_title);
         READING_LIST_LABEL = res.getString(R.string.reading_list_title);
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -77,18 +77,16 @@ skip-if = android_version == "18"
 [testPictureLinkContextMenu]
 # disabled on 4.3, bug 1031496
 skip-if = android_version == "18"
 [testPrefsObserver]
 [testPrivateBrowsing]
 [testPromptGridInput]
 # bug 1001657 for 2.3 and 4.3
 skip-if = android_version == "10" || android_version == "18"
-# [testReaderMode] # see bug 913254, 936224
-[testReadingListCache]
 [testReadingListProvider]
 [testSearchHistoryProvider]
 [testSearchSuggestions]
 # disabled on 2.3, bug 907768; on 4.3, bug 1145867
 skip-if = android_version == "10" || android_version == "18"
 [testSessionOOMSave]
 # disabled on 2.3, bug 945395; on 4.3, bug 1144888
 skip-if = android_version == "10" || android_version == "18"
@@ -117,22 +115,25 @@ skip-if = android_version == "18"
 [testDebuggerServer]
 [testDeviceSearchEngine]
 [testFilePicker]
 [testHistoryService]
 # disabled on 4.3, bug 1116036
 skip-if = android_version == "18"
 [testJNI]
 # [testMozPay] # see bug 945675
+[testMigrateUI]
 [testNetworkManager]
 [testOfflinePage]
 [testOrderedBroadcast]
 [testOSLocale]
 # disabled on 2.3 and 4.3: Bug 1124494
 skip-if = android_version == "10" || android_version == "18"
+[testReaderView]
+[testReadingListCache]
 [testResourceSubstitutions]
 [testRestrictedProfiles]
 [testSessionFormData]
 [testSharedPreferences]
 [testSimpleDiscovery]
 [testTrackingProtection]
 # disabled on 4.3, bug 1158363
 skip-if = android_version == "18"
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testMigrateUI.java
@@ -0,0 +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/. */
+
+package org.mozilla.gecko.tests;
+
+public class testMigrateUI extends JavascriptTest {
+    public testMigrateUI() {
+        super("testMigrateUI.js");
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testMigrateUI.js
@@ -0,0 +1,22 @@
+// -*- 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");
+
+add_task(function* test_migrateUI() {
+  Services.prefs.clearUserPref("browser.migration.version");
+  Services.prefs.setBoolPref("nglayout.debug.paint_flashing", true);
+
+  let BrowserApp = Services.wm.getMostRecentWindow("navigator:browser").BrowserApp;
+  BrowserApp._migrateUI();
+
+  // Check that migration version increased.
+  do_check_eq(Services.prefs.getIntPref("browser.migration.version"), 1);
+
+  // Check that user pref value was reset.
+  do_check_eq(Services.prefs.prefHasUserValue("nglayout.debug.paint_flashing"), false);
+});
+
+run_next_test();
deleted file mode 100644
--- a/mobile/android/base/tests/testReaderMode.java
+++ /dev/null
@@ -1,203 +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/. */
-
-package org.mozilla.gecko.tests;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.Actions;
-import org.mozilla.gecko.home.HomePager;
-import org.mozilla.gecko.R;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ListView;
-
-import com.jayway.android.robotium.solo.Condition;
-import com.jayway.android.robotium.solo.Solo;
-
-/**
- * This patch tests the Reader Mode feature by adding and removing items in reading list
- * checks the reader toolbar functionality(share, add/remove to reading list, go to reading list)
- * accessing a page from reading list menu, checks that the reader icon is associated in History tab
- * and that the reading list is properly populated after adding or removing reader items
- */
-public class testReaderMode extends AboutHomeTest {
-    static final int EVENT_CLEAR_DELAY_MS = 3000;
-    static final int READER_ICON_MAX_WAIT_MS = 15000;
-
-    public void testReaderMode() {
-        blockForGeckoReady();
-
-        Actions.EventExpecter contentEventExpecter;
-        Actions.EventExpecter contentReaderAddedExpecter;
-        Actions.EventExpecter faviconExpecter;
-        Actions.EventExpecter contentPageShowExpecter;
-        Actions.RepeatedEventExpecter paintExpecter;
-        ListView list;
-        View child;
-        View readerIcon;
-        String textUrl = getAbsoluteUrl(mStringHelper.ROBOCOP_TEXT_PAGE_URL);
-        String devType = mDevice.type;
-        int childNo;
-        int height;
-        int width;
-
-        loadAndPaint(textUrl);
-
-        // Add the page to the Reading List using long click on the reader icon
-        readerIcon = getReaderIcon();
-        contentReaderAddedExpecter = mActions.expectGeckoEvent("Reader:Added");
-        mSolo.clickLongOnView(readerIcon);
-        String eventData = contentReaderAddedExpecter.blockForEventData();
-        isAdded(eventData);
-        contentReaderAddedExpecter.unregisterListener();
-
-        // Try to add the page to the Reading List using long click on the reader icon a second time
-        readerIcon = getReaderIcon();
-        contentReaderAddedExpecter = mActions.expectGeckoEvent("Reader:Added");
-        mSolo.clickLongOnView(readerIcon);
-        eventData = contentReaderAddedExpecter.blockForEventData();
-        isAdded(eventData);
-        contentReaderAddedExpecter.unregisterListener();
-
-        // Waiting for the favicon since is the last element loaded usually
-        faviconExpecter = mActions.expectGeckoEvent("Reader:FaviconRequest");
-        contentPageShowExpecter = mActions.expectGeckoEvent("Content:PageShow");
-        readerIcon = getReaderIcon();
-        paintExpecter = mActions.expectPaint();
-        mSolo.clickOnView(readerIcon);
-
-        // Changing devices orientation to be sure that all devices are in portrait when will access the reader toolbar
-        mSolo.setActivityOrientation(Solo.PORTRAIT);
-        faviconExpecter.blockForEvent();
-        faviconExpecter.unregisterListener();
-        contentPageShowExpecter.blockForEvent();
-        contentPageShowExpecter.unregisterListener();
-        paintExpecter.blockUntilClear(EVENT_CLEAR_DELAY_MS);
-        paintExpecter.unregisterListener();
-        verifyUrlBarTitle(mStringHelper.ROBOCOP_TEXT_PAGE_URL);
-
-        // Open the share menu for the reader toolbar
-        height = mDriver.getGeckoTop() + mDriver.getGeckoHeight() - 10;
-        width = mDriver.getGeckoLeft() + mDriver.getGeckoWidth() - 10;
-        mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height));
-        mSolo.clickOnScreen(width,height);
-        mAsserter.ok(mSolo.waitForText("Share via"), "Waiting for the share menu", "The share menu is present");
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK); // Close the share menu
-
-        // Remove page from the Reading List using reader toolbar
-        height = mDriver.getGeckoTop() + mDriver.getGeckoHeight() - 10;
-        width = mDriver.getGeckoLeft() + 50;
-        mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height));
-        mSolo.clickOnScreen(width,height);
-        mAsserter.ok(mSolo.waitForText("Page removed from your Reading List"), "Waiting for the page to removed from your Reading List", "The page is removed from your Reading List");
-
-        //Add page to the Reading List using reader toolbar
-        mSolo.clickOnScreen(width,height);
-        mAsserter.ok(mSolo.waitForText("Page added to your Reading List"), "Waiting for the page to be added to your Reading List", "The page was added to your Reading List");
-
-        // Open the Reading List menu for the toolbar
-        height = mDriver.getGeckoTop() + mDriver.getGeckoHeight() - 10;
-        width = mDriver.getGeckoLeft() + mDriver.getGeckoWidth()/2 - 10;
-        mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height));
-        contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
-        mSolo.clickOnScreen(width,height);
-        contentEventExpecter.blockForEvent();
-        contentEventExpecter.unregisterListener();
-
-        // Check if the page is present in the Reading List
-        mAsserter.ok(mSolo.waitForText(mStringHelper.ROBOCOP_TEXT_PAGE_TITLE),
-                "Verify if the page is added to your Reading List",
-                mStringHelper.ROBOCOP_TEXT_PAGE_TITLE);
-
-        // Check if the page is added in History tab like a Reading List item
-        openAboutHomeTab(AboutHomeTabs.HISTORY);
-        list = findListViewWithTag(HomePager.LIST_TAG_HISTORY);
-        child = list.getChildAt(1);
-        mAsserter.ok(child != null, "item can be retrieved", child != null ? child.toString() : "null!");
-        mSolo.clickLongOnView(child);
-        mAsserter.ok(mSolo.waitForText("Open in Reader"), "Verify if the page is present in history as a Reading List item", "The page is present in history as a Reading List item");
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK); // Dismiss the context menu
-        mSolo.waitForText(mStringHelper.ROBOCOP_TEXT_PAGE_TITLE);
-
-        // Verify separately the Reading List entries for tablets and phone because for tablets there is an extra child in UI design
-        if (devType.equals("phone")) {
-            childNo = 1;
-        }
-        else {
-            childNo = 2;
-        }
-        // Verify if the page is present to your Reading List
-        openAboutHomeTab(AboutHomeTabs.READING_LIST);
-        list = findListViewWithTag(HomePager.LIST_TAG_READING_LIST);
-        child = list.getChildAt(childNo-1);
-        mAsserter.ok(child != null, "Verify if the page is present to your Reading List", "The page is present in your Reading List");
-        contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
-        mSolo.clickOnView(child);
-        contentEventExpecter.blockForEvent();
-        contentEventExpecter.unregisterListener();
-        verifyUrlBarTitle(mStringHelper.ROBOCOP_TEXT_PAGE_URL);
-
-        // Verify that we are in reader mode and remove the page from Reading List
-        height = mDriver.getGeckoTop() + mDriver.getGeckoHeight() - 10;
-        width = mDriver.getGeckoLeft() + 50;
-        mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height));
-        mSolo.clickOnScreen(width,height);
-        mAsserter.ok(mSolo.waitForText("Page removed from your Reading List"), "Waiting for the page to removed from your Reading List", "The page is removed from your Reading List");
-        verifyUrlBarTitle(mStringHelper.ROBOCOP_TEXT_PAGE_URL);
-
-        //Check if the Reading List is empty
-        openAboutHomeTab(AboutHomeTabs.READING_LIST);
-        list = findListViewWithTag(HomePager.LIST_TAG_READING_LIST);
-        child = list.getChildAt(childNo-1);
-        mAsserter.ok(child == null, "Verify if the Reading List is empty", "The Reading List is empty");
-    }
-
-    // Get the reader icon method
-    protected View getReaderIcon() {
-        View pageActionLayout = mSolo.getView(R.id.page_action_layout);
-        final ViewGroup actionLayoutEntry = (ViewGroup)pageActionLayout;
-        View icon = actionLayoutEntry.getChildAt(1);
-        if (icon == null || icon.getVisibility() != View.VISIBLE) {
-            // wait for the view to be visible, otherwise it may not respond
-            // to clicks -- see bug 927578
-            mAsserter.dumpLog("reader icon not visible -- waiting for visibility");
-            Condition visibilityCondition = new Condition() {
-                @Override
-                public boolean isSatisfied() {
-                    View conditionIcon = actionLayoutEntry.getChildAt(1);
-                    if (conditionIcon == null ||
-                        conditionIcon.getVisibility() != View.VISIBLE)
-                        return false;
-                    return true;
-                }
-            };
-            waitForCondition(visibilityCondition, READER_ICON_MAX_WAIT_MS);
-            icon = actionLayoutEntry.getChildAt(1);
-            mAsserter.ok(icon != null, "checking reader icon view", "reader icon view not null");
-            mAsserter.ok(icon.getVisibility() == View.VISIBLE, "checking reader icon visible", "reader icon visible");
-        }
-        return icon;
-    }
-
-    // This method check to see if a reader item is added to the reader list
-    private boolean isAdded(String eventData) {
-        try {
-            JSONObject data = new JSONObject(eventData);
-                if (data.getInt("result") == 0) {
-                    mAsserter.ok(true, "Waiting for the page to be added to your Reading List", "The page was added to your Reading List");
-                }
-                else {
-                    if (data.getInt("result") == 2) {
-                        mAsserter.ok(true, "Trying to add a second time the page in your Reading List", "The page is already in your Reading List");
-                    }
-                }
-        } catch (JSONException e) {
-            mAsserter.ok(false, "Error parsing the event data", e.toString());
-            return false;
-        }
-        return true;
-    }
-}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testReaderView.java
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.tests;
+
+
+public class testReaderView extends JavascriptTest {
+    public testReaderView() {
+        super("testReaderView.js");
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testReaderView.js
@@ -0,0 +1,67 @@
+// -*- 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/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function promiseBrowserEvent(browser, eventType) {
+  return new Promise((resolve) => {
+    function handle(event) {
+      // Since we'll be redirecting, don't make assumptions about the given URL and the loaded URL
+      if (event.target != browser.contentDocument || event.target.location.href == "about:blank") {
+        do_print("Skipping spurious '" + eventType + "' event" + " for " + event.target.location.href);
+        return;
+      }
+      do_print("Received event " + eventType + " from browser");
+      browser.removeEventListener(eventType, handle, true);
+      resolve(event);
+    }
+
+    browser.addEventListener(eventType, handle, true);
+    do_print("Now waiting for " + eventType + " event from browser");
+  });
+}
+
+function promiseWaitForCondition(win, condition) {
+  return new Promise((resolve, reject) => {
+    var tries = 0;
+    var interval = win.setInterval(function() {
+      if (tries >= 30) {
+        do_print("Condition didn't pass. Moving on.");
+        moveOn();
+      }
+      if (condition()) {
+        moveOn();
+      }
+      tries++;
+    }, 200);
+    var moveOn = function() { win.clearInterval(interval); resolve(); };
+  });
+}
+
+add_task(function* test_reader_view_visibility() {
+  let gWin = Services.wm.getMostRecentWindow("navigator:browser");
+  let BrowserApp = gWin.BrowserApp;
+
+  let url = "http://mochi.test:8888/tests/robocop/reader_mode_pages/basic_article.html";
+  let browser = BrowserApp.addTab("about:reader?url=" + url).browser;
+
+  yield promiseBrowserEvent(browser, "load");
+
+  do_register_cleanup(function cleanup() {
+    BrowserApp.closeTab(BrowserApp.getTabForBrowser(browser));
+  });
+
+  let doc = browser.contentDocument;
+  let title = doc.getElementById("reader-title");
+
+  // We need to wait for reader content to appear because AboutReader.jsm
+  // asynchronously fetches the content after about:reader loads.
+  yield promiseWaitForCondition(gWin, () => title.textContent);
+  do_check_eq(title.textContent, "Article title");
+});
+
+run_next_test();
--- a/mobile/android/base/tests/testSettingsMenuItems.java
+++ b/mobile/android/base/tests/testSettingsMenuItems.java
@@ -113,17 +113,16 @@ public class testSettingsMenuItems exten
                 { mStringHelper.FEEDBACK_LABEL },
                 { "Data choices" },
                 { mStringHelper.HEALTH_REPORT_LABEL, "Shares data with Mozilla about your browser health and helps you understand your browser performance" },
                 { mStringHelper.MY_HEALTH_REPORT_LABEL },
         };
 
         PATH_DEVELOPER = new String[] { mStringHelper.DEVELOPER_TOOLS_SECTION_LABEL };
         OPTIONS_DEVELOPER = new String[][] {
-                { mStringHelper.PAINT_FLASHING_LABEL },
                 { mStringHelper.REMOTE_DEBUGGING_LABEL },
                 { mStringHelper.LEARN_MORE_LABEL },
         };
 
         settingsMap.put(PATH_CUSTOMIZE, new ArrayList<String[]>(Arrays.asList(OPTIONS_CUSTOMIZE)));
         settingsMap.put(PATH_HOME, new ArrayList<String[]>(Arrays.asList(OPTIONS_HOME)));
         settingsMap.put(PATH_DISPLAY, new ArrayList<String[]>(Arrays.asList(OPTIONS_DISPLAY)));
         settingsMap.put(PATH_PRIVACY, new ArrayList<String[]>(Arrays.asList(OPTIONS_PRIVACY)));
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -907,47 +907,70 @@ var BrowserApp = {
       });
   },
 
   onAppUpdated: function() {
     // initialize the form history and passwords databases on upgrades
     Services.obs.notifyObservers(null, "FormHistory:Init", "");
     Services.obs.notifyObservers(null, "Passwords:Init", "");
 
-    // Migrate user-set "plugins.click_to_play" pref. See bug 884694.
-    // Because the default value is true, a user-set pref means that the pref was set to false.
-    if (Services.prefs.prefHasUserValue("plugins.click_to_play")) {
-      Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_ENABLED);
-      Services.prefs.clearUserPref("plugins.click_to_play");
-    }
-
-    // Migrate the "privacy.donottrackheader.value" pref. See bug 1042135.
-    if (Services.prefs.prefHasUserValue("privacy.donottrackheader.value")) {
-      // Make sure the doNotTrack value conforms to the conversion from
-      // three-state to two-state. (This reverts a setting of "please track me"
-      // to the default "don't say anything").
-      if (Services.prefs.getBoolPref("privacy.donottrackheader.enabled") &&
-          (Services.prefs.getIntPref("privacy.donottrackheader.value") != 1)) {
-        Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
-      }
-
-      // This pref has been removed, so always clear it.
-      Services.prefs.clearUserPref("privacy.donottrackheader.value");
-    }
-
-    // Set the search activity default pref on app upgrade if it has not been set already.
-    if (this._startupStatus === "upgrade" &&
-        !Services.prefs.prefHasUserValue("searchActivity.default.migrated")) {
-      Services.prefs.setBoolPref("searchActivity.default.migrated", true);
-      SearchEngines.migrateSearchActivityDefaultPref();
-    }
-
     if (this._startupStatus === "upgrade") {
+      this._migrateUI();
+    }
+  },
+
+  _migrateUI: function() {
+    const UI_VERSION = 1;
+    let currentUIVersion = 0;
+    try {
+      currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
+    } catch(ex) {}
+    if (currentUIVersion >= UI_VERSION) {
+      return;
+    }
+
+    if (currentUIVersion < 1) {
+      // Migrate user-set "plugins.click_to_play" pref. See bug 884694.
+      // Because the default value is true, a user-set pref means that the pref was set to false.
+      if (Services.prefs.prefHasUserValue("plugins.click_to_play")) {
+        Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_ENABLED);
+        Services.prefs.clearUserPref("plugins.click_to_play");
+      }
+
+      // Migrate the "privacy.donottrackheader.value" pref. See bug 1042135.
+      if (Services.prefs.prefHasUserValue("privacy.donottrackheader.value")) {
+        // Make sure the doNotTrack value conforms to the conversion from
+        // three-state to two-state. (This reverts a setting of "please track me"
+        // to the default "don't say anything").
+        if (Services.prefs.getBoolPref("privacy.donottrackheader.enabled") &&
+            (Services.prefs.getIntPref("privacy.donottrackheader.value") != 1)) {
+          Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
+        }
+
+        // This pref has been removed, so always clear it.
+        Services.prefs.clearUserPref("privacy.donottrackheader.value");
+      }
+
+      // Set the search activity default pref on app upgrade if it has not been set already.
+      if (!Services.prefs.prefHasUserValue("searchActivity.default.migrated")) {
+        Services.prefs.setBoolPref("searchActivity.default.migrated", true);
+        SearchEngines.migrateSearchActivityDefaultPref();
+      }
+
       Reader.migrateCache().catch(e => Cu.reportError("Error migrating Reader cache: " + e));
-    }
+
+      // We removed this pref from user visible settings, so we should reset it.
+      // Power users can go into about:config to re-enable this if they choose.
+      if (Services.prefs.prefHasUserValue("nglayout.debug.paint_flashing")) {
+        Services.prefs.clearUserPref("nglayout.debug.paint_flashing");
+      }
+    }
+
+    // Update the migration version.
+    Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
   },
 
   // This function returns false during periods where the browser displayed document is
   // different from the browser content document, so user actions and some kinds of viewport
   // updates should be ignored. This period starts when we start loading a new page or
   // switch tabs, and ends when the new browser content document has been drawn and handed
   // off to the compositor.
   isBrowserContentDocumentDisplayed: function() {
--- a/mobile/android/search/java/org/mozilla/search/autocomplete/SearchBar.java
+++ b/mobile/android/search/java/org/mozilla/search/autocomplete/SearchBar.java
@@ -79,17 +79,18 @@ public class SearchBar extends FrameLayo
                 updateClearButtonVisibility();
             }
         });
 
         // Attach a listener for the "search" key on the keyboard.
         editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
             @Override
             public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-                if (listener != null && actionId == EditorInfo.IME_ACTION_SEARCH) {
+                if (listener != null &&
+                    (actionId == EditorInfo.IME_ACTION_UNSPECIFIED || actionId == EditorInfo.IME_ACTION_SEARCH)) {
                     // The user searched without using search engine suggestions.
                     Telemetry.sendUIEvent(TelemetryContract.Event.SEARCH, TelemetryContract.Method.ACTIONBAR, "text");
                     listener.onSubmit(v.getText().toString());
                     return true;
                 }
                 return false;
             }
         });
--- a/storage/src/mozStoragePrivateHelpers.cpp
+++ b/storage/src/mozStoragePrivateHelpers.cpp
@@ -91,32 +91,28 @@ checkAndLogStatementPerformance(sqlite3_
     return;
 
   const char *sql = ::sqlite3_sql(aStatement);
 
   // Check to see if this is marked to not warn
   if (::strstr(sql, "/* do not warn (bug "))
     return;
 
-  nsAutoCString message;
+  nsAutoCString message("Suboptimal indexes for the SQL statement ");
+#ifdef MOZ_STORAGE_SORTWARNING_SQL_DUMP
+  message.Append('`');
+  message.Append(sql);
+  message.AppendLiteral("` [");
   message.AppendInt(count);
-  if (count == 1)
-    message.AppendLiteral(" sort operation has ");
-  else
-    message.AppendLiteral(" sort operations have ");
-  message.AppendLiteral("occurred for the SQL statement '");
-#ifdef MOZ_STORAGE_SORTWARNING_SQL_DUMP
-  message.AppendLiteral("SQL command: ");
-  message.Append(sql);
+  message.AppendLiteral(" sort operation(s)]");
 #else
   nsPrintfCString address("0x%p", aStatement);
   message.Append(address);
 #endif
-  message.Append("'.  See https://developer.mozilla.org/En/Storage/Warnings "
-                 "details.");
+  message.AppendLiteral(" (http://mzl.la/1FuID0j).");
   NS_WARNING(message.get());
 }
 
 nsIVariant *
 convertJSValToVariant(
   JSContext *aCtx,
   JS::Value aValue)
 {
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -1,9 +1,8 @@
-/* vim: set ts=2 sts=2 sw=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 
@@ -403,16 +402,25 @@ var LoginManagerParent = {
       prompterSvc.init(target.isRemoteBrowser ?
                           target.ownerDocument.defaultView :
                           target.contentWindow);
       if (target.isRemoteBrowser)
         prompterSvc.setE10sData(target, opener);
       return prompterSvc;
     }
 
+    function recordLoginUse(login) {
+      // Update the lastUsed timestamp and increment the use count.
+      let propBag = Cc["@mozilla.org/hash-property-bag;1"].
+                    createInstance(Ci.nsIWritablePropertyBag);
+      propBag.setProperty("timeLastUsed", Date.now());
+      propBag.setProperty("timesUsedIncrement", 1);
+      Services.logins.modifyLogin(login, propBag);
+    }
+
     if (!Services.logins.getLoginSavingEnabled(hostname)) {
       log("(form submission ignored -- saving is disabled for:", hostname, ")");
       return;
     }
 
     var formLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
                     createInstance(Ci.nsILoginInfo);
     formLogin.init(hostname, formSubmitURL, null,
@@ -426,16 +434,24 @@ var LoginManagerParent = {
     // If we didn't find a username field, but seem to be changing a
     // password, allow the user to select from a list of applicable
     // logins to update the password for.
     if (!usernameField && oldPasswordField && logins.length > 0) {
       var prompter = getPrompter();
 
       if (logins.length == 1) {
         var oldLogin = logins[0];
+
+        if (oldLogin.password == formLogin.password) {
+          recordLoginUse(oldLogin);
+          log("(Not prompting to save/change since we have no username and the " +
+              "only saved password matches the new password)");
+          return;
+        }
+
         formLogin.username      = oldLogin.username;
         formLogin.usernameField = oldLogin.usernameField;
 
         prompter.promptToChangePassword(oldLogin, formLogin);
       } else {
         prompter.promptToChangePasswordWithUsernames(
                             logins, logins.length, formLogin);
       }
@@ -476,22 +492,17 @@ var LoginManagerParent = {
       log("Found an existing login matching this form submission");
 
       // Change password if needed.
       if (existingLogin.password != formLogin.password) {
         log("...passwords differ, prompting to change.");
         prompter = getPrompter();
         prompter.promptToChangePassword(existingLogin, formLogin);
       } else {
-        // Update the lastUsed timestamp.
-        var propBag = Cc["@mozilla.org/hash-property-bag;1"].
-                      createInstance(Ci.nsIWritablePropertyBag);
-        propBag.setProperty("timeLastUsed", Date.now());
-        propBag.setProperty("timesUsedIncrement", 1);
-        Services.logins.modifyLogin(existingLogin, propBag);
+        recordLoginUse(existingLogin);
       }
 
       return;
     }
 
 
     // Prompt user to save login (via dialog or notification bar)
     prompter = getPrompter();
--- a/toolkit/components/passwordmgr/test/subtst_notifications_2pw_0un.html
+++ b/toolkit/components/passwordmgr/test/subtst_notifications_2pw_0un.html
@@ -7,17 +7,17 @@
 <form id="form" action="formsubmit.sjs">
   <input id="pass1" name="pass1" type="password" value="staticpw">
   <input id="pass" name="pass" type="password">
   <button type="submit">Submit</button>
 </form>
 
 <script>
 function submitForm() {
-  pass.value = "notifyp2";
+  pass.value = "notifyp1";
   form.submit();
 }
 
 window.onload = submitForm;
 var form      = document.getElementById("form");
 var pass      = document.getElementById("pass");
 
 </script>
--- a/toolkit/components/passwordmgr/test/test_notifications.html
+++ b/toolkit/components/passwordmgr/test/test_notifications.html
@@ -46,16 +46,18 @@ var subtests = [
                    "subtst_notifications_9.html", // 19
                    "subtst_notifications_10.html",  // 20
                    "http://test1.example.org:80" + testpath + "subtst_notifications_1.html", // 21
                    "http://test1.example.org:80" + testpath + "subtst_notifications_7.html", // 22
                    "http://test1.example.org:80" + testpath + "subtst_notifications_6.html", // 23
                    "subtst_notifications_2pw_0un.html",  // 24
                    "subtst_notifications_2pw_0un.html",  // 25
                    "subtst_notifications_2pw_0un.html",  // 26
+                   "subtst_notifications_2pw_0un.html",  // 27
+                   "subtst_notifications_2pw_0un.html",  // 28
                ];
 
 
 var ignoreLoad = false;
 function handleLoad(aEvent) {
     // ignore every other load event ... We get one for loading the subtest (which
     // we want to ignore), and another when the subtest's form submits itself
     // (which we want to handle, to start the next test).
@@ -142,17 +144,17 @@ function checkTest() {
       case 5:
         // Same subtest, make sure we didn't prompt for an existing login.
         is(gotUser, "notifyu1", "Checking submitted username");
         is(gotPass, "notifyp1", "Checking submitted password");
         popup = getPopup(popupNotifications, "password-save");
         ok(!popup, "checking for no notification popup");
 
         // Check to make sure we updated the timestamps and use count on the
-        // existing loging that was submitted for this form.
+        // existing login that was submitted for this form.
         var logins = pwmgr.getAllLogins();
         is(logins.length, 1, "Should only have 1 login");
         ok(SpecialPowers.call_Instanceof(logins[0], Ci.nsILoginMetaInfo), "metainfo QI");
         is(logins[0].timesUsed, 2, "check .timesUsed for existing login submission");
         ok(logins[0].timeLastUsed > logins[0].timeCreated, "timeLastUsed bumped");
         ok(logins[0].timeCreated == logins[0].timePasswordChanged, "timeChanged not updated");
 
         // remove that login
@@ -380,50 +382,99 @@ function checkTest() {
         ok(expectedText.test(notificationText), "Checking text: " + notificationText);
         popup.remove();
         break;
 
       case 24:
         // Check for notification popup when a form with 2 password fields (no username) is
         // submitted and there are no saved logins.
         is(gotUser, "null", "Checking submitted username");
-        is(gotPass, "notifyp2", "Checking submitted password");
+        is(gotPass, "notifyp1", "Checking submitted password");
         popup = getPopup(popupNotifications, "password-save");
         ok(popup, "got notification popup");
         popup.remove();
 
         // Add login for the next test
-        pwmgr.addLogin(login1);
+        pwmgr.addLogin(login1B);
         break;
 
       case 25:
         // Check for notification popup when a form with 2 password fields (no username) is
         // submitted and there is a saved login with a username and different password.
         is(gotUser, "null", "Checking submitted username");
-        is(gotPass, "notifyp2", "Checking submitted password");
+        is(gotPass, "notifyp1", "Checking submitted password");
         popup = getPopup(popupNotifications, "password-change");
         ok(popup, "got notification popup");
         popup.remove();
         // remove that login
-        pwmgr.removeLogin(login1);
+        pwmgr.removeLogin(login1B);
 
         // Add login for the next test
         pwmgr.addLogin(login2B);
         break;
 
       case 26:
         // Check for notification popup when a form with 2 password fields (no username) is
         // submitted and there is a saved login with no username and a different password.
         is(gotUser, "null", "Checking submitted username");
-        is(gotPass, "notifyp2", "Checking submitted password");
+        is(gotPass, "notifyp1", "Checking submitted password");
         popup = getPopup(popupNotifications, "password-change");
         ok(popup, "got notification popup");
         popup.remove();
         // remove that login
         pwmgr.removeLogin(login2B);
+
+        // Add login for the next test
+        pwmgr.addLogin(login1);
+
+        break;
+
+      case 27:
+        // Check for no notification popup when a form with 2 password fields (no username) is
+        // submitted and there is a saved login with a username and the same password.
+        is(gotUser, "null", "Checking submitted username");
+        is(gotPass, "notifyp1", "Checking submitted password");
+        popup = getPopup(popupNotifications, "password-change");
+        ok(!popup, "checking for no notification popup");
+
+        // Check to make sure we updated the timestamps and use count on the
+        // existing login that was submitted for this form.
+        var logins = pwmgr.getAllLogins();
+        is(logins.length, 1, "Should only have 1 login");
+        ok(SpecialPowers.call_Instanceof(logins[0], Ci.nsILoginMetaInfo), "metainfo QI");
+        is(logins[0].timesUsed, 2, "check .timesUsed for existing login submission");
+        ok(logins[0].timeLastUsed > logins[0].timeCreated, "timeLastUsed bumped");
+        ok(logins[0].timeCreated == logins[0].timePasswordChanged, "timeChanged not updated");
+
+        // remove that login
+        pwmgr.removeLogin(login1);
+
+        // Add login for the next test
+        pwmgr.addLogin(login2);
+        break;
+
+      case 28:
+        // Check for no notification popup when a form with 2 password fields (no username) is
+        // submitted and there is a saved login with no username and the same password.
+        is(gotUser, "null", "Checking submitted username");
+        is(gotPass, "notifyp1", "Checking submitted password");
+        popup = getPopup(popupNotifications, "password-change");
+        ok(!popup, "checking for no notification popup");
+
+        // Check to make sure we updated the timestamps and use count on the
+        // existing login that was submitted for this form.
+        var logins = pwmgr.getAllLogins();
+        is(logins.length, 1, "Should only have 1 login");
+        ok(SpecialPowers.call_Instanceof(logins[0], Ci.nsILoginMetaInfo), "metainfo QI");
+        is(logins[0].timesUsed, 2, "check .timesUsed for existing login submission");
+        ok(logins[0].timeLastUsed > logins[0].timeCreated, "timeLastUsed bumped");
+        ok(logins[0].timeCreated == logins[0].timePasswordChanged, "timeChanged not updated");
+
+        // remove that login
+        pwmgr.removeLogin(login2);
         break;
 
       default:
         ok(false, "Unexpected call to checkTest for test #" + testNum);
 
     }
 
     // TODO:
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -23,16 +23,18 @@
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "prsystem.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "prtime.h"
 
+#include "nsXULAppAPI.h"
+
 // Time between corrupt database backups.
 #define RECENT_BACKUP_TIME_MICROSEC (int64_t)86400 * PR_USEC_PER_SEC // 24H
 
 // Filename of the database.
 #define DATABASE_FILENAME NS_LITERAL_STRING("places.sqlite")
 // Filename used to backup corrupt databases.
 #define DATABASE_CORRUPT_FILENAME NS_LITERAL_STRING("places.sqlite.corrupt")
 
@@ -341,16 +343,18 @@ Database::Database()
   : mMainThreadStatements(mMainConn)
   , mMainThreadAsyncStatements(mMainConn)
   , mAsyncThreadStatements(mMainConn)
   , mDBPageSize(0)
   , mDatabaseStatus(nsINavHistoryService::DATABASE_STATUS_OK)
   , mShuttingDown(false)
   , mClosed(false)
 {
+  MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content,
+             "Cannot instantiate Places in the content process");
   // Attempting to create two instances of the service?
   MOZ_ASSERT(!gDatabase);
   gDatabase = this;
 }
 
 Database::~Database()
 {
   // Check to make sure it's us, in case somebody wrongly creates an extra
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -2001,16 +2001,22 @@ XPCOMUtils.defineLazyGetter(this, "gAsyn
   () => new Promise((resolve) => {
     Sqlite.wrapStorageConnection({
       connection: PlacesUtils.history.DBConnection,
     }).then(conn => {
       try {
         Sqlite.shutdown.addBlocker(
           "PlacesUtils wrapped connection closing",
           conn.close.bind(conn));
+
+        // Also ensure we close the wrapper in case Places shutdowns first.
+        Services.obs.addObserver(function observe() {
+          Services.obs.removeObserver(observe, "places-will-close-connection");
+          conn.close();
+        }, "places-will-close-connection", false);
       } catch(ex) {
         // It's too late to block shutdown, just close the connection.
         conn.close();
         throw ex;
       }
       resolve(conn);
     });
   })
--- a/toolkit/components/places/nsLivemarkService.js
+++ b/toolkit/components/places/nsLivemarkService.js
@@ -67,19 +67,19 @@ XPCOMUtils.defineLazyGetter(this, "gLive
         feedURI_anno: PlacesUtils.LMANNO_FEEDURI,
         siteURI_anno: PlacesUtils.LMANNO_SITEURI });
     for (let row of rows) {
       let siteURI = row.getResultByName("siteURI");
       let livemark = new Livemark({
         id: row.getResultByName("id"),
         guid: row.getResultByName("guid"),
         title: row.getResultByName("title"),
-        parentId: row.getResultByName("parent"),
+        parentId: row.getResultByName("parentId"),
         parentGuid: row.getResultByName("parentGuid"),
-        index: row.getResultByName("position"),
+        index: row.getResultByName("index"),
         dateAdded: row.getResultByName("dateAdded"),
         lastModified: row.getResultByName("lastModified"),
         feedURI: NetUtil.newURI(row.getResultByName("feedURI")),
         siteURI: siteURI ? NetUtil.newURI(siteURI) : null
       });
       livemarksMap.set(livemark.guid, livemark);
     }
     return livemarksMap;
--- a/toolkit/components/places/tests/unit/test_mozIAsyncLivemarks.js
+++ b/toolkit/components/places/tests/unit/test_mozIAsyncLivemarks.js
@@ -1,16 +1,48 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests functionality of the mozIAsyncLivemarks interface.
 
 const FEED_URI = NetUtil.newURI("http://feed.rss/");
 const SITE_URI = NetUtil.newURI("http://site.org/");
 
+// This test must be the first one, since it's testing the cache.
+add_task(function* test_livemark_cache() {
+  // Add a livemark through other APIs.
+  let folder = yield PlacesUtils.bookmarks.insert({
+    type: PlacesUtils.bookmarks.TYPE_FOLDER,
+    title: "test",
+    parentGuid: PlacesUtils.bookmarks.unfiledGuid
+  });
+  let id = yield PlacesUtils.promiseItemId(folder.guid);
+  PlacesUtils.annotations
+             .setItemAnnotation(id, PlacesUtils.LMANNO_FEEDURI,
+                                "http://example.com/feed",
+                                0, PlacesUtils.annotations.EXPIRE_NEVER);
+  PlacesUtils.annotations
+             .setItemAnnotation(id, PlacesUtils.LMANNO_SITEURI,
+                                "http://example.com/site",
+                                0, PlacesUtils.annotations.EXPIRE_NEVER);
+
+  let livemark = yield PlacesUtils.livemarks.getLivemark({ guid: folder.guid });
+  Assert.equal(folder.guid, livemark.guid);
+  Assert.equal(folder.dateAdded * 1000, livemark.dateAdded);
+  Assert.equal(folder.parentGuid, livemark.parentGuid);
+  Assert.equal(folder.index, livemark.index);
+  Assert.equal(folder.title, livemark.title);
+  Assert.equal(id, livemark.id);
+  Assert.equal(PlacesUtils.unfiledBookmarksFolderId, livemark.parentId);
+  Assert.equal("http://example.com/feed", livemark.feedURI.spec);
+  Assert.equal("http://example.com/site", livemark.siteURI.spec);
+
+  yield PlacesUtils.livemarks.removeLivemark(livemark);
+});
+
 add_task(function* test_addLivemark_noArguments_throws() {
   try {
     yield PlacesUtils.livemarks.addLivemark();
     do_throw("Invoking addLivemark with no arguments should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   }
 });
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -1986,96 +1986,84 @@ ThreadActor.prototype = {
   onNewScript: function (aScript, aGlobal) {
     // XXX: The scripts must be added to the ScriptStore before restoring
     // breakpoints in _addScript. If we try to add them to the ScriptStore
     // inside _addScript, we can accidentally set a breakpoint in a top level
     // script as a "closest match" because we wouldn't have added the child
     // scripts to the ScriptStore yet.
     this.scripts.addScripts(this.dbg.findScripts({ source: aScript.source }));
 
-    this._addScript(aScript);
-
-    // `onNewScript` is only fired for top-level scripts (AKA staticLevel == 0),
-    // but top-level scripts have the wrong `lineCount` sometimes (bug 979094),
-    // so iterate over the immediate children to activate breakpoints for now
-    // (TODO bug 1124258: don't do this when `lineCount` bug is fixed)
-    for (let s of aScript.getChildScripts()) {
-      this._addScript(s);
-    }
+    this._addSource(aScript.source);
   },
 
   onNewSource: function (aSource) {
     this.conn.send({
       from: this.actorID,
       type: "newSource",
       source: aSource.form()
     });
   },
 
   /**
-   * Restore any pre-existing breakpoints to the scripts that we have access to.
+   * Restore any pre-existing breakpoints to the sources that we have access to.
    */
   _restoreBreakpoints: function () {
     if (this.breakpointActorMap.size === 0) {
       return;
     }
 
-    for (let s of this.scripts.getAllScripts()) {
-      this._addScript(s);
+    for (let s of this.scripts.getSources()) {
+      this._addSource(s);
     }
   },
 
   /**
-   * Add the provided script to the server cache.
+   * Add the provided source to the server cache.
    *
-   * @param aScript Debugger.Script
-   *        The source script that will be stored.
-   * @returns true, if the script was added; false otherwise.
+   * @param aSource Debugger.Source
+   *        The source that will be stored.
+   * @returns true, if the source was added; false otherwise.
    */
-  _addScript: function (aScript) {
-    if (!this.sources.allowSource(aScript.source)) {
+  _addSource: function (aSource) {
+    if (!this.sources.allowSource(aSource)) {
       return false;
     }
 
     // Set any stored breakpoints.
     let promises = [];
-    let sourceActor = this.sources.createNonSourceMappedActor(aScript.source);
-    let endLine = aScript.startLine + aScript.lineCount - 1;
+    let sourceActor = this.sources.createNonSourceMappedActor(aSource);
     for (let _actor of this.breakpointActorMap.findActors()) {
       // XXX bug 1142115: We do async work in here, so we need to
       // create a fresh binding because for/of does not yet do that in
       // SpiderMonkey
       let actor = _actor;
 
       if (actor.isPending) {
         promises.push(actor.originalLocation.originalSourceActor._setBreakpoint(actor));
       } else {
         promises.push(this.sources.getGeneratedLocation(actor.originalLocation)
                                   .then((generatedLocation) => {
-          // Limit the search to the line numbers contained in the new script.
-          if (generatedLocation.generatedSourceActor.actorID === sourceActor.actorID &&
-              generatedLocation.generatedLine >= aScript.startLine &&
-              generatedLocation.generatedLine <= endLine) {
+          if (generatedLocation.generatedSourceActor.actorID === sourceActor.actorID) {
             sourceActor._setBreakpointAtGeneratedLocation(
               actor,
               generatedLocation
             );
           }
         }));
       }
     }
 
     if (promises.length > 0) {
       this.synchronize(Promise.all(promises));
     }
 
     // Go ahead and establish the source actors for this script, which
     // fetches sourcemaps if available and sends onNewSource
     // notifications
-    this.sources.createSourceActors(aScript.source);
+    this.sources.createSourceActors(aSource);
 
     return true;
   },
 
 
   /**
    * Get prototypes and properties of multiple objects.
    */