Merge f-t to m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 17 May 2014 18:05:32 -0700
changeset 183681 41a54c8add09fe29b472fb4b7787b60a45dca1c9
parent 183671 00ef3a7d7aa74054a6d94cb8ab456231879f3e81 (current diff)
parent 183680 ece88e3d2ebf882c87991e279065211d3c69f478 (diff)
child 183688 67e42658394e8f74c7b505f30d61fb7e8e76dd8a
child 183759 f2cdab51407b0d333aae8ddd7fb70cf9c2209b52
push id26800
push userphilringnalda@gmail.com
push dateSun, 18 May 2014 01:05:46 +0000
treeherdermozilla-central@41a54c8add09 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone32.0a1
first release with
nightly linux32
41a54c8add09 / 32.0a1 / 20140518030203 / files
nightly linux64
41a54c8add09 / 32.0a1 / 20140518030203 / files
nightly mac
41a54c8add09 / 32.0a1 / 20140518030203 / files
nightly win32
41a54c8add09 / 32.0a1 / 20140518030203 / files
nightly win64
41a54c8add09 / 32.0a1 / 20140518030203 / 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 f-t to m-c
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -806,29 +806,25 @@ pref("plugin.state.npmcffplg", 2);
 #ifdef XP_MACOSX
 pref("plugin.state.f5 ssl vpn plugin", 2);
 pref("plugin.state.f5 sam inspection host plugin", 2);
 #endif
 
 // display door hanger if flash not installed
 pref("plugins.notifyMissingFlash", true);
 
-#ifdef XP_WIN
-pref("browser.preferences.instantApply", false);
-#else
 pref("browser.preferences.instantApply", true);
-#endif
 #ifdef XP_MACOSX
 pref("browser.preferences.animateFadeIn", true);
 #else
 pref("browser.preferences.animateFadeIn", false);
 #endif
 
 // Toggles between the two Preferences implementations, pop-up window and in-content
-pref("browser.preferences.inContent", false);
+pref("browser.preferences.inContent", true);
 
 pref("browser.download.show_plugins_in_list", true);
 pref("browser.download.hide_plugins_without_extensions", true);
 
 // Backspace and Shift+Backspace behavior
 // 0 goes Back/Forward
 // 1 act like PgUp/PgDown
 // 2 and other values, nothing
--- a/browser/base/content/test/general/browser_datareporting_notification.js
+++ b/browser/base/content/test/general/browser_datareporting_notification.js
@@ -94,51 +94,52 @@ function test_multiple_windows() {
   // results in dismiss on every window.
   let window2 = OpenBrowserWindow();
   whenDelayedStartupFinished(window2, function onWindow() {
     let notification1 = document.getElementById("global-notificationbox");
     let notification2 = window2.document.getElementById("global-notificationbox");
     ok(notification2, "2nd window has a global notification box.");
 
     let policy;
-
     let displayCount = 0;
-    let prefPaneClosed = false;
-    let childWindowClosed = false;
+    let prefWindowClosed = false;
+    let mutationObserversRemoved = false;
 
     function onAlertDisplayed() {
       displayCount++;
 
       if (displayCount != 2) {
         return;
       }
 
       ok(true, "Data reporting info bar displayed on all open windows.");
 
       // We register two independent observers and we need both to clean up
       // properly. This handles gating for test completion.
       function maybeFinish() {
-        if (!prefPaneClosed) {
+        if (!prefWindowClosed) {
           dump("Not finishing test yet because pref pane isn't closed.\n");
           return;
         }
 
-        if (!childWindowClosed) {
-          dump("Not finishing test yet because child window isn't closed.\n");
+        if (!mutationObserversRemoved) {
+          dump("Not finishing test yet because mutation observers haven't been removed yet.\n");
           return;
         }
 
+        window2.close();
+
         dump("Finishing multiple window test.\n");
         rootLogger.removeAppender(dumpAppender);
         delete dumpAppender;
         delete rootLogger;
         finish();
       }
+      let closeCount = 0;
 
-      let closeCount = 0;
       function onAlertClose() {
         closeCount++;
 
         if (closeCount != 2) {
           return;
         }
 
         ok(true, "Closing info bar on one window closed them on all.");
@@ -146,18 +147,17 @@ function test_multiple_windows() {
         is(policy.notifyState, policy.STATE_NOTIFY_COMPLETE,
            "Closing info bar with multiple windows completes notification.");
         ok(policy.dataSubmissionPolicyAccepted, "Data submission policy accepted.");
         is(policy.dataSubmissionPolicyResponseType, "accepted-info-bar-button-pressed",
            "Policy records reason for acceptance was button press.");
         is(notification1.allNotifications.length, 0, "No notifications remain on main window.");
         is(notification2.allNotifications.length, 0, "No notifications remain on 2nd window.");
 
-        window2.close();
-        childWindowClosed = true;
+        mutationObserversRemoved = true;
         maybeFinish();
       }
 
       waitForNotificationClose(notification1.currentNotification, onAlertClose);
       waitForNotificationClose(notification2.currentNotification, onAlertClose);
 
       // While we're here, we dual purpose this test to check that pressing the
       // button does the right thing.
@@ -165,21 +165,21 @@ function test_multiple_windows() {
       is(buttons.length, 1, "There is 1 button in the data reporting notification.");
       let button = buttons[0];
 
       // Automatically close preferences window when it is opened as part of
       // button press.
       Services.obs.addObserver(function observer(prefWin, topic, data) {
         Services.obs.removeObserver(observer, "advanced-pane-loaded");
 
-        ok(true, "Pref pane opened on info bar button press.");
+        ok(true, "Advanced preferences opened on info bar button press.");
         executeSoon(function soon() {
-          dump("Closing pref pane.\n");
+          dump("Closing preferences.\n");
           prefWin.close();
-          prefPaneClosed = true;
+          prefWindowClosed = true;
           maybeFinish();
         });
       }, "advanced-pane-loaded", false);
 
       button.click();
     }
 
     notification1.addEventListener("AlertActive", function active1() {
--- a/browser/base/content/test/general/browser_offlineQuotaNotification.js
+++ b/browser/base/content/test/general/browser_offlineQuotaNotification.js
@@ -33,40 +33,40 @@ function checkPreferences(prefsWin) {
       // all good, we are done.
       prefsWin.close();
       finish();
     });
   });
 }
 
 function test() {
+  if (Services.prefs.getBoolPref("browser.preferences.inContent")) {
+    // Bug 881576 - ensure this works with inContent prefs.
+    todo(false, "Bug 881576 - this test needs to be updated for inContent prefs");
+    return;
+  }
   waitForExplicitFinish();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
     gBrowser.selectedBrowser.contentWindow.applicationCache.oncached = function() {
       executeSoon(function() {
         // We got cached - now we should have provoked the quota warning.
         let notification = PopupNotifications.getNotification('offline-app-usage');
         ok(notification, "have offline-app-usage notification");
         // select the default action - this should cause the preferences
         // window to open - which we track either via a window watcher (for
         // the window-based prefs) or via an "Initialized" event (for
         // in-content prefs.)
-        if (Services.prefs.getBoolPref("browser.preferences.inContent")) {
-          // Bug 881576 - ensure this works with inContent prefs.
-          todo(false, "Bug 881576 - this test needs to be updated for inContent prefs");
-        } else {
-          Services.ww.registerNotification(function wwobserver(aSubject, aTopic, aData) {
-            if (aTopic != "domwindowopened")
-              return;
-            Services.ww.unregisterNotification(wwobserver);
-            checkPreferences(aSubject);
-          });
-          PopupNotifications.panel.firstElementChild.button.click();
-        }
+        Services.ww.registerNotification(function wwobserver(aSubject, aTopic, aData) {
+          if (aTopic != "domwindowopened")
+            return;
+          Services.ww.unregisterNotification(wwobserver);
+          checkPreferences(aSubject);
+        });
+        PopupNotifications.panel.firstElementChild.button.click();
       });
     };
     Services.prefs.setIntPref("offline-apps.quota.warn", 1);
 
     // Click the notification panel's "Allow" button.  This should kick
     // off updates which will call our oncached handler above.
     PopupNotifications.panel.firstElementChild.button.click();
   }, true);
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -500,20 +500,23 @@ function openPreferences(paneID, extraAr
     function switchToPane() {
       if (paneID) {
         browser.contentWindow.selectCategory(paneID);
       }
       switchToAdvancedSubPane(browser.contentDocument);
     }
 
     if (newLoad) {
-      browser.addEventListener("load", function onload() {
-        browser.removeEventListener("load", onload, true);
+      Services.obs.addObserver(function advancedPaneLoadedObs(prefWin, topic, data) {
+        if (prefWin != browser.contentWindow) {
+          return;
+        }
+        Services.obs.removeObserver(advancedPaneLoadedObs, "advanced-pane-loaded");
         switchToPane();
-      }, true);
+      }, "advanced-pane-loaded", false);
     } else {
       switchToPane();
     }
   } else {
     var instantApply = getBoolPref("browser.preferences.instantApply", false);
     var features = "chrome,titlebar,toolbar,centerscreen" + (instantApply ? ",dialog=no" : ",modal");
 
     var win = Services.wm.getMostRecentWindow("Browser:Preferences");
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -767,25 +767,26 @@ let CustomizableUIInternal = {
 
     for (let areaNode of areaNodes) {
       let window = areaNode.ownerDocument.defaultView;
       if (!showInPrivateBrowsing &&
           PrivateBrowsingUtils.isWindowPrivate(window)) {
         continue;
       }
 
+      let container = areaNode.customizationTarget;
       let widgetNode = window.document.getElementById(aWidgetId);
-      if (!widgetNode) {
+      if (widgetNode && isOverflowable) {
+        container = areaNode.overflowable.getContainerFor(widgetNode);
+      }
+
+      if (!widgetNode || !container.contains(widgetNode)) {
         INFO("Widget not found, unable to remove");
         continue;
       }
-      let container = areaNode.customizationTarget;
-      if (isOverflowable) {
-        container = areaNode.overflowable.getContainerFor(widgetNode);
-      }
 
       this.notifyListeners("onWidgetBeforeDOMChange", widgetNode, null, container, true);
 
       // We remove location attributes here to make sure they're gone too when a
       // widget is removed from a toolbar to the palette. See bug 930950.
       this.removeLocationAttributes(widgetNode);
       // We also need to remove the panel context menu if it's there:
       this.ensureButtonContextMenu(widgetNode);
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -103,12 +103,13 @@ skip-if = os == "linux"
 [browser_987177_destroyWidget_xul.js]
 [browser_987177_xul_wrapper_updating.js]
 [browser_987492_window_api.js]
 [browser_987640_charEncoding.js]
 [browser_992747_toggle_noncustomizable_toolbar.js]
 [browser_993322_widget_notoolbar.js]
 [browser_995164_registerArea_during_customize_mode.js]
 [browser_996364_registerArea_different_properties.js]
+[browser_996635_remove_non_widgets.js]
 [browser_1003588_no_specials_in_panel.js]
 [browser_1008559_anchor_undo_restore.js]
 [browser_bootstrapped_custom_toolbar.js]
 [browser_panel_toggle.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_996635_remove_non_widgets.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// NB: This is testing what happens if something that /isn't/ a customizable
+// widget gets used in CustomizableUI APIs. Don't use this as an example of
+// what should happen in a "normal" case or how you should use the API.
+function test() {
+  // First create a button that isn't customizable, and add it in the nav-bar,
+  // but not in the customizable part of it (the customization target) but
+  // next to the main (hamburger) menu button.
+  const buttonID = "Test-non-widget-non-removable-button";
+  let btn = document.createElement("toolbarbutton");
+  btn.id = buttonID;
+  btn.label = "Hi";
+  btn.setAttribute("style", "width: 20px; height: 20px; background-color: red");
+  document.getElementById("nav-bar").appendChild(btn);
+  registerCleanupFunction(function() {
+    btn.remove();
+  });
+
+  // Now try to add this non-customizable button to the tabstrip. This will
+  // update the internal bookkeeping (ie placements) information, but shouldn't
+  // move the node.
+  CustomizableUI.addWidgetToArea(buttonID, CustomizableUI.AREA_TABSTRIP);
+  let placement = CustomizableUI.getPlacementOfWidget(buttonID);
+  // Check our bookkeeping
+  ok(placement, "Button should be placed");
+  is(placement && placement.area, CustomizableUI.AREA_TABSTRIP, "Should be placed on tabstrip.");
+  // Check we didn't move the node.
+  is(btn.parentNode && btn.parentNode.id, "nav-bar", "Actual button should still be on navbar.");
+
+  // Now remove the node again. This should remove the bookkeeping, but again
+  // not affect the actual node.
+  CustomizableUI.removeWidgetFromArea(buttonID);
+  placement = CustomizableUI.getPlacementOfWidget(buttonID);
+  // Check our bookkeeping:
+  ok(!placement, "Button should no longer have a placement.");
+  // Check our node.
+  is(btn.parentNode && btn.parentNode.id, "nav-bar", "Actual button should still be on navbar.");
+}
+
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -66,16 +66,19 @@ var gAdvancedPane = {
     this.initSubmitCrashes();
 #endif
     this.initTelemetry();
 #ifdef MOZ_SERVICES_HEALTHREPORT
     this.initSubmitHealthReport();
 #endif
     this.updateActualCacheSize();
     this.updateActualAppCacheSize();
+
+    // Notify observers that the UI is now ready
+    Services.obs.notifyObservers(window, "advanced-pane-loaded", null);
   },
 
   /**
    * Stores the identity of the current tab in preferences so that the selected
    * tab can be persisted between openings of the preferences window.
    */
   tabSelectionChanged: function ()
   {
--- a/browser/components/preferences/in-content/preferences.js
+++ b/browser/components/preferences/in-content/preferences.js
@@ -39,16 +39,17 @@ function init_all() {
     history.replaceState("paneGeneral", document.title);
   }
 }
 
 function selectCategory(name) {
   let categories = document.getElementById("categories");
   let item = categories.querySelector(".category[value=" + name + "]");
   categories.selectedItem = item;
+  gotoPref(name);
 }
 
 function gotoPref(page) {
   window.history.replaceState(page, document.title);
   search(page, "data-category");
 }
 
 function search(aQuery, aAttribute) {
--- a/browser/themes/linux/devtools/debugger.css
+++ b/browser/themes/linux/devtools/debugger.css
@@ -1,9 +1,5 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %include ../../shared/devtools/debugger.inc.css
-
-.devtools-sidebar-tabs > tabs > tab {
-  min-height: 24px !important;
-}
--- a/browser/themes/shared/devtools/debugger.inc.css
+++ b/browser/themes/shared/devtools/debugger.inc.css
@@ -537,17 +537,17 @@
 
 .theme-light .dbg-results-line-contents-string[match=true] {
   color: #18191a; /* Dark foreground text */
 }
 
 /* Toolbar controls */
 
 .devtools-sidebar-tabs > tabs > tab {
-  min-height: 1em !important;
+  min-height: 24px !important;
   padding: 0 !important;
 }
 
 #resume {
   list-style-image: url(debugger-pause.png);
   -moz-image-region: rect(0px,16px,16px,0px);
   transition: background 0.15s ease-in-out;
 }
--- a/browser/themes/windows/devtools/debugger.css
+++ b/browser/themes/windows/devtools/debugger.css
@@ -1,17 +1,5 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %include ../../shared/devtools/debugger.inc.css
-
-.devtools-sidebar-tabs > tabs > tab {
-  min-height: 22px !important;
-}
-
-#instruments-pane-toggle:hover {
-  -moz-image-region: rect(0px,32px,16px,16px);
-}
-
-#instruments-pane-toggle:hover:active {
-  -moz-image-region: rect(0px,48px,16px,32px);
-}
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -427,17 +427,17 @@ abstract public class BrowserApp extends
         return values;
     }
 
     void handleReaderRemoved(final String url) {
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 BrowserDB.removeReadingListItemWithURL(getContentResolver(), url);
-                showToast(R.string.reading_list_removed, Toast.LENGTH_SHORT);
+                showToast(R.string.page_removed, Toast.LENGTH_SHORT);
 
                 final int count = BrowserDB.getReadingListCount(getContentResolver());
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:ListCountUpdated", Integer.toString(count)));
             }
         });
     }
 
     private void handleReaderFaviconRequest(final String url) {
--- a/mobile/android/base/GeckoSharedPrefs.java
+++ b/mobile/android/base/GeckoSharedPrefs.java
@@ -1,30 +1,28 @@
 /* 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;
 
 import org.mozilla.gecko.mozglue.RobocopTarget;
-import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
 import android.os.Build;
 import android.os.StrictMode;
 import android.preference.PreferenceManager;
 import android.util.Log;
 
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * {@code GeckoSharedPrefs} provides scoped SharedPreferences instances.
  * You should use this API instead of using Context.getSharedPreferences()
  * directly. There are three methods to get scoped SharedPreferences instances:
  *
  * forApp()
  *     Use it for app-wide, cross-profile pref keys.
@@ -54,19 +52,16 @@ public final class GeckoSharedPrefs {
     public static final String PROFILE_PREFS_NAME_PREFIX = "GeckoProfile-";
 
     // The prefs key that holds the current migration
     private static final String PREFS_VERSION_KEY = "gecko_shared_prefs_migration";
 
     // For disabling migration when getting a SharedPreferences instance
     private static final EnumSet<Flags> disableMigrations = EnumSet.of(Flags.DISABLE_MIGRATIONS);
 
-    // Timeout for migration commits to be done (10 seconds)
-    private static final int MIGRATION_COMMIT_TIMEOUT_MSEC = 10000;
-
     // The keys that have to be moved from ProfileManager's default
     // shared prefs to the profile from version 0 to 1.
     private static final String[] PROFILE_MIGRATIONS_0_TO_1 = {
         "home_panels",
         "home_locale"
     };
 
     // For optimizing the migration check in subsequent get() calls
@@ -249,17 +244,16 @@ public final class GeckoSharedPrefs {
             putEntry(to, key, entry.getValue());
         }
 
         // Clear PreferenceManager's prefs once we're done
         // and return the Editor to be committed.
         return pmPrefs.edit().clear();
     }
 
-    @SuppressWarnings("unchecked")
     private static void putEntry(Editor to, String key, Object value) {
         Log.d(LOGTAG, "Migrating key = " + key + " with value = " + value);
 
         if (value instanceof String) {
             to.putString(key, (String) value);
         } else if (value instanceof Boolean) {
             to.putBoolean(key, (Boolean) value);
         } else if (value instanceof Long) {
--- a/mobile/android/base/home/HomeFragment.java
+++ b/mobile/android/base/home/HomeFragment.java
@@ -186,31 +186,44 @@ abstract class HomeFragment extends Frag
 
         if (itemId == R.id.home_open_in_reader) {
             final String url = ReaderModeUtils.getAboutReaderForUrl(info.url);
             Tabs.getInstance().loadUrl(url, Tabs.LOADURL_NONE);
             return true;
         }
 
         if (itemId == R.id.home_remove) {
-            // Prioritize removing a history entry over a bookmark in the case of a combined item.
-            if (info.hasHistoryId()) {
-                new RemoveHistoryTask(context, info.historyId).execute();
-                return true;
+            // Track notification queuing of removal so we don't notify multiple times.
+            boolean notifyQueued = false;
+            final String url = info.url;
+
+            // This might be a reading list item. Try removing it by url.
+            (new RemoveReadingListItemTask(context, url)).execute();
+
+            if (info.isInReadingList()) {
+                // For reading list, this may still double-notify because reading list is in Gecko.
+                notifyQueued = true;
             }
 
             if (info.hasBookmarkId()) {
-                new RemoveBookmarkTask(context, info.bookmarkId).execute();
-                return true;
+                new RemoveBookmarkTask(context, info.bookmarkId, !notifyQueued).execute();
+                notifyQueued = true;
+            } else {
+                new RemoveBookmarkTask(context, url, false).execute();
             }
 
-            if (info.isInReadingList()) {
-                (new RemoveReadingListItemTask(context, info.readingListItemId, info.url)).execute();
-                return true;
+            if (info.hasHistoryId()) {
+                new RemoveHistoryTask(context, info.historyId, !notifyQueued).execute();
+                notifyQueued = true;
+            } else {
+                // We can't know for sure if there is also a history item, but try anyways.
+                new RemoveHistoryTask(context, url, false).execute();
             }
+
+            return notifyQueued;
         }
 
         return false;
     }
 
     @Override
     public void setUserVisibleHint (boolean isVisibleToUser) {
         if (isVisibleToUser == getUserVisibleHint()) {
@@ -269,77 +282,112 @@ abstract class HomeFragment extends Frag
 
         load();
         mIsLoaded = true;
     }
 
     private static class RemoveBookmarkTask extends UiAsyncTask<Void, Void, Void> {
         private final Context mContext;
         private final int mId;
+        private final String mUrl;
+        private final boolean mNotify;
 
-        public RemoveBookmarkTask(Context context, int id) {
+        public RemoveBookmarkTask(Context context, int id, String url, boolean notify) {
             super(ThreadUtils.getBackgroundHandler());
 
             mContext = context;
             mId = id;
+            mUrl = url;
+            mNotify = notify;
+        }
+
+        public RemoveBookmarkTask(Context context, int id, boolean notify) {
+            this(context, id, null, notify);
+        }
+
+        public RemoveBookmarkTask(Context context, String url, boolean notify) {
+            this(context, -1, url, notify);
         }
 
         @Override
         public Void doInBackground(Void... params) {
             ContentResolver cr = mContext.getContentResolver();
-            BrowserDB.removeBookmark(cr, mId);
+            if (mId > 0) {
+                BrowserDB.removeBookmark(cr, mId);
+            } else {
+                BrowserDB.removeBookmarksWithURL(cr, mUrl);
+            }
+
             return null;
         }
 
         @Override
         public void onPostExecute(Void result) {
-            Toast.makeText(mContext, R.string.bookmark_removed, Toast.LENGTH_SHORT).show();
+            if (mNotify) {
+                Toast.makeText(mContext, R.string.page_removed, Toast.LENGTH_SHORT).show();
+            }
         }
     }
 
 
     private static class RemoveReadingListItemTask extends UiAsyncTask<Void, Void, Void> {
-        private final int mId;
         private final String mUrl;
         private final Context mContext;
 
-        public RemoveReadingListItemTask(Context context, int id, String url) {
+        public RemoveReadingListItemTask(Context context, String url) {
             super(ThreadUtils.getBackgroundHandler());
-            mId = id;
             mUrl = url;
             mContext = context;
         }
 
         @Override
         public Void doInBackground(Void... params) {
             ContentResolver cr = mContext.getContentResolver();
-            BrowserDB.removeReadingListItem(cr, mId);
+            BrowserDB.removeReadingListItemWithURL(cr, mUrl);
 
             GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Remove", mUrl);
             GeckoAppShell.sendEventToGecko(e);
 
             return null;
         }
     }
 
     private static class RemoveHistoryTask extends UiAsyncTask<Void, Void, Void> {
         private final Context mContext;
         private final int mId;
+        private final String mUrl;
+        private final boolean mNotify;
 
-        public RemoveHistoryTask(Context context, int id) {
+        public RemoveHistoryTask(Context context, int id, boolean notify) {
+            this(context, id, null, notify);
+        }
+
+        public RemoveHistoryTask(Context context, String url, boolean notify) {
+            this(context, -1, url, notify);
+        }
+
+        public RemoveHistoryTask(Context context, int id, String url, boolean notify) {
             super(ThreadUtils.getBackgroundHandler());
 
             mContext = context;
             mId = id;
+            mUrl = url;
+            mNotify = notify;
         }
 
         @Override
         public Void doInBackground(Void... params) {
-            BrowserDB.removeHistoryEntry(mContext.getContentResolver(), mId);
+            if (mId > 0) {
+                BrowserDB.removeHistoryEntry(mContext.getContentResolver(), mId);
+            } else {
+                BrowserDB.removeHistoryEntry(mContext.getContentResolver(), mUrl);
+            }
             return null;
         }
 
         @Override
         public void onPostExecute(Void result) {
-            Toast.makeText(mContext, R.string.history_removed, Toast.LENGTH_SHORT).show();
+            if (mNotify) {
+                Toast.makeText(mContext, R.string.page_removed, Toast.LENGTH_SHORT).show();
+            }
         }
     }
 }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -287,32 +287,34 @@ size. -->
 <!ENTITY pref_titlebar_mode "Title bar">
 <!ENTITY pref_titlebar_mode_title "Show page title">
 <!ENTITY pref_titlebar_mode_url "Show page address">
 
 <!-- Localization note (pref_scroll_title_bar): Label for setting that controls
      whether or not the dynamic toolbar is enabled. -->
 <!ENTITY pref_scroll_title_bar "Scroll title bar">
 
-<!ENTITY history_removed "Page removed">
+<!-- Localization note (page_removed): This string appears in a toast message when
+     any page is removed frome about:home. This includes pages that are in history,
+     bookmarks, or reading list. -->
+<!ENTITY page_removed "Page removed">
 
 <!ENTITY bookmark_edit_title "Edit Bookmark">
 <!ENTITY bookmark_edit_name "Name">
 <!ENTITY bookmark_edit_location "Location">
 <!ENTITY bookmark_edit_keyword "Keyword">
 
 <!-- Localization note (site_settings_*) : These strings are used in the "Site Settings"
      dialog that appears after selecting the "Edit Site Settings" context menu item. -->
 <!ENTITY site_settings_title3       "Site Settings">
 <!ENTITY site_settings_cancel       "Cancel">
 <!ENTITY site_settings_clear        "Clear">
 <!ENTITY site_settings_no_settings  "There are no settings to clear.">
 
 <!ENTITY reading_list_added "Page added to your Reading List">
-<!ENTITY reading_list_removed "Page removed from your Reading List">
 <!ENTITY reading_list_failed "Failed to add page to your Reading List">
 <!ENTITY reading_list_duplicate "Page already in your Reading List">
 
 <!-- Localization note : These strings are used as alternate text for accessibility.
      They are not visible in the UI. -->
 <!ENTITY page_action_dropmarker_description "Additional Actions">
 
 <!ENTITY masterpassword_create_title "Create Master Password">
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -245,17 +245,16 @@
   <string name="edit_mode_cancel">&edit_mode_cancel;</string>
 
   <string name="site_settings_title">&site_settings_title3;</string>
   <string name="site_settings_cancel">&site_settings_cancel;</string>
   <string name="site_settings_clear">&site_settings_clear;</string>
   <string name="site_settings_no_settings">&site_settings_no_settings;</string>
 
   <string name="reading_list_added">&reading_list_added;</string>
-  <string name="reading_list_removed">&reading_list_removed;</string>
   <string name="reading_list_failed">&reading_list_failed;</string>
   <string name="reading_list_duplicate">&reading_list_duplicate;</string>
 
   <string name="page_action_dropmarker_description">&page_action_dropmarker_description;</string>
 
   <string name="contextmenu_open_new_tab">&contextmenu_open_new_tab;</string>
   <string name="contextmenu_open_private_tab">&contextmenu_open_private_tab;</string>
   <string name="contextmenu_open_in_reader">&contextmenu_open_in_reader;</string>
@@ -274,17 +273,17 @@
   <string name="contextmenu_add_search_engine">&contextmenu_add_search_engine;</string>
 
   <string name="pref_titlebar_mode">&pref_titlebar_mode;</string>
   <string name="pref_titlebar_mode_title">&pref_titlebar_mode_title;</string>
   <string name="pref_titlebar_mode_url">&pref_titlebar_mode_url;</string>
 
   <string name="pref_scroll_title_bar">&pref_scroll_title_bar;</string>
 
-  <string name="history_removed">&history_removed;</string>
+  <string name="page_removed">&page_removed;</string>
 
   <string name="bookmark_edit_title">&bookmark_edit_title;</string>
   <string name="bookmark_edit_name">&bookmark_edit_name;</string>
   <string name="bookmark_edit_location">&bookmark_edit_location;</string>
   <string name="bookmark_edit_keyword">&bookmark_edit_keyword;</string>
 
   <string name="pref_use_master_password">&pref_use_master_password;</string>
   <string name="masterpassword_create_title">&masterpassword_create_title;</string>
--- a/toolkit/content/aboutNetworking.xhtml
+++ b/toolkit/content/aboutNetworking.xhtml
@@ -29,17 +29,17 @@
         <div id="menu">
             <button class="selected" value="http">&aboutNetworking.http;</button>
             <button value="sockets">&aboutNetworking.sockets;</button>
             <button value="dns">&aboutNetworking.dns;</button>
             <button value="websockets">&aboutNetworking.websockets;</button>
         </div>
         <div id="refreshdiv">
             <button id="refreshButton">&aboutNetworking.refresh;</button>
-            <input id="autorefcheck" type="checkbox" name="Autorefresh" />&aboutNetworking.autoRefresh;
+            <label><input id="autorefcheck" type="checkbox" name="Autorefresh" />&aboutNetworking.autoRefresh;</label>
         </div>
 
         <div id="http" class="tab active">
             <table border="1" width="100%">
                 <thead>
                     <tr>
                         <th>&aboutNetworking.hostname;</th>
                         <th>&aboutNetworking.port;</th>
--- a/toolkit/mozapps/extensions/test/browser/browser_experiments.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_experiments.js
@@ -204,27 +204,27 @@ add_task(function* testOpenPreferences()
     return;
   }
 
   is_element_visible(btn, "Change telemetry button visible in in-content UI.");
 
   let deferred = Promise.defer();
   Services.obs.addObserver(function observer(prefWin, topic, data) {
     Services.obs.removeObserver(observer, "advanced-pane-loaded");
-
     info("Advanced preference pane opened.");
+    executeSoon(function() {
+      // We want this test to fail if the preferences pane changes.
+      let el = prefWin.document.getElementById("dataChoicesPanel");
+      is_element_visible(el);
 
-    // We want this test to fail if the preferences pane changes.
-    let el = prefWin.document.getElementById("dataChoicesPanel");
-    is_element_visible(el);
+      prefWin.close();
+      info("Closed preferences pane.");
 
-    prefWin.close();
-    info("Closed preferences pane.");
-
-    deferred.resolve();
+      deferred.resolve();
+    });
   }, "advanced-pane-loaded", false);
 
   info("Loading preferences pane.");
   EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow);
 
   yield deferred.promise;
 });
 
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -427,22 +427,22 @@ function is_hidden(aElement) {
   if (aElement.parentNode != aElement.ownerDocument)
     return is_hidden(aElement.parentNode);
 
   return false;
 }
 
 function is_element_visible(aElement, aMsg) {
   isnot(aElement, null, "Element should not be null, when checking visibility");
-  ok(!is_hidden(aElement), aMsg);
+  ok(!is_hidden(aElement), aMsg || (aElement + " should be visible"));
 }
 
 function is_element_hidden(aElement, aMsg) {
   isnot(aElement, null, "Element should not be null, when checking visibility");
-  ok(is_hidden(aElement), aMsg);
+  ok(is_hidden(aElement), aMsg || (aElement + " should be hidden"));
 }
 
 /**
  * Install an add-on and call a callback when complete.
  *
  * The callback will receive the Addon for the installed add-on.
  */
 function install_addon(path, cb, pathPrefix=TESTROOT) {