Merge f-t to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 10 May 2015 18:43:50 -0700
changeset 274580 d8420a541d1c1a00f0e4b60a7c55f4c0b928edd9
parent 274572 1139ae4ca5f7e09e38225981ace07dafb3cf5f37 (current diff)
parent 274579 93f893af2866f332b77c36943e4eb0a05767b7fb (diff)
child 274581 d264b461947f4c3cf844724ad764b29e4a203803
child 275578 152f16cf0602e20ce80022522223ed36a5224938
child 275629 b4018ab42fd39b209e186ca3e648651ec13378ad
child 275680 2f26a0e2c3320194a521f8c0c551ac37a29253d1
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
first release with
nightly linux32
d8420a541d1c / 40.0a1 / 20150511030203 / files
nightly linux64
d8420a541d1c / 40.0a1 / 20150511030203 / files
nightly mac
d8420a541d1c / 40.0a1 / 20150511030203 / files
nightly win32
d8420a541d1c / 40.0a1 / 20150511030203 / files
nightly win64
d8420a541d1c / 40.0a1 / 20150511030203 / 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=merge
--- a/browser/devtools/netmonitor/test/browser_net_autoscroll.js
+++ b/browser/devtools/netmonitor/test/browser_net_autoscroll.js
@@ -6,92 +6,81 @@
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejection should be fixed.
 //
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: aValue.content is undefined");
 
 /**
  * Bug 863102 - Automatically scroll down upon new network requests.
  */
-
-function test() {
+add_task(function*() {
   requestLongerTimeout(2);
   let monitor, debuggee, requestsContainer, scrollTop;
 
-  initNetMonitor(INFINITE_GET_URL).then(([aTab, aDebuggee, aMonitor]) => {
-    monitor = aMonitor;
-    debuggee = aDebuggee;
-    let win = monitor.panelWin;
-    let topNode = win.document.getElementById("requests-menu-contents");
-    requestsContainer = topNode.getElementsByTagName("scrollbox")[0];
-    ok(!!requestsContainer, "Container element exists as expected.");
-  })
+  let [aTab, aDebuggee, aMonitor] = yield initNetMonitor(INFINITE_GET_URL);
+  monitor = aMonitor;
+  debuggee = aDebuggee;
+  let win = monitor.panelWin;
+  let topNode = win.document.getElementById("requests-menu-contents");
+  requestsContainer = topNode.getElementsByTagName("scrollbox")[0];
+  ok(!!requestsContainer, "Container element exists as expected.");
 
   // (1) Check that the scroll position is maintained at the bottom
   // when the requests overflow the vertical size of the container.
-  .then(() => {
-    return waitForRequestsToOverflowContainer(monitor, requestsContainer);
-  })
-  .then(() => {
-    ok(scrolledToBottom(requestsContainer), "Scrolled to bottom on overflow.");
-  })
+  yield waitForRequestsToOverflowContainer(monitor, requestsContainer);
+  yield waitForScroll(monitor);
+  ok(scrolledToBottom(requestsContainer), "Scrolled to bottom on overflow.");
 
   // (2) Now set the scroll position somewhere in the middle and check
   // that additional requests do not change the scroll position.
-  .then(() => {
-    let children = requestsContainer.childNodes;
-    let middleNode = children.item(children.length / 2);
-    middleNode.scrollIntoView();
-    ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom.");
-    scrollTop = requestsContainer.scrollTop; // save for comparison later
-    return waitForNetworkEvents(monitor, 8);
-  })
-  .then(() => {
-    is(requestsContainer.scrollTop, scrollTop, "Did not scroll.");
-  })
+  let children = requestsContainer.childNodes;
+  let middleNode = children.item(children.length / 2);
+  middleNode.scrollIntoView();
+  ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom.");
+  scrollTop = requestsContainer.scrollTop; // save for comparison later
+  yield waitForNetworkEvents(monitor, 8);
+  yield waitSomeTime();
+  is(requestsContainer.scrollTop, scrollTop, "Did not scroll.");
 
   // (3) Now set the scroll position back at the bottom and check that
   // additional requests *do* cause the container to scroll down.
-  .then(() => {
-    requestsContainer.scrollTop = requestsContainer.scrollHeight;
-    ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom.");
-    return waitForNetworkEvents(monitor, 8);
-  })
-  .then(() => {
-    ok(scrolledToBottom(requestsContainer), "Still scrolled to bottom.");
-  })
+  requestsContainer.scrollTop = requestsContainer.scrollHeight;
+  ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom.");
+  yield waitForNetworkEvents(monitor, 8);
+  yield waitForScroll(monitor);
+  ok(scrolledToBottom(requestsContainer), "Still scrolled to bottom.");
 
   // (4) Now select an item in the list and check that additional requests
   // do not change the scroll position.
-  .then(() => {
-    monitor.panelWin.NetMonitorView.RequestsMenu.selectedIndex = 0;
-    return waitForNetworkEvents(monitor, 8);
-  })
-  .then(() => {
-    is(requestsContainer.scrollTop, 0, "Did not scroll.");
-  })
+  monitor.panelWin.NetMonitorView.RequestsMenu.selectedIndex = 0;
+  yield waitForNetworkEvents(monitor, 8);
+  yield waitSomeTime();
+  is(requestsContainer.scrollTop, 0, "Did not scroll.");
 
-  // Done; clean up.
-  .then(() => {
-    return teardown(monitor).then(finish);
-  })
+  // Done: clean up.
+  yield teardown(monitor);
 
-  // Handle exceptions in the chain of promises.
-  .then(null, (err) => {
-    ok(false, err);
-    finish();
-  });
+  finish();
 
-  function waitForRequestsToOverflowContainer (aMonitor, aContainer) {
+  function waitForRequestsToOverflowContainer(aMonitor, aContainer) {
     return waitForNetworkEvents(aMonitor, 1).then(() => {
       if (aContainer.scrollHeight > aContainer.clientHeight) {
-        // Wait for some more just for good measure.
-        return waitForNetworkEvents(aMonitor, 8);
+        return promise.resolve();
       } else {
         return waitForRequestsToOverflowContainer(aMonitor, aContainer);
       }
     });
   }
 
   function scrolledToBottom(aElement) {
     return aElement.scrollTop + aElement.clientHeight >= aElement.scrollHeight;
   }
-}
+
+  function waitSomeTime() {
+    let waitSomeTime = promise.defer();
+    setTimeout(waitSomeTime.resolve, 50); // Wait to make sure no scrolls happen
+    return waitSomeTime.promise;
+  }
+
+  function waitForScroll(aMonitor) {
+    return aMonitor._view.RequestsMenu.widget.once("scroll-to-bottom");
+  }
+});
--- a/browser/devtools/shared/widgets/SideMenuWidget.jsm
+++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm
@@ -5,19 +5,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
+const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {});
 
 this.EXPORTED_SYMBOLS = ["SideMenuWidget"];
 
+const SCROLL_FREQUENCY = 16;
+
 /**
  * A simple side menu, with the ability of grouping menu items.
  *
  * Note: this widget should be used in tandem with the WidgetMethods in
  * ViewHelpers.jsm.
  *
  * @param nsIDOMNode aNode
  *        The element associated with the widget.
@@ -108,31 +111,85 @@ SideMenuWidget.prototype = {
     // needlessly expensive operations that may cause reflows):
     let maintainScrollAtBottom =
       // 1. The behavior should be enabled,
       this.autoscrollWithAppendedItems &&
       // 2. There shouldn't currently be any selected item in the list.
       !this._selectedItem &&
       // 3. The new item should be appended at the end of the list.
       (aIndex < 0 || aIndex >= this._orderedMenuElementsArray.length) &&
-      // 4. The list should already be scrolled at the bottom.
-      (this._list.scrollTop + this._list.clientHeight >= this._list.scrollHeight);
+      // 4. We aren't waiting for a scroll to happen.
+      (!this._scrollToBottomTask || !this._scrollToBottomTask.isArmed) &&
+      // 5. The list should already be scrolled at the bottom.
+      this.isScrolledToBottom();
 
     let group = this._getMenuGroupForName(aAttachment.group);
     let item = this._getMenuItemForGroup(group, aContents, aAttachment);
     let element = item.insertSelfAt(aIndex);
 
     if (maintainScrollAtBottom) {
-      this._list.scrollTop = this._list.scrollHeight;
+      this.scrollToBottom();
     }
 
     return element;
   },
 
   /**
+   * Checks to see if the list is scrolled all the way to the bottom.
+   * Uses getBoundsWithoutFlushing to limit the performance impact
+   * of this function.
+   *
+   * @return bool
+   */
+  isScrolledToBottom: function() {
+    if (this._list.lastElementChild) {
+      let utils = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindowUtils);
+      let childRect = utils.getBoundsWithoutFlushing(this._list.lastElementChild);
+      let listRect = utils.getBoundsWithoutFlushing(this._list);
+
+      // Cheap way to check if it's scrolled all the way to the bottom.
+      return (childRect.height + childRect.top) <= listRect.bottom;
+    }
+
+    return false;
+  },
+
+  /**
+   * Scroll the list to the bottom after a timeout.
+   * If the user scrolls in the meantime, cancel this operation.
+   */
+  scrollToBottom: function() {
+    // Lazily attach this functionality to the object, so it won't get
+    // created unless if this scrollToBottom behavior is needed.
+    if (!this._scrollToBottomTask) {
+      // The scroll event fires asynchronously, so we need to keep a bit to
+      // distinguish between user-initiated events and scrollTop assignment.
+      let ignoreNextScroll = false;
+
+      this._scrollToBottomTask = new DeferredTask(() => {
+        ignoreNextScroll = true;
+        this._list.scrollTop = this._list.scrollHeight;
+        this.emit("scroll-to-bottom");
+      }, SCROLL_FREQUENCY);
+
+      // On a user scroll, cancel any pending calls to the scroll function.
+      this._list.addEventListener("scroll", () => {
+        if (!ignoreNextScroll && this._scrollToBottomTask.isArmed &&
+            !this.isScrolledToBottom()) {
+          this._scrollToBottomTask.disarm();
+        }
+        ignoreNextScroll = false;
+      }, true);
+    }
+
+    this._scrollToBottomTask.arm();
+  },
+
+  /**
    * Returns the child node in this container situated at the specified index.
    *
    * @param number aIndex
    *        The position in the container intended for this item.
    * @return nsIDOMNode
    *         The element associated with the displayed item.
    */
   getItemAtIndex: function(aIndex) {
--- a/browser/locales/en-US/chrome/browser/devtools/inspector.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/inspector.properties
@@ -69,20 +69,20 @@ docsTooltip.loadDocsError=Could not load
 inspector.collapsePane=Collapse pane
 
 # LOCALIZATION NOTE (inspector.expandPane): This is the tooltip for the button
 # that expands the right panel (rules, computed, box-model, etc...) in the
 # inspector UI.
 inspector.expandPane=Expand pane
 
 # LOCALIZATION NOTE (inspector.searchResultsCount): This is the label that
-# will show up next to the inspector search box showing the current result
-# index alongside the total number of search results.  For example, "3 of 9".
+# will show up next to the inspector search box. %1$S is the current result
+# index and %2$S is the total number of search results. For example: "3 of 9".
 # This won't be visible until the search box is updated in Bug 835896.
-inspector.searchResultsCount=%S of %S
+inspector.searchResultsCount2=%1$S of %2$S
 
 # LOCALIZATION NOTE (inspector.searchResultsNone): This is the label that
 # will show up next to the inspector search box when no matches were found
 # for the given string.
 # This won't be visible until the search box is updated in Bug 835896.
 inspector.searchResultsNone=No matches
 
 # LOCALIZATION NOTE (inspector.menu.openUrlInNewTab.label): This is the label of
--- a/browser/locales/en-US/chrome/browser/newTab.properties
+++ b/browser/locales/en-US/chrome/browser/newTab.properties
@@ -38,10 +38,41 @@ newtab.enhanced.explain=A Mozilla partne
 # active link using string newtab.learn.link as text.
 newtab.intro.paragraph1=When you open a new tab, you’ll see tiles from the sites you frequently visit, along with tiles that we think might be of interest to you. Some of these tiles may be sponsored by Mozilla partners. We’ll always indicate to you which tiles are sponsored. %1$S
 # LOCALIZATION NOTE(newtab.intro.paragraph2): %1$S will be replaced inline by
 # active link using string newtab.privacy.link as text.
 newtab.intro.paragraph2=In order to provide this service, Mozilla collects and uses certain analytics information relating to your use of the tiles in accordance with our %1$S.
 # LOCALIZATION NOTE(newtab.intro.paragraph3): %1$S will be replaced inline by
 # the gear icon used to customize the new tab window.
 newtab.intro.paragraph3=You can turn off the tiles feature by clicking the %1$S button for your preferences.
+# LOCALIZATION NOTE(newtab.intro.paragraph4): %1$S will be replaced inline by
+# the gear icon used to customize the new tab window. %2$S will be replaced by
+# newtab.intro.controls as text
+newtab.intro.paragraph4=You can turn off this feature by clicking the gear (%1$S) button and selecting "Show blank page" in the %2$S menu.
+newtab.intro.paragraph5=New Tab will show the sites you visit most frequently, along with sites we think might be of interest to you. To get started, you'll see several sites from Mozilla.
+# LOCALIZATION NOTE(newtab.intro.paragraph6): %1$S will be replaced by
+# newtab.intro.paragraph6.remove as bold text. %2$S will be replaced by
+# newtab.intro.paragraph6.pin as bold text
+newtab.intro.paragraph6=You can %1$S or %2$S any site by using the controls available on rollover.
+newtab.intro.paragraph6.remove=remove
+newtab.intro.paragraph6.pin=pin
+newtab.intro.paragraph7=Some of the sites you will see may be suggested by Mozilla and may be sponsored by a Mozilla partner. We'll always indicate which sites are sponsored.
+# LOCALIZATION NOTE(newtab.intro.paragraph8): %1$S will be replaced by
+# brandShortName as text. %2$S will be replaced inline by an active link using
+# string newtab.learn.link as text.
+newtab.intro.paragraph8=%1$S will only show sites that most closely match your interests on the Web. %2$S
+newtab.intro.paragraph9=Now when you open New Tab, you'll also see sites we think might be interesting to you.
+# LOCALIZATION NOTE(newtab.intro.controls): the controls in the gear icon
+# menu for customizing the new tab window. Used in newtab.intro.paragraph4
+newtab.intro.controls=New Tab Controls
 newtab.learn.link=Learn more…
 newtab.privacy.link=Privacy Notice
+newtab.learn.link2=More about New Tab
+newtab.privacy.link2=About your privacy
+# LOCALIZATION NOTE(newtab.intro.header.welcome): %1$S will be replaced by
+# brandShortName as bold text.
+newtab.intro.header.welcome=Welcome to New Tab on %1$S!
+newtab.intro.header.update=New Tab got an update!
+newtab.intro.skip=Skip this
+newtab.intro.continue=Continue tour
+newtab.intro.back=Back
+newtab.intro.next=Next
+newtab.intro.gotit=Got it!
--- a/toolkit/content/contentAreaUtils.js
+++ b/toolkit/content/contentAreaUtils.js
@@ -140,30 +140,36 @@ function saveDocument(aDocument, aSkipPr
   try {
     contentDisposition =
       ifreq.getInterface(Components.interfaces.nsIDOMWindowUtils)
            .getDocumentMetadata("content-disposition");
   } catch (ex) {
     // Failure to get a content-disposition is ok
   }
 
-  var cacheKey = null;
+  let cacheKey = null;
   try {
-    cacheKey =
+    let shEntry =
       ifreq.getInterface(Components.interfaces.nsIWebNavigation)
-           .QueryInterface(Components.interfaces.nsIWebPageDescriptor);
+           .QueryInterface(Components.interfaces.nsIWebPageDescriptor)
+           .currentDescriptor
+           .QueryInterface(Components.interfaces.nsISHEntry);
+
+    shEntry.cacheKey.QueryInterface(Components.interfaces.nsISupportsPRUint32);
+
+    // In the event that the cacheKey is a CPOW, we cannot pass it to
+    // nsIWebBrowserPersist, so we create a new one and copy the value
+    // over. This is a workaround until bug 1101100 is fixed.
+    cacheKey = Cc["@mozilla.org/supports-PRUint32;1"]
+                 .createInstance(Ci.nsISupportsPRUint32);
+    cacheKey.data = shEntry.cacheKey.data;
   } catch (ex) {
     // We might not find it in the cache.  Oh, well.
   }
 
-  if (cacheKey && Components.utils.isCrossProcessWrapper(cacheKey)) {
-    // Don't use a cache key from another process. See bug 1128050.
-    cacheKey = null;
-  }
-
   internalSave(aDocument.location.href, aDocument, null, contentDisposition,
                aDocument.contentType, false, null, null,
                aDocument.referrer ? makeURI(aDocument.referrer) : null,
                aDocument, aSkipPrompt, cacheKey);
 }
 
 function DownloadListener(win, transfer) {
   function makeClosure(name) {
@@ -334,24 +340,27 @@ function internalSave(aURL, aDocument, a
     // If we should save as a complete page, the saveAsType is kSaveAsType_Complete.
     // If we should save as text, the saveAsType is kSaveAsType_Text.
     var useSaveDocument = aDocument &&
                           (((saveMode & SAVEMODE_COMPLETE_DOM) && (saveAsType == kSaveAsType_Complete)) ||
                            ((saveMode & SAVEMODE_COMPLETE_TEXT) && (saveAsType == kSaveAsType_Text)));
     // If we're saving a document, and are saving either in complete mode or
     // as converted text, pass the document to the web browser persist component.
     // If we're just saving the HTML (second option in the list), send only the URI.
+    let nonCPOWDocument =
+      aDocument && !Components.utils.isCrossProcessWrapper(aDocument);
+
     var persistArgs = {
       sourceURI         : sourceURI,
       sourceReferrer    : aReferrer,
       sourceDocument    : useSaveDocument ? aDocument : null,
       targetContentType : (saveAsType == kSaveAsType_Text) ? "text/plain" : null,
       targetFile        : file,
       sourceCacheKey    : aCacheKey,
-      sourcePostData    : aDocument ? getPostData(aDocument) : null,
+      sourcePostData    : nonCPOWDocument ? getPostData(aDocument) : null,
       bypassCache       : aShouldBypassCache,
       initiatingWindow  : aInitiatingDocument.defaultView
     };
 
     // Start the actual save process
     internalPersist(persistArgs);
   }
 }