mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Merge f-t to m-c, a=merge
This commit is contained in:
commit
7a8f8886f7
@ -11,80 +11,59 @@ thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: aValue.content is un
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@ -94,4 +73,14 @@ function test() {
|
||||
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");
|
||||
}
|
||||
});
|
||||
|
@ -10,9 +10,12 @@ 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.
|
||||
*
|
||||
@ -113,20 +116,74 @@ SideMenuWidget.prototype = {
|
||||
!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.
|
||||
*
|
||||
|
@ -74,10 +74,10 @@ inspector.collapsePane=Collapse pane
|
||||
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
|
||||
|
@ -43,5 +43,36 @@ newtab.intro.paragraph2=In order to provide this service, Mozilla collects and u
|
||||
# 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!
|
||||
|
@ -145,20 +145,26 @@ function saveDocument(aDocument, aSkipPrompt)
|
||||
// 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,
|
||||
@ -339,6 +345,9 @@ function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
|
||||
// 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,
|
||||
@ -346,7 +355,7 @@ function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
|
||||
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
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user