зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to m-c a=merge CLOSED TREE
This commit is contained in:
Коммит
f62801541d
|
@ -241,6 +241,8 @@ pref("lightweightThemes.update.enabled", true);
|
|||
pref("lightweightThemes.getMoreURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes");
|
||||
pref("lightweightThemes.recommendedThemes", "[{\"id\":\"recommended-1\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/a-web-browser-renaissance/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.header.jpg\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.footer.jpg\",\"textcolor\":\"#000000\",\"accentcolor\":\"#f2d9b1\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.preview.jpg\",\"author\":\"Sean.Martell\",\"version\":\"0\"},{\"id\":\"recommended-2\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/space-fantasy/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.header.jpg\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.footer.jpg\",\"textcolor\":\"#ffffff\",\"accentcolor\":\"#d9d9d9\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.preview.jpg\",\"author\":\"fx5800p\",\"version\":\"1.0\"},{\"id\":\"recommended-3\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/linen-light/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.footer.png\",\"textcolor\":\"#None\",\"accentcolor\":\"#ada8a8\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.preview.png\",\"author\":\"DVemer\",\"version\":\"1.0\"},{\"id\":\"recommended-4\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/pastel-gradient/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.footer.png\",\"textcolor\":\"#000000\",\"accentcolor\":\"#000000\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.preview.png\",\"author\":\"darrinhenein\",\"version\":\"1.0\"},{\"id\":\"recommended-5\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/carbon-light/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.footer.png\",\"textcolor\":\"#3b3b3b\",\"accentcolor\":\"#2e2e2e\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.preview.jpg\",\"author\":\"Jaxivo\",\"version\":\"1.0\"}]");
|
||||
|
||||
pref("browser.eme.ui.enabled", false);
|
||||
|
||||
// UI tour experience.
|
||||
pref("browser.uitour.enabled", true);
|
||||
pref("browser.uitour.loglevel", "Error");
|
||||
|
|
|
@ -511,12 +511,15 @@ let AboutReaderListener = {
|
|||
}
|
||||
|
||||
ReaderMode.parseDocument(content.document).then(article => {
|
||||
// Do nothing if there is no article, or if the content window has been destroyed.
|
||||
if (article === null || content === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The loaded page may have changed while we were parsing the document.
|
||||
// Make sure we've got the current one.
|
||||
let currentURL = Services.io.newURI(content.document.documentURI, null, null).specIgnoringRef;
|
||||
|
||||
// Do nothing if there's no article or the page in this tab has changed.
|
||||
if (article == null || (article.url != currentURL)) {
|
||||
if (article.url !== currentURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -487,3 +487,6 @@ skip-if = e10s # bug 1084504 - [e10s] Mixed content detection does not take redi
|
|||
[browser_windowactivation.js]
|
||||
[browser_contextmenu_childprocess.js]
|
||||
[browser_bug963945.js]
|
||||
[browser_readerMode.js]
|
||||
support-files =
|
||||
readerModeArticle.html
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/* 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/. */
|
||||
|
||||
// Test that the reader mode button appears and works properly on reader-able content.
|
||||
|
||||
const PREF = "reader.parse-on-load.enabled";
|
||||
|
||||
const TEST_PATH = "http://example.com/browser/browser/base/content/test/general/";
|
||||
|
||||
let readerButton = document.getElementById("reader-mode-button");
|
||||
|
||||
add_task(function* () {
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref(PREF);
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
});
|
||||
|
||||
// Enable the reader mode button.
|
||||
Services.prefs.setBoolPref(PREF, true);
|
||||
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
is_element_hidden(readerButton, "Reader mode button is not present on a new tab");
|
||||
|
||||
// Point tab to a test page that is reader-able.
|
||||
let url = TEST_PATH + "readerModeArticle.html";
|
||||
yield promiseTabLoadEvent(tab, url);
|
||||
yield promiseWaitForCondition(() => !readerButton.hidden);
|
||||
is_element_visible(readerButton, "Reader mode button is present on a reader-able page");
|
||||
|
||||
readerButton.click();
|
||||
yield promiseTabLoadEvent(tab);
|
||||
|
||||
ok(gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"), "about:reader loaded after clicking reader mode button");
|
||||
is_element_visible(readerButton, "Reader mode button is present on about:reader");
|
||||
|
||||
readerButton.click();
|
||||
yield promiseTabLoadEvent(tab);
|
||||
is(gBrowser.selectedBrowser.currentURI.spec, url, "Original page loaded after clicking active reader mode button");
|
||||
|
||||
// Load a new tab that is NOT reader-able.
|
||||
let newTab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
yield promiseTabLoadEvent(newTab, TEST_PATH + "download_page.html");
|
||||
yield promiseWaitForCondition(() => readerButton.hidden);
|
||||
is_element_hidden(readerButton, "Reader mode button is not present on a non-reader-able page");
|
||||
|
||||
// Switch back to the original tab to make sure reader mode button is still visible.
|
||||
gBrowser.removeCurrentTab();
|
||||
yield promiseWaitForCondition(() => !readerButton.hidden);
|
||||
is_element_visible(readerButton, "Reader mode button is present on a reader-able page");
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Article title</title>
|
||||
<meta name="description" content="This is the article description." />
|
||||
</head>
|
||||
<body>
|
||||
<header>Site header</header>
|
||||
<div>
|
||||
<h1>Article title</h1>
|
||||
<h2 class="author">by Jane Doe</h2>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.</p>
|
||||
<p>Vivamus fermentum semper porta. Nunc diam velit, adipiscing ut tristique vitae, sagittis vel odio. Maecenas convallis ullamcorper ultricies. Curabitur ornare, ligula semper consectetur sagittis, nisi diam iaculis velit, id fringilla sem nunc vel mi. Nam dictum, odio nec pretium volutpat, arcu ante placerat erat, non tristique elit urna et turpis. Quisque mi metus, ornare sit amet fermentum et, tincidunt et orci. Fusce eget orci a orci congue vestibulum. Ut dolor diam, elementum et vestibulum eu, porttitor vel elit. Curabitur venenatis pulvinar tellus gravida ornare. Sed et erat faucibus nunc euismod ultricies ut id justo. Nullam cursus suscipit nisi, et ultrices justo sodales nec. Fusce venenatis facilisis lectus ac semper. Aliquam at massa ipsum. Quisque bibendum purus convallis nulla ultrices ultricies. Nullam aliquam, mi eu aliquam tincidunt, purus velit laoreet tortor, viverra pretium nisi quam vitae mi. Fusce vel volutpat elit. Nam sagittis nisi dui.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -4,12 +4,9 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var gContentPane = {
|
||||
|
||||
/**
|
||||
* Initializes the fonts dropdowns displayed in this pane.
|
||||
*/
|
||||
init: function ()
|
||||
{
|
||||
// Initializes the fonts dropdowns displayed in this pane.
|
||||
this._rebuildFonts();
|
||||
var menulist = document.getElementById("defaultFont");
|
||||
if (menulist.selectedIndex == -1) {
|
||||
|
@ -23,6 +20,12 @@ var gContentPane = {
|
|||
let row = document.getElementById("translationBox");
|
||||
row.removeAttribute("hidden");
|
||||
}
|
||||
|
||||
let drmInfoURL =
|
||||
Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
|
||||
document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);
|
||||
document.getElementById("playDRMContentRow").hidden =
|
||||
!Services.prefs.getBoolPref("browser.eme.ui.enabled");
|
||||
},
|
||||
|
||||
// UTILITY FUNCTIONS
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
<preferences id="contentPreferences">
|
||||
<!--XXX buttons prefs -->
|
||||
|
||||
<!-- DRM content -->
|
||||
<preference id="media.eme.enabled" name="media.eme.enabled" type="bool"/>
|
||||
|
||||
<!-- POPUPS -->
|
||||
<preference id="dom.disable_open_during_load" name="dom.disable_open_during_load" type="bool"/>
|
||||
|
||||
|
@ -50,6 +53,15 @@
|
|||
<column/>
|
||||
</columns>
|
||||
<rows id="contentRows-1">
|
||||
<row id="playDRMContentRow">
|
||||
<vbox align="start">
|
||||
<checkbox id="playDRMContent" preference="media.eme.enabled"
|
||||
label="&playDRMContent.label;" accesskey="&playDRMContent.accesskey;"/>
|
||||
</vbox>
|
||||
<hbox pack="end">
|
||||
<label id="playDRMContentLink" class="text-link" value="&playDRMContent.learnMore.label;"/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row id="popupPolicyRow">
|
||||
<vbox align="start">
|
||||
<checkbox id="popupPolicy" preference="dom.disable_open_during_load"
|
||||
|
@ -57,9 +69,11 @@
|
|||
onsyncfrompreference="return gContentPane.updateButtons('popupPolicyButton',
|
||||
'dom.disable_open_during_load');"/>
|
||||
</vbox>
|
||||
<button id="popupPolicyButton" label="&popupExceptions.label;"
|
||||
oncommand="gContentPane.showPopupExceptions();"
|
||||
accesskey="&popupExceptions.accesskey;"/>
|
||||
<hbox pack="end">
|
||||
<button id="popupPolicyButton" label="&popupExceptions.label;"
|
||||
oncommand="gContentPane.showPopupExceptions();"
|
||||
accesskey="&popupExceptions.accesskey;"/>
|
||||
</hbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
|
|
|
@ -1506,16 +1506,21 @@ var gApplicationsPane = {
|
|||
}
|
||||
|
||||
// Create a menu item for selecting a local application.
|
||||
let canOpenWithOtherApp = true;
|
||||
#ifdef XP_WIN
|
||||
// On Windows, selecting an application to open another application
|
||||
// would be meaningless so we special case executables.
|
||||
var executableType = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService)
|
||||
let executableType = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService)
|
||||
.getTypeFromExtension("exe");
|
||||
if (handlerInfo.type != executableType)
|
||||
canOpenWithOtherApp = handlerInfo.type != executableType;
|
||||
#endif
|
||||
if (canOpenWithOtherApp)
|
||||
{
|
||||
let menuItem = document.createElement("menuitem");
|
||||
menuItem.setAttribute("oncommand", "gApplicationsPane.chooseApp(event)");
|
||||
menuItem.className = "choose-app-item";
|
||||
menuItem.addEventListener("command", function(e) {
|
||||
gApplicationsPane.chooseApp(e);
|
||||
});
|
||||
let label = this._prefsBundle.getString("useOtherApp");
|
||||
menuItem.setAttribute("label", label);
|
||||
menuItem.setAttribute("tooltiptext", label);
|
||||
|
@ -1527,7 +1532,10 @@ var gApplicationsPane = {
|
|||
let menuItem = document.createElement("menuseparator");
|
||||
menuPopup.appendChild(menuItem);
|
||||
menuItem = document.createElement("menuitem");
|
||||
menuItem.setAttribute("oncommand", "gApplicationsPane.manageApp(event)");
|
||||
menuItem.className = "manage-app-item";
|
||||
menuItem.addEventListener("command", function(e) {
|
||||
gApplicationsPane.manageApp(e);
|
||||
});
|
||||
menuItem.setAttribute("label", this._prefsBundle.getString("manageApp"));
|
||||
menuPopup.appendChild(menuItem);
|
||||
}
|
||||
|
@ -1700,20 +1708,23 @@ var gApplicationsPane = {
|
|||
var typeItem = this._list.selectedItem;
|
||||
var handlerInfo = this._handledTypes[typeItem.type];
|
||||
|
||||
let onComplete = () => {
|
||||
// Rebuild the actions menu so that we revert to the previous selection,
|
||||
// or "Always ask" if the previous default application has been removed
|
||||
this.rebuildActionsMenu();
|
||||
|
||||
// update the richlistitem too. Will be visible when selecting another row
|
||||
typeItem.setAttribute("actionDescription",
|
||||
this._describePreferredAction(handlerInfo));
|
||||
if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
|
||||
typeItem.setAttribute("actionIcon",
|
||||
this._getIconURLForPreferredAction(handlerInfo));
|
||||
}
|
||||
};
|
||||
|
||||
gSubDialog.open("chrome://browser/content/preferences/applicationManager.xul",
|
||||
"resizable=no", handlerInfo);
|
||||
"resizable=no", handlerInfo, onComplete);
|
||||
|
||||
// Rebuild the actions menu so that we revert to the previous selection,
|
||||
// or "Always ask" if the previous default application has been removed
|
||||
this.rebuildActionsMenu();
|
||||
|
||||
// update the richlistitem too. Will be visible when selecting another row
|
||||
typeItem.setAttribute("actionDescription",
|
||||
this._describePreferredAction(handlerInfo));
|
||||
if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
|
||||
typeItem.setAttribute("actionIcon",
|
||||
this._getIconURLForPreferredAction(handlerInfo));
|
||||
}
|
||||
},
|
||||
|
||||
chooseApp: function(aEvent) {
|
||||
|
@ -1762,17 +1773,20 @@ var gApplicationsPane = {
|
|||
params.filename = null;
|
||||
params.handlerApp = null;
|
||||
|
||||
let onAppSelected = () => {
|
||||
if (this.isValidHandlerApp(params.handlerApp)) {
|
||||
handlerApp = params.handlerApp;
|
||||
|
||||
// Add the app to the type's list of possible handlers.
|
||||
handlerInfo.addPossibleApplicationHandler(handlerApp);
|
||||
}
|
||||
|
||||
chooseAppCallback(handlerApp);
|
||||
};
|
||||
|
||||
gSubDialog.open("chrome://global/content/appPicker.xul",
|
||||
null, params);
|
||||
null, params, onAppSelected);
|
||||
|
||||
if (this.isValidHandlerApp(params.handlerApp)) {
|
||||
handlerApp = params.handlerApp;
|
||||
|
||||
// Add the app to the type's list of possible handlers.
|
||||
handlerInfo.addPossibleApplicationHandler(handlerApp);
|
||||
}
|
||||
|
||||
chooseAppCallback(handlerApp);
|
||||
#else
|
||||
let winTitle = this._prefsBundle.getString("fpTitleChooseApp");
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var gContentPane = {
|
||||
|
||||
/**
|
||||
* Initializes the fonts dropdowns displayed in this pane.
|
||||
*/
|
||||
init: function ()
|
||||
{
|
||||
function setEventListener(aId, aEventType, aCallback)
|
||||
|
@ -15,6 +11,7 @@ var gContentPane = {
|
|||
.addEventListener(aEventType, aCallback.bind(gContentPane));
|
||||
}
|
||||
|
||||
// Initializes the fonts dropdowns displayed in this pane.
|
||||
this._rebuildFonts();
|
||||
var menulist = document.getElementById("defaultFont");
|
||||
if (menulist.selectedIndex == -1) {
|
||||
|
@ -43,6 +40,12 @@ var gContentPane = {
|
|||
gContentPane.openTranslationProviderAttribution);
|
||||
setEventListener("translateButton", "command",
|
||||
gContentPane.showTranslationExceptions);
|
||||
|
||||
let drmInfoURL =
|
||||
Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
|
||||
document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);
|
||||
document.getElementById("playDRMContentRow").hidden =
|
||||
!Services.prefs.getBoolPref("browser.eme.ui.enabled");
|
||||
},
|
||||
|
||||
// UTILITY FUNCTIONS
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
<preferences id="contentPreferences" hidden="true" data-category="paneContent">
|
||||
|
||||
<!-- DRM content -->
|
||||
<preference id="media.eme.enabled"
|
||||
name="media.eme.enabled"
|
||||
type="bool"/>
|
||||
|
||||
<!-- Popups -->
|
||||
<preference id="dom.disable_open_during_load"
|
||||
name="dom.disable_open_during_load"
|
||||
|
@ -35,14 +40,21 @@
|
|||
</hbox>
|
||||
|
||||
<groupbox id="miscGroup" data-category="paneContent" hidden="true">
|
||||
<caption><label>&popups.label;</label></caption>
|
||||
|
||||
<grid id="contentGrid">
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows id="contentRows-1">
|
||||
<row id="playDRMContentRow">
|
||||
<vbox align="start">
|
||||
<checkbox id="playDRMContent" preference="media.eme.enabled"
|
||||
label="&playDRMContent.label;" accesskey="&playDRMContent.accesskey;"/>
|
||||
</vbox>
|
||||
<hbox pack="end">
|
||||
<label id="playDRMContentLink" class="text-link" value="&playDRMContent.learnMore.label;"/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row id="popupPolicyRow">
|
||||
<vbox align="start">
|
||||
<checkbox id="popupPolicy" preference="dom.disable_open_during_load"
|
||||
|
@ -50,8 +62,10 @@
|
|||
onsyncfrompreference="return gContentPane.updateButtons('popupPolicyButton',
|
||||
'dom.disable_open_during_load');"/>
|
||||
</vbox>
|
||||
<button id="popupPolicyButton" label="&popupExceptions.label;"
|
||||
accesskey="&popupExceptions.accesskey;"/>
|
||||
<hbox pack="end">
|
||||
<button id="popupPolicyButton" label="&popupExceptions.label;"
|
||||
accesskey="&popupExceptions.accesskey;"/>
|
||||
</hbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
|
|
|
@ -10,6 +10,8 @@ support-files =
|
|||
[browser_bug795764_cachedisabled.js]
|
||||
[browser_bug1018066_resetScrollPosition.js]
|
||||
[browser_bug1020245_openPreferences_to_paneContent.js]
|
||||
[browser_change_app_handler.js]
|
||||
run-if = os == "win" # This test tests the windows-specific app selection dialog, so can't run on non-Windows
|
||||
[browser_connection.js]
|
||||
[browser_connection_bug388287.js]
|
||||
[browser_healthreport.js]
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
let gMimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
||||
let gHandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService);
|
||||
|
||||
function setupFakeHandler() {
|
||||
let info = gMimeSvc.getFromTypeAndExtension("text/plain", "foo.txt");
|
||||
ok(info.possibleLocalHandlers.length, "Should have at least one known handler");
|
||||
let handler = info.possibleLocalHandlers.queryElementAt(0, Ci.nsILocalHandlerApp);
|
||||
|
||||
let infoToModify = gMimeSvc.getFromTypeAndExtension("text/x-test-handler", null);
|
||||
infoToModify.possibleApplicationHandlers.appendElement(handler, false);
|
||||
|
||||
gHandlerSvc.store(infoToModify);
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
setupFakeHandler();
|
||||
yield openPreferencesViaOpenPreferencesAPI("applications", null, {leaveOpen: true});
|
||||
info("Preferences page opened on the applications pane.");
|
||||
let win = gBrowser.selectedBrowser.contentWindow;
|
||||
|
||||
let container = win.document.getElementById("handlersView");
|
||||
let ourItem = container.querySelector("richlistitem[type='text/x-test-handler']");
|
||||
ok(ourItem, "handlersView is present");
|
||||
ourItem.scrollIntoView();
|
||||
container.selectItem(ourItem);
|
||||
ok(ourItem.selected, "Should be able to select our item.");
|
||||
|
||||
let list = yield waitForCondition(() => win.document.getAnonymousElementByAttribute(ourItem, "class", "actionsMenu"));
|
||||
info("Got list after item was selected");
|
||||
|
||||
let chooseItem = list.firstChild.querySelector(".choose-app-item");
|
||||
let dialogLoadedPromise = promiseLoadSubDialog("chrome://global/content/appPicker.xul");
|
||||
chooseItem.click();
|
||||
|
||||
let dialog = yield dialogLoadedPromise;
|
||||
info("Dialog loaded");
|
||||
|
||||
let dialogDoc = dialog.document;
|
||||
let dialogList = dialogDoc.getElementById("app-picker-listbox");
|
||||
dialogList.selectItem(dialogList.firstChild);
|
||||
let selectedApp = dialogList.firstChild.handlerApp;
|
||||
dialogDoc.documentElement.acceptDialog();
|
||||
|
||||
// Verify results are correct in mime service:
|
||||
let mimeInfo = gMimeSvc.getFromTypeAndExtension("text/x-test-handler", null);
|
||||
ok(mimeInfo.preferredApplicationHandler.equals(selectedApp), "App should be set as preferred.");
|
||||
|
||||
// Check that we display this result:
|
||||
list = yield waitForCondition(() => win.document.getAnonymousElementByAttribute(ourItem, "class", "actionsMenu"));
|
||||
info("Got list after item was selected");
|
||||
ok(list.selectedItem, "Should have a selected item");
|
||||
ok(mimeInfo.preferredApplicationHandler.equals(list.selectedItem.handlerApp),
|
||||
"App should be visible as preferred item.");
|
||||
|
||||
|
||||
// Now try to 'manage' this list:
|
||||
dialogLoadedPromise = promiseLoadSubDialog("chrome://browser/content/preferences/applicationManager.xul");
|
||||
|
||||
let manageItem = list.firstChild.querySelector(".manage-app-item");
|
||||
manageItem.click();
|
||||
|
||||
dialog = yield dialogLoadedPromise;
|
||||
info("Dialog loaded the second time");
|
||||
|
||||
dialogDoc = dialog.document;
|
||||
dialogList = dialogDoc.getElementById("appList");
|
||||
let itemToRemove = dialogList.querySelector('listitem[label="' + selectedApp.name + '"]');
|
||||
dialogList.selectItem(itemToRemove);
|
||||
let itemsBefore = dialogList.children.length;
|
||||
dialogDoc.getElementById("remove").click();
|
||||
ok(!itemToRemove.parentNode, "Item got removed from DOM");
|
||||
is(dialogList.children.length, itemsBefore - 1, "Item got removed");
|
||||
dialogDoc.documentElement.acceptDialog();
|
||||
|
||||
// Verify results are correct in mime service:
|
||||
mimeInfo = gMimeSvc.getFromTypeAndExtension("text/x-test-handler", null);
|
||||
ok(!mimeInfo.preferredApplicationHandler, "App should no longer be set as preferred.");
|
||||
|
||||
// Check that we display this result:
|
||||
list = yield waitForCondition(() => win.document.getAnonymousElementByAttribute(ourItem, "class", "actionsMenu"));
|
||||
ok(list.selectedItem, "Should have a selected item");
|
||||
ok(!list.selectedItem.handlerApp,
|
||||
"No app should be visible as preferred item.");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
let infoToModify = gMimeSvc.getFromTypeAndExtension("text/x-test-handler", null);
|
||||
gHandlerSvc.remove(infoToModify);
|
||||
});
|
||||
|
|
@ -39,35 +39,38 @@ function open_preferences(aCallback) {
|
|||
}
|
||||
|
||||
function openAndLoadSubDialog(aURL, aFeatures = null, aParams = null, aClosingCallback = null) {
|
||||
let dialog = content.gSubDialog.open(aURL, aFeatures, aParams, aClosingCallback);
|
||||
let deferred = Promise.defer();
|
||||
let promise = promiseLoadSubDialog(aURL);
|
||||
content.gSubDialog.open(aURL, aFeatures, aParams, aClosingCallback);
|
||||
return promise;
|
||||
}
|
||||
|
||||
content.gSubDialog._frame.addEventListener("load", function load(aEvent) {
|
||||
if (aEvent.target.contentWindow.location == "about:blank")
|
||||
return;
|
||||
content.gSubDialog._frame.removeEventListener("load", load);
|
||||
function promiseLoadSubDialog(aURL) {
|
||||
return new Promise((resolve, reject) => {
|
||||
content.gSubDialog._frame.addEventListener("load", function load(aEvent) {
|
||||
if (aEvent.target.contentWindow.location == "about:blank")
|
||||
return;
|
||||
content.gSubDialog._frame.removeEventListener("load", load);
|
||||
|
||||
ise(content.gSubDialog._frame.contentWindow.location.toString(), aURL,
|
||||
"Check the proper URL is loaded");
|
||||
ise(content.gSubDialog._frame.contentWindow.location.toString(), aURL,
|
||||
"Check the proper URL is loaded");
|
||||
|
||||
// Check visibility
|
||||
is_element_visible(content.gSubDialog._overlay, "Overlay is visible");
|
||||
// Check visibility
|
||||
is_element_visible(content.gSubDialog._overlay, "Overlay is visible");
|
||||
|
||||
// Check that stylesheets were injected
|
||||
let expectedStyleSheetURLs = content.gSubDialog._injectedStyleSheets.slice(0);
|
||||
for (let styleSheet of content.gSubDialog._frame.contentDocument.styleSheets) {
|
||||
let i = expectedStyleSheetURLs.indexOf(styleSheet.href);
|
||||
if (i >= 0) {
|
||||
info("found " + styleSheet.href);
|
||||
expectedStyleSheetURLs.splice(i, 1);
|
||||
// Check that stylesheets were injected
|
||||
let expectedStyleSheetURLs = content.gSubDialog._injectedStyleSheets.slice(0);
|
||||
for (let styleSheet of content.gSubDialog._frame.contentDocument.styleSheets) {
|
||||
let i = expectedStyleSheetURLs.indexOf(styleSheet.href);
|
||||
if (i >= 0) {
|
||||
info("found " + styleSheet.href);
|
||||
expectedStyleSheetURLs.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
ise(expectedStyleSheetURLs.length, 0, "All expectedStyleSheetURLs should have been found");
|
||||
ise(expectedStyleSheetURLs.length, 0, "All expectedStyleSheetURLs should have been found");
|
||||
|
||||
deferred.resolve(dialog);
|
||||
resolve(content.gSubDialog._frame.contentWindow);
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,3 +142,24 @@ function openPreferencesViaOpenPreferencesAPI(aPane, aAdvancedTab, aOptions) {
|
|||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function tryNow() {
|
||||
tries++;
|
||||
let rv = aConditionFn();
|
||||
if (rv) {
|
||||
resolve(rv);
|
||||
} else if (tries < aMaxTries) {
|
||||
tryAgain();
|
||||
} else {
|
||||
reject("Condition timed out: " + aConditionFn.toSource());
|
||||
}
|
||||
}
|
||||
function tryAgain() {
|
||||
setTimeout(tryNow, aCheckInterval);
|
||||
}
|
||||
let tries = 0;
|
||||
tryAgain();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ function test() {
|
|||
// public session, add new tab: (A)
|
||||
let tab_A = aWin.gBrowser.addTab(testURL);
|
||||
ss.setTabState(tab_A, JSON.stringify(state));
|
||||
whenBrowserLoaded(tab_A.linkedBrowser, function() {
|
||||
promiseBrowserLoaded(tab_A.linkedBrowser).then(() => {
|
||||
// make sure that the next closed tab will increase getClosedTabCount
|
||||
Services.prefs.setIntPref(
|
||||
"browser.sessionstore.max_tabs_undo", max_tabs_undo + 1)
|
||||
|
@ -119,7 +119,7 @@ function test() {
|
|||
// verify tab: (A), in undo list
|
||||
let tab_A_restored = test(function() ss.undoCloseTab(aWin, 0));
|
||||
ok(tab_A_restored, "a tab is in undo list");
|
||||
whenTabRestored(tab_A_restored, function() {
|
||||
promiseTabRestored(tab_A_restored).then(() => {
|
||||
is(testURL, tab_A_restored.linkedBrowser.currentURI.spec,
|
||||
"it's the same tab that we expect");
|
||||
aWin.gBrowser.removeTab(tab_A_restored);
|
||||
|
@ -136,15 +136,14 @@ function test() {
|
|||
};
|
||||
|
||||
let tab_B = aWin.gBrowser.addTab(testURL2);
|
||||
ss.setTabState(tab_B, JSON.stringify(state1));
|
||||
whenTabRestored(tab_B, function() {
|
||||
promiseTabState(tab_B, state1).then(() => {
|
||||
// populate tab: (B) with different form data
|
||||
for (let item in fieldList)
|
||||
setFormValue(tab_B, item, fieldList[item]);
|
||||
|
||||
// duplicate tab: (B)
|
||||
let tab_C = aWin.gBrowser.duplicateTab(tab_B);
|
||||
whenTabRestored(tab_C, function() {
|
||||
promiseTabRestored(tab_C).then(() => {
|
||||
// verify the correctness of the duplicated tab
|
||||
is(ss.getTabValue(tab_C, key1), value1,
|
||||
"tab successfully duplicated - correct state");
|
||||
|
|
|
@ -11,13 +11,13 @@ function test() {
|
|||
"browser/components/sessionstore/test/browser_339445_sample.html";
|
||||
|
||||
let tab = gBrowser.addTab(testURL);
|
||||
whenBrowserLoaded(tab.linkedBrowser, function() {
|
||||
promiseBrowserLoaded(tab.linkedBrowser).then(() => {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
is(doc.getElementById("storageTestItem").textContent, "PENDING",
|
||||
"sessionStorage value has been set");
|
||||
|
||||
let tab2 = gBrowser.duplicateTab(tab);
|
||||
whenTabRestored(tab2, function() {
|
||||
promiseTabRestored(tab2).then(() => {
|
||||
let doc2 = tab2.linkedBrowser.contentDocument;
|
||||
is(doc2.getElementById("storageTestItem").textContent, "SUCCESS",
|
||||
"sessionStorage value has been duplicated");
|
||||
|
|
|
@ -71,7 +71,7 @@ function test() {
|
|||
// create a new tab
|
||||
let testURL = "about:";
|
||||
tab = gBrowser.addTab(testURL);
|
||||
whenBrowserLoaded(tab.linkedBrowser, function() {
|
||||
promiseBrowserLoaded(tab.linkedBrowser).then(() => {
|
||||
// make sure that the next closed tab will increase getClosedTabCount
|
||||
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
|
||||
|
||||
|
@ -86,7 +86,7 @@ function test() {
|
|||
tab = test(function() ss.undoCloseTab(window, 0));
|
||||
ok(tab, "undoCloseTab doesn't throw")
|
||||
|
||||
whenTabRestored(tab, function() {
|
||||
promiseTabRestored(tab).then(() => {
|
||||
is(tab.linkedBrowser.currentURI.spec, testURL, "correct tab was reopened");
|
||||
|
||||
// clean up
|
||||
|
|
|
@ -14,12 +14,11 @@ function test() {
|
|||
|
||||
// restore a blank tab
|
||||
let tab = gBrowser.addTab("about:");
|
||||
whenBrowserLoaded(tab.linkedBrowser, function() {
|
||||
promiseBrowserLoaded(tab.linkedBrowser).then(() => {
|
||||
let history = tab.linkedBrowser.webNavigation.sessionHistory;
|
||||
ok(history.count >= 1, "the new tab does have at least one history entry");
|
||||
|
||||
ss.setTabState(tab, JSON.stringify({ entries: [] }));
|
||||
whenTabRestored(tab, function() {
|
||||
promiseTabState(tab, {entries: []}).then(() => {
|
||||
// We may have a different sessionHistory object if the tab
|
||||
// switched from non-remote to remote.
|
||||
history = tab.linkedBrowser.webNavigation.sessionHistory;
|
||||
|
|
|
@ -20,9 +20,7 @@ function test() {
|
|||
let uniqueText = "pi != " + Math.random();
|
||||
|
||||
// Clear the list of closed windows.
|
||||
while (SessionStore.getClosedWindowCount()) {
|
||||
SessionStore.forgetClosedWindow(0);
|
||||
}
|
||||
forgetClosedWindows();
|
||||
|
||||
provideWindow(function onTestURLLoaded(newWin) {
|
||||
newWin.gBrowser.addTab().linkedBrowser.stop();
|
||||
|
|
|
@ -84,9 +84,7 @@ function promiseBlankState() {
|
|||
}
|
||||
|
||||
add_task(function* init() {
|
||||
while (ss.getClosedWindowCount() > 0) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
forgetClosedWindows();
|
||||
while (ss.getClosedTabCount(window) > 0) {
|
||||
ss.forgetClosedTab(window, 0);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ function test() {
|
|||
executeSoon(function() {
|
||||
newWin.gBrowser.loadURI(testURL, null, null);
|
||||
|
||||
whenBrowserLoaded(newWin.gBrowser.selectedBrowser, function() {
|
||||
promiseBrowserLoaded(newWin.gBrowser.selectedBrowser).then(() => {
|
||||
// get the sessionstore state for the window
|
||||
TabState.flush(newWin.gBrowser.selectedBrowser);
|
||||
let state = ss.getWindowState(newWin);
|
||||
|
|
|
@ -18,14 +18,13 @@ function test() {
|
|||
});
|
||||
|
||||
let tab = gBrowser.addTab();
|
||||
whenBrowserLoaded(tab.linkedBrowser, function() {
|
||||
promiseBrowserLoaded(tab.linkedBrowser).then(() => {
|
||||
let tabState = { entries: [] };
|
||||
let max_entries = gPrefService.getIntPref("browser.sessionhistory.max_entries");
|
||||
for (let i = 0; i < max_entries; i++)
|
||||
tabState.entries.push({ url: baseURL + i });
|
||||
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
whenTabRestored(tab, function() {
|
||||
promiseTabState(tab, tabState).then(() => {
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
tabState = JSON.parse(ss.getTabState(tab));
|
||||
is(tabState.entries.length, max_entries, "session history filled to the limit");
|
||||
|
|
|
@ -17,15 +17,13 @@ add_task(function test_check_urls_before_restoring() {
|
|||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
// Restore form data with a valid URL.
|
||||
ss.setTabState(tab, getState(URL));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, getState(URL));
|
||||
|
||||
let value = yield getInputValue(browser, {id: "text"});
|
||||
is(value, "foobar", "value was restored");
|
||||
|
||||
// Restore form data with an invalid URL.
|
||||
ss.setTabState(tab, getState("http://example.com/"));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, getState("http://example.com/"));
|
||||
|
||||
value = yield getInputValue(browser, {id: "text"});
|
||||
is(value, "", "value was not restored");
|
||||
|
|
|
@ -32,7 +32,7 @@ function test() {
|
|||
typeText(doc.defaultView.frames[0].frames[1].document.getElementById("in1"), new Date());
|
||||
|
||||
let tab2 = gBrowser.duplicateTab(tab);
|
||||
whenTabRestored(tab2, function() {
|
||||
promiseTabRestored(tab2).then(() => {
|
||||
let doc = tab2.linkedBrowser.contentDocument;
|
||||
let win = tab2.linkedBrowser.contentWindow;
|
||||
isnot(doc.getElementById("out1").value,
|
||||
|
|
|
@ -13,7 +13,7 @@ function test() {
|
|||
|
||||
// set a unique value on a new, blank tab
|
||||
let tab1 = gBrowser.addTab();
|
||||
whenBrowserLoaded(tab1.linkedBrowser, function() {
|
||||
promiseBrowserLoaded(tab1.linkedBrowser).then(() => {
|
||||
ss.setTabValue(tab1, uniqueName, uniqueValue1);
|
||||
|
||||
// duplicate the tab with that value
|
||||
|
@ -24,8 +24,7 @@ function test() {
|
|||
isnot(ss.getTabValue(tab1, uniqueName), uniqueValue2, "tab values aren't sync'd");
|
||||
|
||||
// overwrite the tab with the value which should remove it
|
||||
ss.setTabState(tab1, JSON.stringify({ entries: [] }));
|
||||
whenTabRestored(tab1, function() {
|
||||
promiseTabState(tab1, {entries: []}).then(() => {
|
||||
is(ss.getTabValue(tab1, uniqueName), "", "tab value was cleared");
|
||||
|
||||
// clean up
|
||||
|
|
|
@ -37,18 +37,15 @@ add_task(function test_nested_about_sessionrestore() {
|
|||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
// test 1
|
||||
ss.setTabState(tab, JSON.stringify(STATE));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, STATE);
|
||||
checkState("test1", tab);
|
||||
|
||||
// test 2
|
||||
ss.setTabState(tab, JSON.stringify(STATE2));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, STATE2);
|
||||
checkState("test2", tab);
|
||||
|
||||
// test 3
|
||||
ss.setTabState(tab, JSON.stringify(STATE3));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, STATE3);
|
||||
checkState("test3", tab);
|
||||
|
||||
// Cleanup.
|
||||
|
|
|
@ -13,12 +13,12 @@ function test() {
|
|||
let tab = gBrowser.addTab("about:sessionrestore");
|
||||
gBrowser.selectedTab = tab;
|
||||
let browser = tab.linkedBrowser;
|
||||
whenBrowserLoaded(browser, function() {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
let doc = browser.contentDocument;
|
||||
|
||||
// click on the "Start New Session" button after about:sessionrestore is loaded
|
||||
doc.getElementById("errorCancel").click();
|
||||
whenBrowserLoaded(browser, function() {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
let doc = browser.contentDocument;
|
||||
|
||||
is(doc.URL, "about:blank", "loaded page is about:blank");
|
||||
|
@ -29,12 +29,12 @@ function test() {
|
|||
gPrefService.setCharPref("browser.startup.homepage", homepage);
|
||||
gPrefService.setIntPref("browser.startup.page", 1);
|
||||
gBrowser.loadURI("about:sessionrestore");
|
||||
whenBrowserLoaded(browser, function() {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
let doc = browser.contentDocument;
|
||||
|
||||
// click on the "Start New Session" button after about:sessionrestore is loaded
|
||||
doc.getElementById("errorCancel").click();
|
||||
whenBrowserLoaded(browser, function() {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
let doc = browser.contentDocument;
|
||||
|
||||
is(doc.URL, homepage, "loaded page is the homepage");
|
||||
|
|
|
@ -10,7 +10,7 @@ function test() {
|
|||
let uniqueValue = Math.random() + "\u2028Second line\u2029Second paragraph\u2027";
|
||||
|
||||
let tab = gBrowser.addTab();
|
||||
whenBrowserLoaded(tab.linkedBrowser, function() {
|
||||
promiseBrowserLoaded(tab.linkedBrowser).then(() => {
|
||||
ss.setTabValue(tab, "bug485563", uniqueValue);
|
||||
let tabState = JSON.parse(ss.getTabState(tab));
|
||||
is(tabState.extData["bug485563"], uniqueValue,
|
||||
|
|
|
@ -14,21 +14,20 @@ function test() {
|
|||
gBrowser.selectedTab = tab;
|
||||
|
||||
let browser = tab.linkedBrowser;
|
||||
whenBrowserLoaded(browser, function() {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
let tabState = JSON.parse(ss.getTabState(tab));
|
||||
is(tabState.entries[0].referrer, REFERRER1,
|
||||
"Referrer retrieved via getTabState matches referrer set via loadURI.");
|
||||
|
||||
tabState.entries[0].referrer = REFERRER2;
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
|
||||
whenTabRestored(tab, function(e) {
|
||||
promiseTabState(tab, tabState).then(() => {
|
||||
is(window.content.document.referrer, REFERRER2, "document.referrer matches referrer set via setTabState.");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
let newTab = ss.undoCloseTab(window, 0);
|
||||
whenTabRestored(newTab, function() {
|
||||
promiseTabRestored(newTab).then(() => {
|
||||
is(window.content.document.referrer, REFERRER2, "document.referrer is still correct after closing and reopening the tab.");
|
||||
gBrowser.removeTab(newTab);
|
||||
|
||||
|
|
|
@ -83,10 +83,10 @@ function test() {
|
|||
let tab = gBrowser.addTab("about:blank");
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
whenBrowserLoaded(browser, function() {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
browser.loadURI("http://example.com", null, null);
|
||||
|
||||
whenBrowserLoaded(browser, function() {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
// After these push/replaceState calls, the window should have three
|
||||
// history entries:
|
||||
// testURL (state object: null) <-- oldest
|
||||
|
@ -109,9 +109,7 @@ function test() {
|
|||
ss.setTabState(tab2, state, true);
|
||||
|
||||
// Run checkState() once the tab finishes loading its restored state.
|
||||
whenTabRestored(tab2, function() {
|
||||
checkState(tab2);
|
||||
});
|
||||
promiseTabRestored(tab2).then(() => checkState(tab2));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -47,7 +47,7 @@ function test() {
|
|||
|
||||
// create and select a first tab
|
||||
let tab = gBrowser.addTab(TEST_URL);
|
||||
whenBrowserLoaded(tab.linkedBrowser, function() {
|
||||
promiseBrowserLoaded(tab.linkedBrowser).then(() => {
|
||||
// step1: the above has triggered some saveStateDelayed(), sleep until
|
||||
// it's done, and get the initial sessionstore.js mtime
|
||||
setTimeout(function step1(e) {
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function test() {
|
||||
let tab1 = gBrowser.addTab("about:rights");
|
||||
let tab2 = gBrowser.addTab("about:mozilla");
|
||||
whenBrowserLoaded(tab1.linkedBrowser, mainPart);
|
||||
waitForExplicitFinish();
|
||||
|
||||
function mainPart() {
|
||||
let tab1 = gBrowser.addTab("about:rights");
|
||||
let tab2 = gBrowser.addTab("about:mozilla");
|
||||
|
||||
promiseBrowserLoaded(tab1.linkedBrowser).then(() => {
|
||||
// Tell the session storer that the tab is pinned
|
||||
let newTabState = '{"entries":[{"url":"about:rights"}],"pinned":true,"userTypedValue":"Hello World!"}';
|
||||
ss.setTabState(tab1, newTabState);
|
||||
|
@ -26,5 +26,5 @@ function test() {
|
|||
gBrowser.removeTab(tab1);
|
||||
gBrowser.removeTab(tab2);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ function test() {
|
|||
var tab1 = gBrowser.addTab("data:text/plain;charset=utf-8,foo");
|
||||
gBrowser.pinTab(tab1);
|
||||
|
||||
whenBrowserLoaded(tab1.linkedBrowser, function() {
|
||||
promiseBrowserLoaded(tab1.linkedBrowser).then(() => {
|
||||
var tab2 = gBrowser.addTab();
|
||||
gBrowser.pinTab(tab2);
|
||||
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, true);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
|
||||
|
@ -25,29 +21,36 @@ function runTests() {
|
|||
], selected: 5 }] };
|
||||
|
||||
let loadCount = 0;
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
loadCount++;
|
||||
let promiseRestoringTabs = new Promise(resolve => {
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
loadCount++;
|
||||
|
||||
// We'll make sure that the loads we get come from pinned tabs or the
|
||||
// the selected tab.
|
||||
// We'll make sure that the loads we get come from pinned tabs or the
|
||||
// the selected tab.
|
||||
|
||||
// get the tab
|
||||
let tab;
|
||||
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
|
||||
if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
|
||||
tab = window.gBrowser.tabs[i];
|
||||
}
|
||||
// get the tab
|
||||
let tab;
|
||||
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
|
||||
if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
|
||||
tab = window.gBrowser.tabs[i];
|
||||
}
|
||||
|
||||
ok(tab.pinned || tab.selected,
|
||||
"load came from pinned or selected tab");
|
||||
ok(tab.pinned || tab.selected,
|
||||
"load came from pinned or selected tab");
|
||||
|
||||
// We should get 4 loads: 3 app tabs + 1 normal selected tab
|
||||
if (loadCount < 4)
|
||||
return;
|
||||
// We should get 4 loads: 3 app tabs + 1 normal selected tab
|
||||
if (loadCount < 4)
|
||||
return;
|
||||
|
||||
gProgressListener.unsetCallback();
|
||||
executeSoon(next);
|
||||
gProgressListener.unsetCallback();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield ss.setBrowserState(JSON.stringify(state));
|
||||
}
|
||||
let backupState = ss.getBrowserState();
|
||||
ss.setBrowserState(JSON.stringify(state));
|
||||
yield promiseRestoringTabs;
|
||||
|
||||
// Cleanup.
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
|
|
@ -5,11 +5,7 @@
|
|||
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
|
||||
const PREF_RESTORE_PINNED_TABS_ON_DEMAND = "browser.sessionstore.restore_pinned_tabs_on_demand";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, true);
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_PINNED_TABS_ON_DEMAND, true);
|
||||
|
||||
|
@ -28,23 +24,30 @@ function runTests() {
|
|||
{ entries: [{ url: "http://example.org/#7" }], extData: { "uniq": r() } },
|
||||
], selected: 5 }] };
|
||||
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
// get the tab
|
||||
let tab;
|
||||
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
|
||||
if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
|
||||
tab = window.gBrowser.tabs[i];
|
||||
}
|
||||
let promiseRestoringTabs = new Promise(resolve => {
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
// get the tab
|
||||
let tab;
|
||||
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
|
||||
if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
|
||||
tab = window.gBrowser.tabs[i];
|
||||
}
|
||||
|
||||
// Check that the load only comes from the selected tab.
|
||||
ok(tab.selected, "load came from selected tab");
|
||||
is(aNeedRestore, 6, "six tabs left to restore");
|
||||
is(aRestoring, 1, "one tab is restoring");
|
||||
is(aRestored, 0, "no tabs have been restored, yet");
|
||||
// Check that the load only comes from the selected tab.
|
||||
ok(tab.selected, "load came from selected tab");
|
||||
is(aNeedRestore, 6, "six tabs left to restore");
|
||||
is(aRestoring, 1, "one tab is restoring");
|
||||
is(aRestored, 0, "no tabs have been restored, yet");
|
||||
|
||||
gProgressListener.unsetCallback();
|
||||
executeSoon(next);
|
||||
gProgressListener.unsetCallback();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield ss.setBrowserState(JSON.stringify(state));
|
||||
}
|
||||
let backupState = ss.getBrowserState();
|
||||
ss.setBrowserState(JSON.stringify(state));
|
||||
yield promiseRestoringTabs;
|
||||
|
||||
// Cleanup.
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
|
||||
|
@ -64,32 +60,33 @@ function runTests() {
|
|||
let numTabs = state2.windows[0].tabs.length + state2.windows[1].tabs.length;
|
||||
|
||||
let loadCount = 0;
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
loadCount++;
|
||||
let promiseRestoringTabs = new Promise(resolve => {
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
loadCount++;
|
||||
|
||||
if (aBrowser.currentURI.spec == state1.windows[0].tabs[2].entries[0].url)
|
||||
loadedWindow1 = true;
|
||||
if (aBrowser.currentURI.spec == state1.windows[1].tabs[0].entries[0].url)
|
||||
loadedWindow2 = true;
|
||||
if (aBrowser.currentURI.spec == state1.windows[0].tabs[2].entries[0].url)
|
||||
loadedWindow1 = true;
|
||||
if (aBrowser.currentURI.spec == state1.windows[1].tabs[0].entries[0].url)
|
||||
loadedWindow2 = true;
|
||||
|
||||
if (!interruptedAfter && loadedWindow1 && loadedWindow2) {
|
||||
interruptedAfter = loadCount;
|
||||
ss.setBrowserState(JSON.stringify(state2));
|
||||
return;
|
||||
}
|
||||
if (!interruptedAfter && loadedWindow1 && loadedWindow2) {
|
||||
interruptedAfter = loadCount;
|
||||
ss.setBrowserState(JSON.stringify(state2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadCount < numTabs + interruptedAfter)
|
||||
return;
|
||||
if (loadCount < numTabs + interruptedAfter)
|
||||
return;
|
||||
|
||||
// We don't actually care about load order in this test, just that they all
|
||||
// do load.
|
||||
is(loadCount, numTabs + interruptedAfter, "all tabs were restored");
|
||||
is(aNeedRestore, 0, "there are no tabs left needing restore");
|
||||
// We don't actually care about load order in this test, just that they all
|
||||
// do load.
|
||||
is(loadCount, numTabs + interruptedAfter, "all tabs were restored");
|
||||
is(aNeedRestore, 0, "there are no tabs left needing restore");
|
||||
|
||||
// Remove the progress listener from this window, it will be removed from
|
||||
// theWin when that window is closed (in setBrowserState).
|
||||
gProgressListener.unsetCallback();
|
||||
executeSoon(next);
|
||||
// Remove the progress listener.
|
||||
gProgressListener.unsetCallback();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// We also want to catch the extra windows (there should be 2), so we need to observe domwindowopened
|
||||
|
@ -104,5 +101,11 @@ function runTests() {
|
|||
}
|
||||
});
|
||||
|
||||
yield ss.setBrowserState(JSON.stringify(state1));
|
||||
}
|
||||
let backupState = ss.getBrowserState();
|
||||
ss.setBrowserState(JSON.stringify(state1));
|
||||
yield promiseRestoringTabs;
|
||||
|
||||
// Cleanup.
|
||||
yield promiseAllButPrimaryWindowClosed();
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
|
||||
|
@ -33,19 +29,26 @@ function runTests() {
|
|||
];
|
||||
|
||||
let loadCount = 0;
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
loadCount++;
|
||||
let expected = expectedCounts[loadCount - 1];
|
||||
let promiseRestoringTabs = new Promise(resolve => {
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
loadCount++;
|
||||
let expected = expectedCounts[loadCount - 1];
|
||||
|
||||
is(aNeedRestore, expected[0], "load " + loadCount + " - # tabs that need to be restored");
|
||||
is(aRestoring, expected[1], "load " + loadCount + " - # tabs that are restoring");
|
||||
is(aRestored, expected[2], "load " + loadCount + " - # tabs that has been restored");
|
||||
is(aNeedRestore, expected[0], "load " + loadCount + " - # tabs that need to be restored");
|
||||
is(aRestoring, expected[1], "load " + loadCount + " - # tabs that are restoring");
|
||||
is(aRestored, expected[2], "load " + loadCount + " - # tabs that has been restored");
|
||||
|
||||
if (loadCount == state.windows[0].tabs.length) {
|
||||
gProgressListener.unsetCallback();
|
||||
executeSoon(next);
|
||||
}
|
||||
if (loadCount == state.windows[0].tabs.length) {
|
||||
gProgressListener.unsetCallback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
yield ss.setBrowserState(JSON.stringify(state));
|
||||
}
|
||||
let backupState = ss.getBrowserState();
|
||||
ss.setBrowserState(JSON.stringify(state));
|
||||
yield promiseRestoringTabs;
|
||||
|
||||
// Cleanup.
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
|
||||
|
@ -38,16 +34,18 @@ function runTests() {
|
|||
let numTabs = state.windows[0].tabs.length + state.windows[1].tabs.length;
|
||||
|
||||
let loadCount = 0;
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
if (++loadCount == numTabs) {
|
||||
// We don't actually care about load order in this test, just that they all
|
||||
// do load.
|
||||
is(loadCount, numTabs, "all tabs were restored");
|
||||
is(aNeedRestore, 0, "there are no tabs left needing restore");
|
||||
let promiseRestoringTabs = new Promise(resolve => {
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
if (++loadCount == numTabs) {
|
||||
// We don't actually care about load order in this test, just that they all
|
||||
// do load.
|
||||
is(loadCount, numTabs, "all tabs were restored");
|
||||
is(aNeedRestore, 0, "there are no tabs left needing restore");
|
||||
|
||||
gProgressListener.unsetCallback();
|
||||
executeSoon(next);
|
||||
}
|
||||
gProgressListener.unsetCallback();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// We also want to catch the 2nd window, so we need to observe domwindowopened
|
||||
|
@ -62,5 +60,11 @@ function runTests() {
|
|||
}
|
||||
});
|
||||
|
||||
yield ss.setBrowserState(JSON.stringify(state));
|
||||
}
|
||||
let backupState = ss.getBrowserState();
|
||||
ss.setBrowserState(JSON.stringify(state));
|
||||
yield promiseRestoringTabs;
|
||||
|
||||
// Cleanup.
|
||||
yield promiseAllButPrimaryWindowClosed();
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, true);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
|
||||
|
@ -27,28 +23,32 @@ function runTests() {
|
|||
], selected: 1 }] };
|
||||
|
||||
let loadCount = 0;
|
||||
gBrowser.tabContainer.addEventListener("SSTabRestored", function onRestored(event) {
|
||||
let tab = event.target;
|
||||
let browser = tab.linkedBrowser;
|
||||
let tabData = state.windows[0].tabs[loadCount++];
|
||||
let promiseRestoringTabs = new Promise(resolve => {
|
||||
gBrowser.tabContainer.addEventListener("SSTabRestored", function onRestored(event) {
|
||||
let tab = event.target;
|
||||
let browser = tab.linkedBrowser;
|
||||
let tabData = state.windows[0].tabs[loadCount++];
|
||||
|
||||
// double check that this tab was the right one
|
||||
is(browser.currentURI.spec, tabData.entries[0].url,
|
||||
"load " + loadCount + " - browser loaded correct url");
|
||||
is(ss.getTabValue(tab, "uniq"), tabData.extData.uniq,
|
||||
"load " + loadCount + " - correct tab was restored");
|
||||
// double check that this tab was the right one
|
||||
is(browser.currentURI.spec, tabData.entries[0].url,
|
||||
"load " + loadCount + " - browser loaded correct url");
|
||||
is(ss.getTabValue(tab, "uniq"), tabData.extData.uniq,
|
||||
"load " + loadCount + " - correct tab was restored");
|
||||
|
||||
if (loadCount == state.windows[0].tabs.length) {
|
||||
gBrowser.tabContainer.removeEventListener("SSTabRestored", onRestored);
|
||||
|
||||
executeSoon(function () {
|
||||
waitForBrowserState(TestRunner.backupState, finish);
|
||||
});
|
||||
} else {
|
||||
// reload the next tab
|
||||
gBrowser.browsers[loadCount].reload();
|
||||
}
|
||||
if (loadCount == state.windows[0].tabs.length) {
|
||||
gBrowser.tabContainer.removeEventListener("SSTabRestored", onRestored);
|
||||
resolve();
|
||||
} else {
|
||||
// reload the next tab
|
||||
gBrowser.browsers[loadCount].reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
yield ss.setBrowserState(JSON.stringify(state));
|
||||
}
|
||||
let backupState = ss.getBrowserState();
|
||||
ss.setBrowserState(JSON.stringify(state));
|
||||
yield promiseRestoringTabs;
|
||||
|
||||
// Cleanup.
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, true);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
|
||||
|
@ -34,33 +30,40 @@ function runTests() {
|
|||
let tabOrder = [0, 5, 1, 4, 3, 2];
|
||||
|
||||
let loadCount = 0;
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
loadCount++;
|
||||
let expected = expectedCounts[loadCount - 1];
|
||||
let promiseRestoringTabs = new Promise(resolve => {
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
loadCount++;
|
||||
let expected = expectedCounts[loadCount - 1];
|
||||
|
||||
is(aNeedRestore, expected[0], "load " + loadCount + " - # tabs that need to be restored");
|
||||
is(aRestoring, expected[1], "load " + loadCount + " - # tabs that are restoring");
|
||||
is(aRestored, expected[2], "load " + loadCount + " - # tabs that has been restored");
|
||||
is(aNeedRestore, expected[0], "load " + loadCount + " - # tabs that need to be restored");
|
||||
is(aRestoring, expected[1], "load " + loadCount + " - # tabs that are restoring");
|
||||
is(aRestored, expected[2], "load " + loadCount + " - # tabs that has been restored");
|
||||
|
||||
if (loadCount < state.windows[0].tabs.length) {
|
||||
// double check that this tab was the right one
|
||||
let expectedData = state.windows[0].tabs[tabOrder[loadCount - 1]].extData.uniq;
|
||||
let tab;
|
||||
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
|
||||
if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
|
||||
tab = window.gBrowser.tabs[i];
|
||||
if (loadCount < state.windows[0].tabs.length) {
|
||||
// double check that this tab was the right one
|
||||
let expectedData = state.windows[0].tabs[tabOrder[loadCount - 1]].extData.uniq;
|
||||
let tab;
|
||||
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
|
||||
if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
|
||||
tab = window.gBrowser.tabs[i];
|
||||
}
|
||||
|
||||
is(ss.getTabValue(tab, "uniq"), expectedData,
|
||||
"load " + loadCount + " - correct tab was restored");
|
||||
|
||||
// select the next tab
|
||||
window.gBrowser.selectTabAtIndex(tabOrder[loadCount]);
|
||||
} else {
|
||||
gProgressListener.unsetCallback();
|
||||
resolve();
|
||||
}
|
||||
|
||||
is(ss.getTabValue(tab, "uniq"), expectedData,
|
||||
"load " + loadCount + " - correct tab was restored");
|
||||
|
||||
// select the next tab
|
||||
window.gBrowser.selectTabAtIndex(tabOrder[loadCount]);
|
||||
} else {
|
||||
gProgressListener.unsetCallback();
|
||||
executeSoon(next);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
yield ss.setBrowserState(JSON.stringify(state));
|
||||
}
|
||||
let backupState = ss.getBrowserState();
|
||||
ss.setBrowserState(JSON.stringify(state));
|
||||
yield promiseRestoringTabs;
|
||||
|
||||
// Cleanup.
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
|
||||
|
@ -33,24 +29,31 @@ function runTests() {
|
|||
let numTabs = state1.windows[0].tabs.length + state2.windows[0].tabs.length;
|
||||
|
||||
let loadCount = 0;
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
// When loadCount == 2, we'll also restore state2 into the window
|
||||
if (++loadCount == 2) {
|
||||
ss.setWindowState(window, JSON.stringify(state2), false);
|
||||
}
|
||||
let promiseRestoringTabs = new Promise(resolve => {
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
// When loadCount == 2, we'll also restore state2 into the window
|
||||
if (++loadCount == 2) {
|
||||
ss.setWindowState(window, JSON.stringify(state2), false);
|
||||
}
|
||||
|
||||
if (loadCount < numTabs) {
|
||||
return;
|
||||
}
|
||||
if (loadCount < numTabs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't actually care about load order in this test, just that they all
|
||||
// do load.
|
||||
is(loadCount, numTabs, "test_setWindowStateNoOverwrite: all tabs were restored");
|
||||
is(aNeedRestore, 0, "there are no tabs left needing restore");
|
||||
// We don't actually care about load order in this test, just that they all
|
||||
// do load.
|
||||
is(loadCount, numTabs, "test_setWindowStateNoOverwrite: all tabs were restored");
|
||||
is(aNeedRestore, 0, "there are no tabs left needing restore");
|
||||
|
||||
gProgressListener.unsetCallback();
|
||||
executeSoon(next);
|
||||
gProgressListener.unsetCallback();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield ss.setWindowState(window, JSON.stringify(state1), true);
|
||||
}
|
||||
let backupState = ss.getBrowserState();
|
||||
ss.setWindowState(window, JSON.stringify(state1), true);
|
||||
yield promiseRestoringTabs;
|
||||
|
||||
// Cleanup.
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
|
||||
|
@ -33,24 +29,31 @@ function runTests() {
|
|||
let numTabs = 2 + state2.windows[0].tabs.length;
|
||||
|
||||
let loadCount = 0;
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
// When loadCount == 2, we'll also restore state2 into the window
|
||||
if (++loadCount == 2) {
|
||||
executeSoon(() => ss.setWindowState(window, JSON.stringify(state2), true));
|
||||
}
|
||||
let promiseRestoringTabs = new Promise(resolve => {
|
||||
gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
|
||||
// When loadCount == 2, we'll also restore state2 into the window
|
||||
if (++loadCount == 2) {
|
||||
executeSoon(() => ss.setWindowState(window, JSON.stringify(state2), true));
|
||||
}
|
||||
|
||||
if (loadCount < numTabs) {
|
||||
return;
|
||||
}
|
||||
if (loadCount < numTabs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't actually care about load order in this test, just that they all
|
||||
// do load.
|
||||
is(loadCount, numTabs, "all tabs were restored");
|
||||
is(aNeedRestore, 0, "there are no tabs left needing restore");
|
||||
// We don't actually care about load order in this test, just that they all
|
||||
// do load.
|
||||
is(loadCount, numTabs, "all tabs were restored");
|
||||
is(aNeedRestore, 0, "there are no tabs left needing restore");
|
||||
|
||||
gProgressListener.unsetCallback();
|
||||
executeSoon(next);
|
||||
gProgressListener.unsetCallback();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield ss.setWindowState(window, JSON.stringify(state1), true);
|
||||
}
|
||||
let backupState = ss.getBrowserState();
|
||||
ss.setWindowState(window, JSON.stringify(state1), true);
|
||||
yield promiseRestoringTabs;
|
||||
|
||||
// Cleanup.
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ function newWindowWithState(state, callback) {
|
|||
executeSoon(function () {
|
||||
win.addEventListener("SSWindowStateReady", function onReady() {
|
||||
win.removeEventListener("SSWindowStateReady", onReady, false);
|
||||
whenTabRestored(win.gBrowser.tabs[0], () => callback(win));
|
||||
promiseTabRestored(win.gBrowser.tabs[0]).then(() => callback(win));
|
||||
}, false);
|
||||
|
||||
ss.setWindowState(win, JSON.stringify(state), true);
|
||||
|
|
|
@ -163,7 +163,7 @@ function onStateRestored(aSubject, aTopic, aData) {
|
|||
newWin.addEventListener("load", function(aEvent) {
|
||||
newWin.removeEventListener("load", arguments.callee, false);
|
||||
|
||||
whenBrowserLoaded(newWin.gBrowser.selectedBrowser, function() {
|
||||
promiseBrowserLoaded(newWin.gBrowser.selectedBrowser).then(() => {
|
||||
// pin this tab
|
||||
if (shouldPinTab)
|
||||
newWin.gBrowser.pinTab(newWin.gBrowser.selectedTab);
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
*/
|
||||
add_task(function test_close_last_nonpopup_window() {
|
||||
// Purge the list of closed windows.
|
||||
while (ss.getClosedWindowCount()) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
forgetClosedWindows();
|
||||
|
||||
let oldState = ss.getWindowState(window);
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
let TEST_STATE = { windows: [{ tabs: [{ url: "about:blank" }] }] };
|
||||
|
||||
function runTests() {
|
||||
add_task(function* () {
|
||||
function assertNumberOfTabs(num, msg) {
|
||||
is(gBrowser.tabs.length, num, msg);
|
||||
}
|
||||
|
@ -29,13 +27,9 @@ function runTests() {
|
|||
assertNumberOfPinnedTabs(2, "both tabs are now pinned");
|
||||
|
||||
// run the test
|
||||
yield waitForBrowserState(
|
||||
{ windows: [{ tabs: [{ url: "about:blank" }] }] },
|
||||
function () {
|
||||
assertNumberOfTabs(1, "one tab left after setBrowserState()");
|
||||
assertNumberOfPinnedTabs(0, "there are no pinned tabs");
|
||||
is(gBrowser.tabs[0].linkedBrowser, linkedBrowser, "first tab's browser got re-used");
|
||||
next();
|
||||
}
|
||||
);
|
||||
}
|
||||
yield promiseBrowserState(TEST_STATE);
|
||||
|
||||
assertNumberOfTabs(1, "one tab left after setBrowserState()");
|
||||
assertNumberOfPinnedTabs(0, "there are no pinned tabs");
|
||||
is(gBrowser.tabs[0].linkedBrowser, linkedBrowser, "first tab's browser got re-used");
|
||||
});
|
||||
|
|
|
@ -17,9 +17,7 @@ add_task(function* setup() {
|
|||
|
||||
// We'll clear all closed windows to make sure our state is clean
|
||||
// forgetClosedWindow doesn't trigger a delayed save
|
||||
while (ss.getClosedWindowCount()) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
forgetClosedWindows();
|
||||
is(ss.getClosedWindowCount(), 0, "starting with no closed windows");
|
||||
});
|
||||
|
||||
|
@ -79,8 +77,6 @@ add_task(function* done() {
|
|||
// The API still represents the closed window as closed, so we can clear it
|
||||
// with the API, but just to make sure...
|
||||
// is(ss.getClosedWindowCount(), 1, "1 closed window according to API");
|
||||
while (ss.getClosedWindowCount()) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
forgetClosedWindows();
|
||||
Services.prefs.clearUserPref("browser.sessionstore.interval");
|
||||
});
|
||||
|
|
|
@ -18,10 +18,6 @@ const TEST_STATE = {
|
|||
}]
|
||||
};
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that windows that have just been restored will be marked
|
||||
* as dirty, otherwise _getCurrentState() might ignore them when collecting
|
||||
|
@ -32,19 +28,20 @@ function test() {
|
|||
* their state at least once.
|
||||
*/
|
||||
|
||||
function runTests() {
|
||||
let win;
|
||||
|
||||
add_task(function* test() {
|
||||
// Wait until the new window has been opened.
|
||||
Services.obs.addObserver(function onOpened(subject) {
|
||||
Services.obs.removeObserver(onOpened, "domwindowopened");
|
||||
win = subject;
|
||||
executeSoon(next);
|
||||
}, "domwindowopened", false);
|
||||
let promiseWindow = new Promise(resolve => {
|
||||
Services.obs.addObserver(function onOpened(subject) {
|
||||
Services.obs.removeObserver(onOpened, "domwindowopened");
|
||||
resolve(subject);
|
||||
}, "domwindowopened", false);
|
||||
});
|
||||
|
||||
// Set the new browser state that will
|
||||
// restore a window with two slowly loading tabs.
|
||||
yield SessionStore.setBrowserState(JSON.stringify(TEST_STATE));
|
||||
let backupState = SessionStore.getBrowserState();
|
||||
SessionStore.setBrowserState(JSON.stringify(TEST_STATE));
|
||||
let win = yield promiseWindow;
|
||||
|
||||
// The window has now been opened. Check the state that is returned,
|
||||
// this should come from the cache while the window isn't restored, yet.
|
||||
|
@ -53,10 +50,14 @@ function runTests() {
|
|||
|
||||
// The history has now been restored and the tabs are loading. The data must
|
||||
// now come from the window, if it's correctly been marked as dirty before.
|
||||
yield whenDelayedStartupFinished(win, next);
|
||||
yield new Promise(resolve => whenDelayedStartupFinished(win, resolve));
|
||||
info("the delayed startup has finished");
|
||||
checkWindows();
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
yield promiseWindowClosed(win);
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
||||
function checkWindows() {
|
||||
let state = JSON.parse(SessionStore.getBrowserState());
|
||||
|
|
|
@ -63,10 +63,8 @@ function testTabRestoreData(aFormData, aExpectedValue, aCallback) {
|
|||
let tab = gBrowser.addTab(testURL);
|
||||
let tabState = { entries: [{ url: testURL, formdata: aFormData}] };
|
||||
|
||||
whenBrowserLoaded(tab.linkedBrowser, function() {
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
|
||||
whenTabRestored(tab, function() {
|
||||
promiseBrowserLoaded(tab.linkedBrowser).then(() => {
|
||||
promiseTabState(tab, tabState).then(() => {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
let select = doc.getElementById("select_id");
|
||||
let value = select.options[select.selectedIndex].value;
|
||||
|
|
|
@ -30,7 +30,7 @@ function test()
|
|||
});
|
||||
|
||||
let tab = gBrowser.addTab("about:blank");
|
||||
waitForTabState(tab, state, function () {
|
||||
promiseTabState(tab, state).then(() => {
|
||||
let history = tab.linkedBrowser.webNavigation.sessionHistory;
|
||||
|
||||
is(history.count, 2, "history.count");
|
||||
|
|
|
@ -18,15 +18,14 @@ function test() {
|
|||
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
waitForTabState(tab, tabState, function () {
|
||||
|
||||
promiseTabState(tab, tabState).then(() => {
|
||||
let sessionHistory = browser.sessionHistory;
|
||||
let entry = sessionHistory.getEntryAtIndex(0, false);
|
||||
entry.QueryInterface(Ci.nsISHContainer);
|
||||
|
||||
whenChildCount(entry, 1, function () {
|
||||
whenChildCount(entry, 2, function () {
|
||||
whenBrowserLoaded(browser, function () {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
TabState.flush(browser);
|
||||
let {entries} = JSON.parse(ss.getTabState(tab));
|
||||
is(entries.length, 1, "tab has one history entry");
|
||||
|
|
|
@ -18,15 +18,14 @@ function test() {
|
|||
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
waitForTabState(tab, tabState, function() {
|
||||
|
||||
promiseTabState(tab, tabState).then(() => {
|
||||
let sessionHistory = browser.sessionHistory;
|
||||
let entry = sessionHistory.getEntryAtIndex(0, false);
|
||||
entry.QueryInterface(Ci.nsISHContainer);
|
||||
|
||||
whenChildCount(entry, 1, function () {
|
||||
whenChildCount(entry, 2, function () {
|
||||
whenBrowserLoaded(browser, function () {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
let sessionHistory = browser.sessionHistory;
|
||||
let entry = sessionHistory.getEntryAtIndex(0, false);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ function test() {
|
|||
let tab = gBrowser.addTab("about:blank");
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
whenBrowserLoaded(browser, function () {
|
||||
promiseBrowserLoaded(browser).then(() => {
|
||||
isnot(gBrowser.selectedTab, tab, "newly created tab is not selected");
|
||||
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
|
@ -28,7 +28,7 @@ function test() {
|
|||
let formdata = state.entries[0].formdata;
|
||||
is(formdata && formdata.id["foo"], "bar", "tab state's formdata is valid");
|
||||
|
||||
whenTabRestored(tab, function () {
|
||||
promiseTabRestored(tab).then(() => {
|
||||
let input = browser.contentDocument.getElementById("foo");
|
||||
is(input.value, "bar", "formdata has been restored correctly");
|
||||
finish();
|
||||
|
@ -38,17 +38,3 @@ function test() {
|
|||
gBrowser.selectedTab = tab;
|
||||
});
|
||||
}
|
||||
|
||||
function whenBrowserLoaded(aBrowser, aCallback) {
|
||||
aBrowser.addEventListener("load", function onLoad() {
|
||||
aBrowser.removeEventListener("load", onLoad, true);
|
||||
executeSoon(aCallback);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function whenTabRestored(aTab, aCallback) {
|
||||
aTab.addEventListener("SSTabRestored", function onRestored() {
|
||||
aTab.removeEventListener("SSTabRestored", onRestored);
|
||||
executeSoon(aCallback);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,183 +1,116 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const originalState = ss.getBrowserState();
|
||||
|
||||
/** Private Browsing Test for Bug 819510 **/
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
let tests = [test_1, test_2, test_3 ];
|
||||
|
||||
const testState = {
|
||||
windows: [{
|
||||
tabs: [
|
||||
{ entries: [{ url: "about:blank" }] },
|
||||
]
|
||||
}]
|
||||
};
|
||||
|
||||
function runNextTest() {
|
||||
// Set an empty state
|
||||
closeAllButPrimaryWindow();
|
||||
|
||||
// Run the next test, or finish
|
||||
if (tests.length) {
|
||||
let currentTest = tests.shift();
|
||||
waitForBrowserState(testState, currentTest);
|
||||
} else {
|
||||
Services.obs.addObserver(
|
||||
function observe(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(observe, aTopic);
|
||||
finish();
|
||||
},
|
||||
"sessionstore-browser-state-restored", false);
|
||||
ss.setBrowserState(originalState);
|
||||
}
|
||||
}
|
||||
|
||||
// Test opening default mochitest-normal-private-normal-private windows
|
||||
// (saving the state with last window being private)
|
||||
function test_1() {
|
||||
testOnWindow(false, function(aWindow) {
|
||||
aWindow.gBrowser.addTab("http://www.example.com/1");
|
||||
testOnWindow(true, function(aWindow) {
|
||||
aWindow.gBrowser.addTab("http://www.example.com/2");
|
||||
testOnWindow(false, function(aWindow) {
|
||||
aWindow.gBrowser.addTab("http://www.example.com/3");
|
||||
testOnWindow(true, function(aWindow) {
|
||||
aWindow.gBrowser.addTab("http://www.example.com/4");
|
||||
add_task(function* test_1() {
|
||||
let win = yield promiseNewWindowLoaded();
|
||||
win.gBrowser.addTab("http://www.example.com/1");
|
||||
|
||||
let curState = JSON.parse(ss.getBrowserState());
|
||||
is (curState.windows.length, 5, "Browser has opened 5 windows");
|
||||
is (curState.windows[2].isPrivate, true, "Window is private");
|
||||
is (curState.windows[4].isPrivate, true, "Last window is private");
|
||||
is (curState.selectedWindow, 5, "Last window opened is the one selected");
|
||||
win = yield promiseNewWindowLoaded({private: true});
|
||||
win.gBrowser.addTab("http://www.example.com/2");
|
||||
|
||||
forceWriteState(function(state) {
|
||||
is(state.windows.length, 3,
|
||||
"sessionstore state: 3 windows in data being written to disk");
|
||||
is (state.selectedWindow, 3,
|
||||
"Selected window is updated to match one of the saved windows");
|
||||
state.windows.forEach(function(win) {
|
||||
is(!win.isPrivate, true, "Saved window is not private");
|
||||
});
|
||||
is(state._closedWindows.length, 0,
|
||||
"sessionstore state: no closed windows in data being written to disk");
|
||||
runNextTest();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
win = yield promiseNewWindowLoaded();
|
||||
win.gBrowser.addTab("http://www.example.com/3");
|
||||
|
||||
win = yield promiseNewWindowLoaded({private: true});
|
||||
win.gBrowser.addTab("http://www.example.com/4");
|
||||
|
||||
let curState = JSON.parse(ss.getBrowserState());
|
||||
is(curState.windows.length, 5, "Browser has opened 5 windows");
|
||||
is(curState.windows[2].isPrivate, true, "Window is private");
|
||||
is(curState.windows[4].isPrivate, true, "Last window is private");
|
||||
is(curState.selectedWindow, 5, "Last window opened is the one selected");
|
||||
|
||||
let state = JSON.parse(yield promiseRecoveryFileContents());
|
||||
|
||||
is(state.windows.length, 3,
|
||||
"sessionstore state: 3 windows in data being written to disk");
|
||||
is(state.selectedWindow, 3,
|
||||
"Selected window is updated to match one of the saved windows");
|
||||
ok(state.windows.every(win => !win.isPrivate),
|
||||
"Saved windows are not private");
|
||||
is(state._closedWindows.length, 0,
|
||||
"sessionstore state: no closed windows in data being written to disk");
|
||||
|
||||
// Cleanup.
|
||||
yield promiseAllButPrimaryWindowClosed();
|
||||
forgetClosedWindows();
|
||||
});
|
||||
|
||||
// Test opening default mochitest window + 2 private windows
|
||||
function test_2() {
|
||||
testOnWindow(true, function(aWindow) {
|
||||
aWindow.gBrowser.addTab("http://www.example.com/1");
|
||||
testOnWindow(true, function(aWindow) {
|
||||
aWindow.gBrowser.addTab("http://www.example.com/2");
|
||||
add_task(function* test_2() {
|
||||
let win = yield promiseNewWindowLoaded({private: true});
|
||||
win.gBrowser.addTab("http://www.example.com/1");
|
||||
|
||||
let curState = JSON.parse(ss.getBrowserState());
|
||||
is (curState.windows.length, 3, "Browser has opened 3 windows");
|
||||
is (curState.windows[1].isPrivate, true, "Window 1 is private");
|
||||
is (curState.windows[2].isPrivate, true, "Window 2 is private");
|
||||
is (curState.selectedWindow, 3, "Last window opened is the one selected");
|
||||
win = yield promiseNewWindowLoaded({private: true});
|
||||
win.gBrowser.addTab("http://www.example.com/2");
|
||||
|
||||
forceWriteState(function(state) {
|
||||
is(state.windows.length, 1,
|
||||
"sessionstore state: 1 windows in data being written to disk");
|
||||
is (state.selectedWindow, 1,
|
||||
"Selected window is updated to match one of the saved windows");
|
||||
is(state._closedWindows.length, 0,
|
||||
"sessionstore state: no closed windows in data being written to disk");
|
||||
runNextTest();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
let curState = JSON.parse(ss.getBrowserState());
|
||||
is(curState.windows.length, 3, "Browser has opened 3 windows");
|
||||
is(curState.windows[1].isPrivate, true, "Window 1 is private");
|
||||
is(curState.windows[2].isPrivate, true, "Window 2 is private");
|
||||
is(curState.selectedWindow, 3, "Last window opened is the one selected");
|
||||
|
||||
let state = JSON.parse(yield promiseRecoveryFileContents());
|
||||
|
||||
is(state.windows.length, 1,
|
||||
"sessionstore state: 1 windows in data being written to disk");
|
||||
is(state.selectedWindow, 1,
|
||||
"Selected window is updated to match one of the saved windows");
|
||||
is(state._closedWindows.length, 0,
|
||||
"sessionstore state: no closed windows in data being written to disk");
|
||||
|
||||
// Cleanup.
|
||||
yield promiseAllButPrimaryWindowClosed();
|
||||
forgetClosedWindows();
|
||||
});
|
||||
|
||||
// Test opening default-normal-private-normal windows and closing a normal window
|
||||
function test_3() {
|
||||
testOnWindow(false, function(normalWindow) {
|
||||
waitForTabLoad(normalWindow, "http://www.example.com/", function() {
|
||||
testOnWindow(true, function(aWindow) {
|
||||
waitForTabLoad(aWindow, "http://www.example.com/", function() {
|
||||
testOnWindow(false, function(aWindow) {
|
||||
waitForTabLoad(aWindow, "http://www.example.com/", function() {
|
||||
add_task(function* test_3() {
|
||||
let normalWindow = yield promiseNewWindowLoaded();
|
||||
yield promiseTabLoad(normalWindow, "http://www.example.com/");
|
||||
|
||||
let curState = JSON.parse(ss.getBrowserState());
|
||||
is(curState.windows.length, 4, "Browser has opened 4 windows");
|
||||
is(curState.windows[2].isPrivate, true, "Window 2 is private");
|
||||
is(curState.selectedWindow, 4, "Last window opened is the one selected");
|
||||
let win = yield promiseNewWindowLoaded({private: true});
|
||||
yield promiseTabLoad(win, "http://www.example.com/");
|
||||
|
||||
waitForWindowClose(normalWindow, function() {
|
||||
// Pin and unpin a tab before checking the written state so that
|
||||
// the list of restoring windows gets cleared. Otherwise the
|
||||
// window we just closed would be marked as not closed.
|
||||
let tab = aWindow.gBrowser.tabs[0];
|
||||
aWindow.gBrowser.pinTab(tab);
|
||||
aWindow.gBrowser.unpinTab(tab);
|
||||
win = yield promiseNewWindowLoaded();
|
||||
yield promiseTabLoad(win, "http://www.example.com/");
|
||||
|
||||
forceWriteState(function(state) {
|
||||
is(state.windows.length, 2,
|
||||
"sessionstore state: 2 windows in data being written to disk");
|
||||
is(state.selectedWindow, 2,
|
||||
"Selected window is updated to match one of the saved windows");
|
||||
state.windows.forEach(function(win) {
|
||||
is(!win.isPrivate, true, "Saved window is not private");
|
||||
});
|
||||
is(state._closedWindows.length, 1,
|
||||
"sessionstore state: 1 closed window in data being written to disk");
|
||||
state._closedWindows.forEach(function(win) {
|
||||
is(!win.isPrivate, true, "Closed window is not private");
|
||||
});
|
||||
runNextTest();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function waitForWindowClose(aWin, aCallback) {
|
||||
let winCount = JSON.parse(ss.getBrowserState()).windows.length;
|
||||
aWin.addEventListener("SSWindowClosing", function onWindowClosing() {
|
||||
aWin.removeEventListener("SSWindowClosing", onWindowClosing, false);
|
||||
function checkCount() {
|
||||
let state = JSON.parse(ss.getBrowserState());
|
||||
if (state.windows.length == (winCount - 1)) {
|
||||
aCallback();
|
||||
} else {
|
||||
executeSoon(checkCount);
|
||||
}
|
||||
}
|
||||
executeSoon(checkCount);
|
||||
}, false);
|
||||
aWin.close();
|
||||
}
|
||||
|
||||
function forceWriteState(aCallback) {
|
||||
return promiseRecoveryFileContents().then(function(data) {
|
||||
aCallback(JSON.parse(data));
|
||||
});
|
||||
}
|
||||
|
||||
function testOnWindow(aIsPrivate, aCallback) {
|
||||
whenNewWindowLoaded({private: aIsPrivate}, aCallback);
|
||||
}
|
||||
|
||||
function waitForTabLoad(aWin, aURL, aCallback) {
|
||||
let browser = aWin.gBrowser.selectedBrowser;
|
||||
browser.loadURI(aURL);
|
||||
whenBrowserLoaded(browser, function () {
|
||||
TabState.flush(browser);
|
||||
executeSoon(aCallback);
|
||||
});
|
||||
let curState = JSON.parse(ss.getBrowserState());
|
||||
is(curState.windows.length, 4, "Browser has opened 4 windows");
|
||||
is(curState.windows[2].isPrivate, true, "Window 2 is private");
|
||||
is(curState.selectedWindow, 4, "Last window opened is the one selected");
|
||||
|
||||
yield promiseWindowClosed(normalWindow);
|
||||
|
||||
// Pin and unpin a tab before checking the written state so that
|
||||
// the list of restoring windows gets cleared. Otherwise the
|
||||
// window we just closed would be marked as not closed.
|
||||
let tab = win.gBrowser.tabs[0];
|
||||
win.gBrowser.pinTab(tab);
|
||||
win.gBrowser.unpinTab(tab);
|
||||
|
||||
let state = JSON.parse(yield promiseRecoveryFileContents());
|
||||
|
||||
is(state.windows.length, 2,
|
||||
"sessionstore state: 2 windows in data being written to disk");
|
||||
is(state.selectedWindow, 2,
|
||||
"Selected window is updated to match one of the saved windows");
|
||||
ok(state.windows.every(win => !win.isPrivate),
|
||||
"Saved windows are not private");
|
||||
is(state._closedWindows.length, 1,
|
||||
"sessionstore state: 1 closed window in data being written to disk");
|
||||
ok(state._closedWindows.every(win => !win.isPrivate),
|
||||
"Closed windows are not private");
|
||||
|
||||
// Cleanup.
|
||||
yield promiseAllButPrimaryWindowClosed();
|
||||
forgetClosedWindows();
|
||||
});
|
||||
|
||||
function promiseTabLoad(win, url) {
|
||||
let browser = win.gBrowser.selectedBrowser;
|
||||
browser.loadURI(url);
|
||||
return promiseBrowserLoaded(browser).then(() => TabState.flush(browser));
|
||||
}
|
||||
|
|
|
@ -5,18 +5,14 @@
|
|||
// security policy with the document.
|
||||
// The policy being tested disallows inline scripts
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
// create a tab that has a CSP
|
||||
let testURL = "http://mochi.test:8888/browser/browser/components/sessionstore/test/browser_911547_sample.html";
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(testURL);
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let browser = tab.linkedBrowser;
|
||||
yield waitForLoad(browser);
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
// this is a baseline to ensure CSP is active
|
||||
// attempt to inject and run a script via inline (pre-restore, allowed)
|
||||
|
@ -27,7 +23,7 @@ function runTests() {
|
|||
// attempt to click a link to a data: URI (will inherit the CSP of the
|
||||
// origin document) and navigate to the data URI in the link.
|
||||
browser.contentDocument.getElementById("test_data_link").click();
|
||||
yield waitForLoad(browser);
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
is(browser.contentDocument.getElementById("test_id2").value, "ok",
|
||||
"CSP should block the script loaded by the clicked data URI");
|
||||
|
@ -37,7 +33,7 @@ function runTests() {
|
|||
|
||||
// open new tab and recover the state
|
||||
tab = ss.undoCloseTab(window, 0);
|
||||
yield waitForTabRestored(tab);
|
||||
yield promiseTabRestored(tab);
|
||||
browser = tab.linkedBrowser;
|
||||
|
||||
is(browser.contentDocument.getElementById("test_id2").value, "ok",
|
||||
|
@ -45,21 +41,7 @@ function runTests() {
|
|||
|
||||
// clean up
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
|
||||
function waitForLoad(aElement) {
|
||||
aElement.addEventListener("load", function onLoad() {
|
||||
aElement.removeEventListener("load", onLoad, true);
|
||||
executeSoon(next);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function waitForTabRestored(aElement) {
|
||||
aElement.addEventListener("SSTabRestored", function tabRestored(e) {
|
||||
aElement.removeEventListener("SSTabRestored", tabRestored, true);
|
||||
executeSoon(next);
|
||||
}, true);
|
||||
}
|
||||
});
|
||||
|
||||
// injects an inline script element (with a text body)
|
||||
function injectInlineScript(browser, scriptText) {
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* This test makes sure that we correctly preserve tab attributes when storing
|
||||
* and restoring tabs. It also ensures that we skip special attributes like
|
||||
|
@ -13,13 +9,13 @@ function test() {
|
|||
|
||||
const PREF = "browser.sessionstore.restore_on_demand";
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
Services.prefs.setBoolPref(PREF, true)
|
||||
registerCleanupFunction(() => Services.prefs.clearUserPref(PREF));
|
||||
|
||||
// Add a new tab with a nice icon.
|
||||
let tab = gBrowser.addTab("about:robots");
|
||||
yield whenBrowserLoaded(tab.linkedBrowser);
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
|
||||
// Check that the tab has an 'image' attribute.
|
||||
ok(tab.hasAttribute("image"), "tab.image exists");
|
||||
|
@ -44,15 +40,16 @@ function runTests() {
|
|||
};
|
||||
|
||||
// Prepare a pending tab waiting to be restored.
|
||||
whenTabRestoring(tab);
|
||||
yield ss.setTabState(tab, JSON.stringify(state));
|
||||
let promise = promiseTabRestoring(tab);
|
||||
ss.setTabState(tab, JSON.stringify(state));
|
||||
yield promise;
|
||||
|
||||
ok(tab.hasAttribute("pending"), "tab is pending");
|
||||
is(gBrowser.getIcon(tab), state.attributes.image, "tab has correct icon");
|
||||
|
||||
// Let the pending tab load.
|
||||
gBrowser.selectedTab = tab;
|
||||
yield whenTabRestored(tab);
|
||||
yield promiseTabRestored(tab);
|
||||
|
||||
// Ensure no 'image' or 'pending' attributes are stored.
|
||||
({attributes} = JSON.parse(ss.getTabState(tab)));
|
||||
|
@ -62,11 +59,13 @@ function runTests() {
|
|||
|
||||
// Clean up.
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
function whenTabRestoring(tab) {
|
||||
tab.addEventListener("SSTabRestoring", function onRestoring() {
|
||||
tab.removeEventListener("SSTabRestoring", onRestoring);
|
||||
executeSoon(next);
|
||||
function promiseTabRestoring(tab) {
|
||||
return new Promise(resolve => {
|
||||
tab.addEventListener("SSTabRestoring", function onRestoring() {
|
||||
tab.removeEventListener("SSTabRestoring", onRestoring);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -102,8 +102,7 @@ add_task(function flush_on_settabstate() {
|
|||
// asynchronous messages.
|
||||
TabState.flushAsync(browser);
|
||||
|
||||
ss.setTabState(tab, state);
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, state);
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
|
|
|
@ -41,8 +41,7 @@ add_task(function docshell_capabilities() {
|
|||
is(disallow.size, 2, "two capabilities disallowed");
|
||||
|
||||
// Reuse the tab to restore a new, clean state into it.
|
||||
ss.setTabState(tab, JSON.stringify({ entries: [{url: "about:robots"}] }));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, {entries: [{url: "about:robots"}]});
|
||||
|
||||
// Flush to make sure chrome received all data.
|
||||
TabState.flush(browser);
|
||||
|
@ -53,8 +52,7 @@ add_task(function docshell_capabilities() {
|
|||
ok(flags.every(f => docShell[f]), "all flags set to true");
|
||||
|
||||
// Restore the state with disallowed features.
|
||||
ss.setTabState(tab, JSON.stringify(disallowedState));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, disallowedState);
|
||||
|
||||
// Check that docShell flags are set.
|
||||
ok(!docShell.allowImages, "images not allowed");
|
||||
|
|
|
@ -37,9 +37,7 @@ function getClosedState() {
|
|||
let CLOSED_STATE;
|
||||
|
||||
add_task(function* init() {
|
||||
while (ss.getClosedWindowCount() > 0) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
forgetClosedWindows();
|
||||
while (ss.getClosedTabCount(window) > 0) {
|
||||
ss.forgetClosedTab(window, 0);
|
||||
}
|
||||
|
|
|
@ -1,29 +1,24 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that after closing a window we keep its state data around
|
||||
* as long as something keeps a reference to it. It should only be possible to
|
||||
* read data after closing - writing should fail.
|
||||
*/
|
||||
|
||||
function runTests() {
|
||||
add_task(function* test() {
|
||||
// Open a new window.
|
||||
let win = OpenBrowserWindow();
|
||||
yield whenDelayedStartupFinished(win, next);
|
||||
let win = yield promiseNewWindowLoaded();
|
||||
|
||||
// Load some URL in the current tab.
|
||||
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
|
||||
win.gBrowser.selectedBrowser.loadURIWithFlags("about:robots", flags);
|
||||
yield whenBrowserLoaded(win.gBrowser.selectedBrowser);
|
||||
yield promiseBrowserLoaded(win.gBrowser.selectedBrowser);
|
||||
|
||||
// Open a second tab and close the first one.
|
||||
let tab = win.gBrowser.addTab("about:mozilla");
|
||||
yield whenBrowserLoaded(tab.linkedBrowser);
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
win.gBrowser.removeTab(win.gBrowser.tabs[0]);
|
||||
|
||||
|
@ -36,9 +31,8 @@ function runTests() {
|
|||
let state = ss.getWindowState(win);
|
||||
let closedTabData = ss.getClosedTabData(win);
|
||||
|
||||
// Close our window and wait a tick.
|
||||
whenWindowClosed(win);
|
||||
yield win.close();
|
||||
// Close our window.
|
||||
yield promiseWindowClosed(win);
|
||||
|
||||
// SessionStore should no longer track our window
|
||||
// but it should still report the same state.
|
||||
|
@ -46,11 +40,11 @@ function runTests() {
|
|||
checkWindowState(win);
|
||||
|
||||
// Make sure we're not allowed to modify state data.
|
||||
ok(shouldThrow(() => ss.setWindowState(win, {})),
|
||||
"we're not allowed to modify state data anymore");
|
||||
ok(shouldThrow(() => ss.setWindowValue(win, "foo", "baz")),
|
||||
"we're not allowed to modify state data anymore");
|
||||
}
|
||||
Assert.throws(() => ss.setWindowState(win, {}),
|
||||
"we're not allowed to modify state data anymore");
|
||||
Assert.throws(() => ss.setWindowValue(win, "foo", "baz"),
|
||||
"we're not allowed to modify state data anymore");
|
||||
});
|
||||
|
||||
function checkWindowState(window) {
|
||||
let {windows: [{tabs}]} = JSON.parse(ss.getWindowState(window));
|
||||
|
@ -71,10 +65,3 @@ function shouldThrow(f) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function whenWindowClosed(window) {
|
||||
window.addEventListener("SSWindowClosing", function onClosing() {
|
||||
window.removeEventListener("SSWindowClosing", onClosing);
|
||||
executeSoon(next);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -73,8 +73,7 @@ add_task(function test_old_format() {
|
|||
|
||||
// Check that the form value is restored.
|
||||
let state = {entries: [{url: URL, formdata: {id: {input: VALUE}}}]};
|
||||
ss.setTabState(tab, JSON.stringify(state));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, state);
|
||||
is((yield getInputValue(browser, "input")), VALUE, "form data restored");
|
||||
|
||||
// Cleanup.
|
||||
|
@ -97,8 +96,7 @@ add_task(function test_old_format_inner_html() {
|
|||
|
||||
// Restore the tab state.
|
||||
let state = {entries: [{url: URL, innerHTML: VALUE}]};
|
||||
ss.setTabState(tab, JSON.stringify(state));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, state);
|
||||
|
||||
// Check that the innerHTML value was restored.
|
||||
let html = yield getInnerHTML(browser);
|
||||
|
@ -130,8 +128,7 @@ add_task(function test_url_check() {
|
|||
state.formdata.url = url;
|
||||
}
|
||||
|
||||
ss.setTabState(tab, JSON.stringify(state));
|
||||
return promiseTabRestored(tab).then(() => getInputValue(browser, "input"));
|
||||
return promiseTabState(tab, state).then(() => getInputValue(browser, "input"));
|
||||
}
|
||||
|
||||
// Check that the form value is restored with the correct URL.
|
||||
|
|
|
@ -75,9 +75,7 @@ function testTabRestoreData(aFormData, aExpectedValue, aCallback) {
|
|||
|
||||
Task.spawn(function () {
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, tabState);
|
||||
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
let restoredTabState = JSON.parse(ss.getTabState(tab));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests the API for saving global session data.
|
||||
function runTests() {
|
||||
add_task(function* () {
|
||||
const key1 = "Unique name 1: " + Date.now();
|
||||
const key2 = "Unique name 2: " + Date.now();
|
||||
const value1 = "Unique value 1: " + Math.random();
|
||||
|
@ -39,11 +39,7 @@ function runTests() {
|
|||
is(ss.getGlobalValue(key2), "", "global value was deleted");
|
||||
}
|
||||
|
||||
yield waitForBrowserState(testState, next);
|
||||
yield promiseBrowserState(testState);
|
||||
testRestoredState();
|
||||
testGlobalStore();
|
||||
}
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -47,8 +47,7 @@ add_task(function *test_history_cap() {
|
|||
|
||||
info("Testing situation where only a subset of session history entries should be restored.");
|
||||
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, tabState);
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
|
||||
let restoredTabState = JSON.parse(ss.getTabState(tab));
|
||||
|
@ -68,8 +67,7 @@ add_task(function *test_history_cap() {
|
|||
|
||||
info("Testing situation where all of the entries in the session history should be restored.");
|
||||
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, tabState);
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
|
||||
restoredTabState = JSON.parse(ss.getTabState(tab));
|
||||
|
@ -87,8 +85,7 @@ add_task(function *test_history_cap() {
|
|||
// Set the one-based tab-state index to the oldest session history entry.
|
||||
tabState.index = 1;
|
||||
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, tabState);
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
|
||||
restoredTabState = JSON.parse(ss.getTabState(tab));
|
||||
|
@ -106,8 +103,7 @@ add_task(function *test_history_cap() {
|
|||
// Set the one-based tab-state index to the newest session history entry.
|
||||
tabState.index = maxEntries;
|
||||
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, tabState);
|
||||
TabState.flush(tab.linkedBrowser);
|
||||
|
||||
restoredTabState = JSON.parse(ss.getTabState(tab));
|
||||
|
|
|
@ -68,9 +68,7 @@ add_task(function () {
|
|||
"docShell.QueryInterface%28Components.interfaces.nsILoadContext%29.usePrivateBrowsing%3Dtrue";
|
||||
|
||||
// Clear the list of closed windows.
|
||||
while (ss.getClosedWindowCount()) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
forgetClosedWindows();
|
||||
|
||||
// Create a new window to attach our frame script to.
|
||||
let win = yield promiseNewWindowLoaded();
|
||||
|
@ -109,9 +107,7 @@ add_task(function () {
|
|||
|
||||
add_task(function () {
|
||||
// Clear the list of closed windows.
|
||||
while (ss.getClosedWindowCount()) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
forgetClosedWindows();
|
||||
|
||||
// Create a new window to attach our frame script to.
|
||||
let win = yield promiseNewWindowLoaded({private: true});
|
||||
|
|
|
@ -115,8 +115,7 @@ add_task(function test_scroll_old_format() {
|
|||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
// Apply the tab state with the old format.
|
||||
ss.setTabState(tab, JSON.stringify(TAB_STATE));
|
||||
yield promiseTabRestored(tab);
|
||||
yield promiseTabState(tab, TAB_STATE);
|
||||
|
||||
// Check that the scroll positions has been applied.
|
||||
let scroll = yield sendMessage(browser, "ss-test:getScrollPosition");
|
||||
|
|
|
@ -20,7 +20,7 @@ add_task(function test_load_start() {
|
|||
// Undo close the tab.
|
||||
tab = ss.undoCloseTab(window, 0);
|
||||
browser = tab.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser);
|
||||
yield promiseTabRestored(tab);
|
||||
|
||||
// Check that the correct URL was restored.
|
||||
is(browser.currentURI.spec, "about:mozilla", "url is correct");
|
||||
|
|
|
@ -24,9 +24,7 @@ function gt(a, b, message) {
|
|||
}
|
||||
|
||||
add_task(function init() {
|
||||
for (let i = ss.getClosedWindowCount() - 1; i >= 0; --i) {
|
||||
ss.forgetClosedWindow(i);
|
||||
}
|
||||
forgetClosedWindows();
|
||||
for (let i = ss.getClosedTabCount(window) - 1; i >= 0; --i) {
|
||||
ss.forgetClosedTab(window, i);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@ function test() {
|
|||
waitForExplicitFinish();
|
||||
|
||||
// Purging the list of closed windows
|
||||
while(ss.getClosedWindowCount() > 0)
|
||||
ss.forgetClosedWindow(0);
|
||||
forgetClosedWindows();
|
||||
|
||||
// Load a private window, then close it
|
||||
// and verify it doesn't get remembered for restoring
|
||||
|
|
|
@ -172,29 +172,14 @@ function promiseBrowserState(aState) {
|
|||
return new Promise(resolve => waitForBrowserState(aState, resolve));
|
||||
}
|
||||
|
||||
// Doesn't assume that the tab needs to be closed in a cleanup function.
|
||||
// If that's the case, the test author should handle that in the test.
|
||||
function waitForTabState(aTab, aState, aCallback) {
|
||||
let listening = true;
|
||||
|
||||
function onSSTabRestored() {
|
||||
aTab.removeEventListener("SSTabRestored", onSSTabRestored, false);
|
||||
listening = false;
|
||||
aCallback();
|
||||
function promiseTabState(tab, state) {
|
||||
if (typeof(state) != "string") {
|
||||
state = JSON.stringify(state);
|
||||
}
|
||||
|
||||
aTab.addEventListener("SSTabRestored", onSSTabRestored, false);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
if (listening) {
|
||||
aTab.removeEventListener("SSTabRestored", onSSTabRestored, false);
|
||||
}
|
||||
});
|
||||
ss.setTabState(aTab, JSON.stringify(aState));
|
||||
}
|
||||
|
||||
function promiseTabState(tab, state) {
|
||||
return new Promise(resolve => waitForTabState(tab, state, resolve));
|
||||
let promise = promiseTabRestored(tab);
|
||||
ss.setTabState(tab, state);
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,55 +282,14 @@ let promiseForEachSessionRestoreFile = Task.async(function*(cb) {
|
|||
}
|
||||
});
|
||||
|
||||
function whenBrowserLoaded(aBrowser, aCallback = next, ignoreSubFrames = true, expectedURL = null) {
|
||||
aBrowser.messageManager.addMessageListener("ss-test:loadEvent", function onLoad(msg) {
|
||||
if (expectedURL && aBrowser.currentURI.spec != expectedURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ignoreSubFrames || !msg.data.subframe) {
|
||||
aBrowser.messageManager.removeMessageListener("ss-test:loadEvent", onLoad);
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
});
|
||||
}
|
||||
function promiseBrowserLoaded(aBrowser, ignoreSubFrames = true) {
|
||||
return new Promise(resolve => {
|
||||
whenBrowserLoaded(aBrowser, resolve, ignoreSubFrames);
|
||||
});
|
||||
}
|
||||
function whenBrowserUnloaded(aBrowser, aContainer, aCallback = next) {
|
||||
aBrowser.addEventListener("unload", function onUnload() {
|
||||
aBrowser.removeEventListener("unload", onUnload, true);
|
||||
executeSoon(aCallback);
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a page in a browser, and returns a Promise that
|
||||
* resolves once a "load" event has been fired within that
|
||||
* browser.
|
||||
*
|
||||
* @param browser
|
||||
* The browser to load the page in.
|
||||
* @param uri
|
||||
* The URI to load.
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
function loadPage(browser, uri) {
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.addEventListener("load", function onLoad(event) {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
resolve();
|
||||
}, true);
|
||||
browser.loadURI(uri);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseBrowserUnloaded(aBrowser, aContainer) {
|
||||
return new Promise(resolve => {
|
||||
whenBrowserUnloaded(aBrowser, aContainer, resolve);
|
||||
aBrowser.messageManager.addMessageListener("ss-test:loadEvent", function onLoad(msg) {
|
||||
if (!ignoreSubFrames || !msg.data.subframe) {
|
||||
aBrowser.messageManager.removeMessageListener("ss-test:loadEvent", onLoad);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -361,15 +305,6 @@ function promiseWindowLoaded(aWindow) {
|
|||
return new Promise(resolve => whenWindowLoaded(aWindow, resolve));
|
||||
}
|
||||
|
||||
function whenTabRestored(aTab, aCallback = next) {
|
||||
aTab.addEventListener("SSTabRestored", function onRestored(aEvent) {
|
||||
aTab.removeEventListener("SSTabRestored", onRestored, true);
|
||||
executeSoon(function executeWhenTabRestored() {
|
||||
aCallback();
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
var gUniqueCounter = 0;
|
||||
function r() {
|
||||
return Date.now() + "-" + (++gUniqueCounter);
|
||||
|
@ -464,14 +399,23 @@ registerCleanupFunction(function () {
|
|||
gProgressListener.unsetCallback();
|
||||
});
|
||||
|
||||
// Close everything but our primary window. We can't use waitForFocus()
|
||||
// because apparently it's buggy. See bug 599253.
|
||||
function closeAllButPrimaryWindow() {
|
||||
// Close all but our primary window.
|
||||
function promiseAllButPrimaryWindowClosed() {
|
||||
let windows = [];
|
||||
for (let win in BrowserWindowIterator()) {
|
||||
if (win != window) {
|
||||
win.close();
|
||||
windows.push(win);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(windows.map(promiseWindowClosed));
|
||||
}
|
||||
|
||||
// Forget all closed windows.
|
||||
function forgetClosedWindows() {
|
||||
while (ss.getClosedWindowCount() > 0) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -544,56 +488,6 @@ function promiseDelayedStartupFinished(aWindow) {
|
|||
return new Promise(resolve => whenDelayedStartupFinished(aWindow, resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* The test runner that controls the execution flow of our tests.
|
||||
*/
|
||||
let TestRunner = {
|
||||
_iter: null,
|
||||
|
||||
/**
|
||||
* Holds the browser state from before we started so
|
||||
* that we can restore it after all tests ran.
|
||||
*/
|
||||
backupState: {},
|
||||
|
||||
/**
|
||||
* Starts the test runner.
|
||||
*/
|
||||
run: function () {
|
||||
waitForExplicitFinish();
|
||||
|
||||
SessionStore.promiseInitialized.then(() => {
|
||||
this.backupState = JSON.parse(ss.getBrowserState());
|
||||
this._iter = runTests();
|
||||
this.next();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs the next available test or finishes if there's no test left.
|
||||
*/
|
||||
next: function () {
|
||||
try {
|
||||
TestRunner._iter.next();
|
||||
} catch (e if e instanceof StopIteration) {
|
||||
TestRunner.finish();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Finishes all tests and cleans up.
|
||||
*/
|
||||
finish: function () {
|
||||
closeAllButPrimaryWindow();
|
||||
gBrowser.selectedTab = gBrowser.tabs[0];
|
||||
waitForBrowserState(this.backupState, finish);
|
||||
}
|
||||
};
|
||||
|
||||
function next() {
|
||||
TestRunner.next();
|
||||
}
|
||||
|
||||
function promiseTabRestored(tab) {
|
||||
return new Promise(resolve => {
|
||||
tab.addEventListener("SSTabRestored", function onRestored() {
|
||||
|
|
|
@ -1559,20 +1559,20 @@ this.UITour = {
|
|||
|
||||
getConfiguration: function(aMessageManager, aWindow, aConfiguration, aCallbackID) {
|
||||
switch (aConfiguration) {
|
||||
case "availableTargets":
|
||||
this.getAvailableTargets(aMessageManager, aWindow, aCallbackID);
|
||||
break;
|
||||
case "sync":
|
||||
this.sendPageCallback(aMessageManager, aCallbackID, {
|
||||
setup: Services.prefs.prefHasUserValue("services.sync.username"),
|
||||
});
|
||||
break;
|
||||
case "appinfo":
|
||||
let props = ["defaultUpdateChannel", "version"];
|
||||
let appinfo = {};
|
||||
props.forEach(property => appinfo[property] = Services.appinfo[property]);
|
||||
this.sendPageCallback(aMessageManager, aCallbackID, appinfo);
|
||||
break;
|
||||
case "availableTargets":
|
||||
this.getAvailableTargets(aMessageManager, aWindow, aCallbackID);
|
||||
break;
|
||||
case "loop":
|
||||
this.sendPageCallback(aMessageManager, aCallbackID, {
|
||||
gettingStartedSeen: Services.prefs.getBoolPref("loop.gettingStarted.seen"),
|
||||
});
|
||||
break;
|
||||
case "selectedSearchEngine":
|
||||
Services.search.init(rv => {
|
||||
let engine;
|
||||
|
@ -1586,6 +1586,11 @@ this.UITour = {
|
|||
});
|
||||
});
|
||||
break;
|
||||
case "sync":
|
||||
this.sendPageCallback(aMessageManager, aCallbackID, {
|
||||
setup: Services.prefs.prefHasUserValue("services.sync.username"),
|
||||
});
|
||||
break;
|
||||
default:
|
||||
log.error("getConfiguration: Unknown configuration requested: " + aConfiguration);
|
||||
break;
|
||||
|
|
|
@ -60,6 +60,14 @@ let tests = [
|
|||
done();
|
||||
});
|
||||
},
|
||||
function test_getConfigurationLoop(done) {
|
||||
let gettingStartedSeen = Services.prefs.getBoolPref("loop.gettingStarted.seen");
|
||||
gContentAPI.getConfiguration("loop", (data) => {
|
||||
is(data.gettingStartedSeen, gettingStartedSeen,
|
||||
"The configuration property should equal that of the pref");
|
||||
done();
|
||||
});
|
||||
},
|
||||
function test_hideMenuHidesAnnotations(done) {
|
||||
let infoPanel = document.getElementById("UITourTooltip");
|
||||
let highlightPanel = document.getElementById("UITourHighlightContainer");
|
||||
|
|
|
@ -98,6 +98,7 @@ browser.jar:
|
|||
content/browser/devtools/performance/views/overview.js (performance/views/overview.js)
|
||||
content/browser/devtools/performance/views/toolbar.js (performance/views/toolbar.js)
|
||||
content/browser/devtools/performance/views/details.js (performance/views/details.js)
|
||||
content/browser/devtools/performance/views/details-subview.js (performance/views/details-abstract-subview.js)
|
||||
content/browser/devtools/performance/views/details-call-tree.js (performance/views/details-call-tree.js)
|
||||
content/browser/devtools/performance/views/details-waterfall.js (performance/views/details-waterfall.js)
|
||||
content/browser/devtools/performance/views/details-flamegraph.js (performance/views/details-flamegraph.js)
|
||||
|
|
|
@ -57,3 +57,43 @@ exports.RecordingUtils.offsetMarkerTimes = function(markers, timeOffset) {
|
|||
marker.end -= timeOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts allocation data from the memory actor to something that follows
|
||||
* the same structure as the samples data received from the profiler.
|
||||
*
|
||||
* @see MemoryActor.prototype.getAllocations for more information.
|
||||
*
|
||||
* @param object allocations
|
||||
* A list of { sites, timestamps, frames, counts } arrays.
|
||||
* @return array
|
||||
* The samples data.
|
||||
*/
|
||||
exports.RecordingUtils.getSamplesFromAllocations = function(allocations) {
|
||||
let { sites, timestamps, frames, counts } = allocations;
|
||||
let samples = [];
|
||||
|
||||
for (let i = 0, len = sites.length; i < len; i++) {
|
||||
let site = sites[i];
|
||||
let timestamp = timestamps[i];
|
||||
let frame = frames[site];
|
||||
let count = counts[site];
|
||||
|
||||
let sample = { time: timestamp, frames: [] };
|
||||
samples.push(sample);
|
||||
|
||||
while (frame) {
|
||||
sample.frames.push({
|
||||
location: frame.source + ":" + frame.line + ":" + frame.column,
|
||||
allocations: count
|
||||
});
|
||||
site = frame.parent;
|
||||
frame = frames[site];
|
||||
count = counts[site];
|
||||
}
|
||||
|
||||
sample.frames.reverse();
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
<script type="application/javascript" src="performance/recording-model.js"/>
|
||||
<script type="application/javascript" src="performance/views/overview.js"/>
|
||||
<script type="application/javascript" src="performance/views/toolbar.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-subview.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-call-tree.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-waterfall.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-flamegraph.js"/>
|
||||
|
|
|
@ -9,6 +9,7 @@ support-files =
|
|||
# that need to be moved over to performance tool
|
||||
|
||||
[browser_perf-aaa-run-first-leaktest.js]
|
||||
[browser_perf-allocations-to-samples.js]
|
||||
[browser_perf-data-massaging-01.js]
|
||||
[browser_perf-data-samples.js]
|
||||
[browser_perf-details-calltree-render.js]
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if allocations data received from the memory actor is properly
|
||||
* converted to something that follows the same structure as the samples data
|
||||
* received from the profiler.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { RecordingUtils } = devtools.require("devtools/performance/recording-utils");
|
||||
|
||||
let output = RecordingUtils.getSamplesFromAllocations(TEST_DATA);
|
||||
is(output.toSource(), EXPECTED_OUTPUT.toSource(), "The output is correct.");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
let TEST_DATA = {
|
||||
sites: [0, 0, 1, 2, 3],
|
||||
timestamps: [50, 100, 150, 200, 250],
|
||||
frames: [
|
||||
null,
|
||||
{
|
||||
source: "A",
|
||||
line: 1,
|
||||
column: 2,
|
||||
parent: 0
|
||||
},
|
||||
{
|
||||
source: "B",
|
||||
line: 3,
|
||||
column: 4,
|
||||
parent: 1
|
||||
},
|
||||
{
|
||||
source: "C",
|
||||
line: 5,
|
||||
column: 6,
|
||||
parent: 2
|
||||
}
|
||||
],
|
||||
counts: [11, 22, 33, 44]
|
||||
};
|
||||
|
||||
let EXPECTED_OUTPUT = [{
|
||||
time: 50,
|
||||
frames: []
|
||||
}, {
|
||||
time: 100,
|
||||
frames: []
|
||||
}, {
|
||||
time: 150,
|
||||
frames: [{
|
||||
location: "A:1:2",
|
||||
allocations: 22
|
||||
}]
|
||||
}, {
|
||||
time: 200,
|
||||
frames: [{
|
||||
location: "A:1:2",
|
||||
allocations: 22
|
||||
}, {
|
||||
location: "B:3:4",
|
||||
allocations: 33
|
||||
}]
|
||||
}, {
|
||||
time: 250,
|
||||
frames: [{
|
||||
location: "A:1:2",
|
||||
allocations: 22
|
||||
}, {
|
||||
location: "B:3:4",
|
||||
allocations: 33
|
||||
}, {
|
||||
location: "C:5:6",
|
||||
allocations: 44
|
||||
}]
|
||||
}];
|
|
@ -37,10 +37,10 @@ function spawnTest () {
|
|||
}
|
||||
|
||||
function checkViews (DetailsView, doc, currentView) {
|
||||
for (let viewName in DetailsView.viewIndexes) {
|
||||
for (let viewName in DetailsView.components) {
|
||||
let button = doc.querySelector(`toolbarbutton[data-view="${viewName}"]`);
|
||||
|
||||
is(DetailsView.el.selectedIndex, DetailsView.viewIndexes[currentView],
|
||||
is(DetailsView.el.selectedPanel.id, DetailsView.components[currentView].id,
|
||||
`DetailsView correctly has ${currentView} selected.`);
|
||||
if (viewName === currentView) {
|
||||
ok(button.getAttribute("checked"), `${viewName} button checked`);
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, CallTreeView } = panel.panelWin;
|
||||
let { EVENTS, DetailsView, CallTreeView } = panel.panelWin;
|
||||
|
||||
DetailsView.selectView("calltree");
|
||||
ok(DetailsView.isViewSelected(CallTreeView), "The call tree is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, FlameGraphView } = panel.panelWin;
|
||||
let { EVENTS, DetailsView, FlameGraphView } = panel.panelWin;
|
||||
|
||||
DetailsView.selectView("flamegraph");
|
||||
ok(DetailsView.isViewSelected(FlameGraphView), "The flamegraph is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, PerformanceController, WaterfallView } = panel.panelWin;
|
||||
let { EVENTS, PerformanceController, DetailsView, WaterfallView } = panel.panelWin;
|
||||
|
||||
ok(DetailsView.isViewSelected(WaterfallView),
|
||||
"The waterfall view is selected by default in the details view.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
|
||||
|
|
|
@ -8,10 +8,13 @@ const INVERT_PREF = "devtools.performance.ui.invert-call-tree";
|
|||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, CallTreeView } = panel.panelWin;
|
||||
let { EVENTS, DetailsView, CallTreeView } = panel.panelWin;
|
||||
|
||||
Services.prefs.setBoolPref(INVERT_PREF, true);
|
||||
|
||||
DetailsView.selectView("calltree");
|
||||
ok(DetailsView.isViewSelected(CallTreeView), "The call tree is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
|
|
|
@ -52,8 +52,8 @@ function spawnTest () {
|
|||
ok(true, "Waterfall rerenders after its corresponding pane is shown.");
|
||||
|
||||
is(updatedWaterfall, 3, "WaterfallView rerendered 3 times.");
|
||||
is(updatedCallTree, 3, "CallTreeView rerendered 3 times.");
|
||||
is(updatedFlameGraph, 3, "FlameGraphView rerendered 3 times.");
|
||||
is(updatedCallTree, 2, "CallTreeView rerendered 2 times.");
|
||||
is(updatedFlameGraph, 2, "FlameGraphView rerendered 2 times.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that all components get rerendered for a profile when switching.
|
||||
* Tests that all components can get rerendered for a profile when switching.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||
let { $, EVENTS, PerformanceController, RecordingsView } = panel.panelWin;
|
||||
let { $, EVENTS, PerformanceController, DetailsSubview, RecordingsView } = panel.panelWin;
|
||||
|
||||
DetailsSubview.canUpdateWhileHidden = true;
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
let test = Task.async(function*() {
|
||||
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, PerformanceController } = panel.panelWin;
|
||||
let { EVENTS, PerformanceController, DetailsSubview } = panel.panelWin;
|
||||
|
||||
DetailsSubview.canUpdateWhileHidden = true;
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
let test = Task.async(function*() {
|
||||
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, PerformanceController } = panel.panelWin;
|
||||
let { EVENTS, PerformanceController, DetailsSubview } = panel.panelWin;
|
||||
|
||||
DetailsSubview.canUpdateWhileHidden = true;
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/* 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";
|
||||
|
||||
/**
|
||||
* A base class from which all detail views inherit.
|
||||
*/
|
||||
let DetailsSubview = {
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: function () {
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onOverviewRangeChange = this._onOverviewRangeChange.bind(this);
|
||||
this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onOverviewRangeChange);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
clearNamedTimeout("range-change-debounce");
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onOverviewRangeChange);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
* Amount of time (in milliseconds) to wait until this view gets updated,
|
||||
* when the range is changed in the overview.
|
||||
*/
|
||||
rangeChangeDebounceTime: 0,
|
||||
|
||||
/**
|
||||
* Flag specifying if this view should be updated when selected. This will
|
||||
* be set to true, for example, when the range changes in the overview and
|
||||
* this view is not currently visible.
|
||||
*/
|
||||
shouldUpdateWhenShown: false,
|
||||
|
||||
/**
|
||||
* Flag specifying if this view may get updated even when it's not selected.
|
||||
* Should only be used in tests.
|
||||
*/
|
||||
canUpdateWhileHidden: false,
|
||||
|
||||
/**
|
||||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function(_, recording) {
|
||||
if (recording.isRecording()) {
|
||||
return;
|
||||
}
|
||||
if (DetailsView.isViewSelected(this) || this.canUpdateWhileHidden) {
|
||||
this.render();
|
||||
} else {
|
||||
this.shouldUpdateWhenShown = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onOverviewRangeChange: function (_, interval) {
|
||||
if (DetailsView.isViewSelected(this)) {
|
||||
let debounced = () => this.render(interval);
|
||||
setNamedTimeout("range-change-debounce", this.rangeChangeDebounceTime, debounced);
|
||||
} else {
|
||||
this.shouldUpdateWhenShown = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a view is selected in the DetailsView.
|
||||
*/
|
||||
_onDetailsViewSelected: function() {
|
||||
if (DetailsView.isViewSelected(this) && this.shouldUpdateWhenShown) {
|
||||
this.render(OverviewView.getTimeInterval());
|
||||
this.shouldUpdateWhenShown = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenient way of emitting events from the view.
|
||||
*/
|
||||
EventEmitter.decorate(DetailsSubview);
|
|
@ -3,42 +3,31 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const CALLTREE_UPDATE_DEBOUNCE = 50; // ms
|
||||
|
||||
/**
|
||||
* CallTree view containing profiler call tree, controlled by DetailsView.
|
||||
*/
|
||||
let CallTreeView = {
|
||||
let CallTreeView = Heritage.extend(DetailsSubview, {
|
||||
rangeChangeDebounceTime: 50, // ms
|
||||
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: function () {
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
|
||||
DetailsSubview.initialize.call(this);
|
||||
|
||||
this._onPrefChanged = this._onPrefChanged.bind(this);
|
||||
this._onLink = this._onLink.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
clearNamedTimeout("calltree-update");
|
||||
DetailsSubview.destroy.call(this);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -57,38 +46,6 @@ let CallTreeView = {
|
|||
this.emit(EVENTS.CALL_TREE_RENDERED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when recording is stopped or has been selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
if (!recording.isRecording()) {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onRangeChange: function (_, interval) {
|
||||
if (DetailsView.isViewSelected(this)) {
|
||||
let debounced = () => this.render(interval);
|
||||
setNamedTimeout("calltree-update", CALLTREE_UPDATE_DEBOUNCE, debounced);
|
||||
} else {
|
||||
this._dirty = true;
|
||||
this._interval = interval;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a view is selected in the DetailsView.
|
||||
*/
|
||||
_onDetailsViewSelected: function() {
|
||||
if (DetailsView.isViewSelected(this) && this._dirty) {
|
||||
this.render(this._interval);
|
||||
this._dirty = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired on the "link" event for the call tree in this container.
|
||||
*/
|
||||
|
@ -155,12 +112,7 @@ let CallTreeView = {
|
|||
this.render(OverviewView.getTimeInterval());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenient way of emitting events from the view.
|
||||
*/
|
||||
EventEmitter.decorate(CallTreeView);
|
||||
});
|
||||
|
||||
/**
|
||||
* Opens/selects the debugger in this toolbox and jumps to the specified
|
||||
|
|
|
@ -7,40 +7,29 @@
|
|||
* FlameGraph view containing a pyramid-like visualization of a profile,
|
||||
* controlled by DetailsView.
|
||||
*/
|
||||
let FlameGraphView = {
|
||||
let FlameGraphView = Heritage.extend(DetailsSubview, {
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: Task.async(function* () {
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
|
||||
this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
|
||||
DetailsSubview.initialize.call(this);
|
||||
|
||||
this.graph = new FlameGraph($("#flamegraph-view"));
|
||||
this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
|
||||
yield this.graph.ready();
|
||||
|
||||
this.graph.on("selecting", this._onRangeChangeInGraph);
|
||||
this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
this.graph.on("selecting", this._onRangeChangeInGraph);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
this.graph.off("selecting", this._onRangeChangeInGraph);
|
||||
DetailsSubview.destroy.call(this);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
this.graph.off("selecting", this._onRangeChangeInGraph);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -51,42 +40,28 @@ let FlameGraphView = {
|
|||
*/
|
||||
render: function (interval={}) {
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
let startTime = interval.startTime || 0;
|
||||
let endTime = interval.endTime || recording.getDuration();
|
||||
this.graph.setViewRange({ startTime, endTime });
|
||||
this.emit(EVENTS.FLAMEGRAPH_RENDERED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when recording is stopped or selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
if (recording.isRecording()) {
|
||||
return;
|
||||
}
|
||||
let duration = recording.getDuration();
|
||||
let profile = recording.getProfile();
|
||||
let samples = profile.threads[0].samples;
|
||||
|
||||
let data = FlameGraphUtils.createFlameGraphDataFromSamples(samples, {
|
||||
flattenRecursion: Prefs.flattenTreeRecursion,
|
||||
filterFrames: !Prefs.showPlatformData && FrameNode.isContent,
|
||||
showIdleBlocks: Prefs.showIdleBlocks && L10N.getStr("table.idle")
|
||||
});
|
||||
let startTime = 0;
|
||||
let endTime = recording.getDuration();
|
||||
this.graph.setData({ data, bounds: { startTime, endTime } });
|
||||
this.render();
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onRangeChange: function (_, interval) {
|
||||
if (DetailsView.isViewSelected(this)) {
|
||||
this.render(interval);
|
||||
} else {
|
||||
this._dirty = true;
|
||||
this._interval = interval;
|
||||
}
|
||||
this.graph.setData({ data,
|
||||
bounds: {
|
||||
startTime: 0,
|
||||
endTime: duration
|
||||
},
|
||||
visible: {
|
||||
startTime: interval.startTime || 0,
|
||||
endTime: interval.endTime || duration
|
||||
}
|
||||
});
|
||||
|
||||
this.emit(EVENTS.FLAMEGRAPH_RENDERED);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -95,20 +70,5 @@ let FlameGraphView = {
|
|||
_onRangeChangeInGraph: function () {
|
||||
let interval = this.graph.getViewRange();
|
||||
OverviewView.setTimeInterval(interval, { stopPropagation: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a view is selected in the DetailsView.
|
||||
*/
|
||||
_onDetailsViewSelected: function() {
|
||||
if (DetailsView.isViewSelected(this) && this._dirty) {
|
||||
this.render(this._interval);
|
||||
this._dirty = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenient way of emitting events from the view.
|
||||
*/
|
||||
EventEmitter.decorate(FlameGraphView);
|
||||
});
|
||||
|
|
|
@ -3,56 +3,45 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const WATERFALL_UPDATE_DEBOUNCE = 10; // ms
|
||||
|
||||
/**
|
||||
* Waterfall view containing the timeline markers, controlled by DetailsView.
|
||||
*/
|
||||
let WaterfallView = {
|
||||
let WaterfallView = Heritage.extend(DetailsSubview, {
|
||||
rangeChangeDebounceTime: 10, // ms
|
||||
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: Task.async(function *() {
|
||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
|
||||
this._onMarkerSelected = this._onMarkerSelected.bind(this);
|
||||
this._onResize = this._onResize.bind(this);
|
||||
initialize: function () {
|
||||
DetailsSubview.initialize.call(this);
|
||||
|
||||
this.waterfall = new Waterfall($("#waterfall-breakdown"), $("#details-pane"), TIMELINE_BLUEPRINT);
|
||||
this.details = new MarkerDetails($("#waterfall-details"), $("#waterfall-view > splitter"));
|
||||
|
||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||
this._onMarkerSelected = this._onMarkerSelected.bind(this);
|
||||
this._onResize = this._onResize.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
|
||||
this.waterfall.on("selected", this._onMarkerSelected);
|
||||
this.waterfall.on("unselected", this._onMarkerSelected);
|
||||
this.details.on("resize", this._onResize);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
|
||||
this.waterfall.recalculateBounds();
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
clearNamedTimeout("waterfall-update");
|
||||
DetailsSubview.destroy.call(this);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
|
||||
this.waterfall.off("selected", this._onMarkerSelected);
|
||||
this.waterfall.off("unselected", this._onMarkerSelected);
|
||||
this.details.off("resize", this._onResize);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -77,38 +66,6 @@ let WaterfallView = {
|
|||
this.waterfall.clearView();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
if (!recording.isRecording()) {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onRangeChange: function (_, interval) {
|
||||
if (DetailsView.isViewSelected(this)) {
|
||||
let debounced = () => this.render(interval);
|
||||
setNamedTimeout("waterfall-update", WATERFALL_UPDATE_DEBOUNCE, debounced);
|
||||
} else {
|
||||
this._dirty = true;
|
||||
this._interval = interval;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a view is selected in the DetailsView.
|
||||
*/
|
||||
_onDetailsViewSelected: function() {
|
||||
if (DetailsView.isViewSelected(this) && this._dirty) {
|
||||
this.render(this._interval);
|
||||
this._dirty = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a marker is selected in the waterfall view,
|
||||
* updating the markers detail view.
|
||||
|
@ -132,9 +89,4 @@ let WaterfallView = {
|
|||
this.waterfall.recalculateBounds();
|
||||
this.render();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenient way of emitting events from the view.
|
||||
*/
|
||||
EventEmitter.decorate(WaterfallView);
|
||||
});
|
||||
|
|
|
@ -11,12 +11,12 @@ const DEFAULT_DETAILS_SUBVIEW = "waterfall";
|
|||
*/
|
||||
let DetailsView = {
|
||||
/**
|
||||
* Name to index mapping of subviews, used by selecting view.
|
||||
* Name to node+object mapping of subviews.
|
||||
*/
|
||||
components: {
|
||||
waterfall: { index: 0, view: WaterfallView },
|
||||
calltree: { index: 1, view: CallTreeView },
|
||||
flamegraph: { index: 2, view: FlameGraphView }
|
||||
waterfall: { id: "waterfall-view", view: WaterfallView },
|
||||
calltree: { id: "calltree-view", view: CallTreeView },
|
||||
flamegraph: { id: "flamegraph-view", view: FlameGraphView }
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -32,9 +32,9 @@ let DetailsView = {
|
|||
button.addEventListener("command", this._onViewToggle);
|
||||
}
|
||||
|
||||
yield WaterfallView.initialize();
|
||||
yield CallTreeView.initialize();
|
||||
yield FlameGraphView.initialize();
|
||||
for (let [_, { view }] of Iterator(this.components)) {
|
||||
yield view.initialize();
|
||||
}
|
||||
|
||||
this.selectView(DEFAULT_DETAILS_SUBVIEW);
|
||||
}),
|
||||
|
@ -47,9 +47,9 @@ let DetailsView = {
|
|||
button.removeEventListener("command", this._onViewToggle);
|
||||
}
|
||||
|
||||
yield WaterfallView.destroy();
|
||||
yield CallTreeView.destroy();
|
||||
yield FlameGraphView.destroy();
|
||||
for (let [_, { view }] of Iterator(this.components)) {
|
||||
yield view.destroy();
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -60,7 +60,7 @@ let DetailsView = {
|
|||
* Name of the view to be shown.
|
||||
*/
|
||||
selectView: function (viewName) {
|
||||
this.el.selectedIndex = this.components[viewName].index;
|
||||
this.el.selectedPanel = $("#" + this.components[viewName].id);
|
||||
|
||||
for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
|
||||
if (button.getAttribute("data-view") === viewName) {
|
||||
|
@ -80,10 +80,11 @@ let DetailsView = {
|
|||
* @return boolean
|
||||
*/
|
||||
isViewSelected: function(viewObject) {
|
||||
let selectedIndex = this.el.selectedIndex;
|
||||
let selectedPanel = this.el.selectedPanel;
|
||||
let selectedId = selectedPanel.id;
|
||||
|
||||
for (let [, { index, view }] of Iterator(this.components)) {
|
||||
if (index == selectedIndex && view == viewObject) {
|
||||
for (let [, { id, view }] of Iterator(this.components)) {
|
||||
if (id == selectedId && view == viewObject) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ support-files =
|
|||
[browser_flame-graph-utils-02.js]
|
||||
[browser_flame-graph-utils-03.js]
|
||||
[browser_flame-graph-utils-04.js]
|
||||
[browser_flame-graph-utils-05.js]
|
||||
[browser_flame-graph-utils-hash.js]
|
||||
[browser_graphs-01.js]
|
||||
[browser_graphs-02.js]
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that flame graph data is cached, and that the cache may be cleared.
|
||||
|
||||
let {FlameGraphUtils} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
|
||||
|
||||
add_task(function*() {
|
||||
yield promiseTab("about:blank");
|
||||
yield performTest();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let out1 = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA);
|
||||
let out2 = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA);
|
||||
is(out1, out2, "The outputted data is identical.")
|
||||
|
||||
let out3 = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA, { flattenRecursion: true });
|
||||
is(out2, out3, "The outputted data is still identical.");
|
||||
|
||||
FlameGraphUtils.removeFromCache(TEST_DATA);
|
||||
let out4 = FlameGraphUtils.createFlameGraphDataFromSamples(TEST_DATA, { flattenRecursion: true });
|
||||
isnot(out3, out4, "The outputted data is not identical anymore.");
|
||||
}
|
||||
|
||||
let TEST_DATA = [{
|
||||
frames: [{
|
||||
location: "A"
|
||||
}, {
|
||||
location: "A"
|
||||
}, {
|
||||
location: "A"
|
||||
}, {
|
||||
location: "B",
|
||||
}, {
|
||||
location: "B",
|
||||
}, {
|
||||
location: "C"
|
||||
}],
|
||||
time: 50,
|
||||
}];
|
|
@ -856,10 +856,16 @@ const COLOR_PALLETTE = Array.from(Array(PALLETTE_SIZE)).map((_, i) => "hsla" +
|
|||
* into a format drawable by the FlameGraph.
|
||||
*/
|
||||
let FlameGraphUtils = {
|
||||
_cache: new WeakMap(),
|
||||
|
||||
/**
|
||||
* Converts a list of samples from the profiler data to something that's
|
||||
* drawable by a FlameGraph widget.
|
||||
*
|
||||
* The outputted data will be cached, so the next time this method is called
|
||||
* the previous output is returned. If this is undesirable, or should the
|
||||
* options change, use `removeFromCache`.
|
||||
*
|
||||
* @param array samples
|
||||
* A list of { time, frames: [{ location }] } objects.
|
||||
* @param object options [optional]
|
||||
|
@ -876,6 +882,11 @@ let FlameGraphUtils = {
|
|||
* The flame graph data.
|
||||
*/
|
||||
createFlameGraphDataFromSamples: function(samples, options = {}, out = []) {
|
||||
let cached = this._cache.get(samples);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// 1. Create a map of colors to arrays, representing buckets of
|
||||
// blocks inside the flame graph pyramid sharing the same style.
|
||||
|
||||
|
@ -952,9 +963,18 @@ let FlameGraphUtils = {
|
|||
out.push({ color, blocks });
|
||||
}
|
||||
|
||||
this._cache.set(samples, out);
|
||||
return out;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the cached flame graph data created for the given source.
|
||||
* @param any source
|
||||
*/
|
||||
removeFromCache: function(source) {
|
||||
this._cache.delete(source);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the provided frame is the same as the next one in a sample.
|
||||
*
|
||||
|
|
|
@ -42,3 +42,7 @@
|
|||
-->
|
||||
<!ENTITY translation.options.attribution.beforeLogo "Translations by">
|
||||
<!ENTITY translation.options.attribution.afterLogo "">
|
||||
|
||||
<!ENTITY playDRMContent.label "Play DRM content">
|
||||
<!ENTITY playDRMContent.accesskey "P">
|
||||
<!ENTITY playDRMContent.learnMore.label "Learn more">
|
||||
|
|
|
@ -45,7 +45,10 @@ let ReaderParent = {
|
|||
|
||||
case "Reader:ArticleGet":
|
||||
this._getArticle(message.data.url, message.target).then((article) => {
|
||||
message.target.messageManager.sendAsyncMessage("Reader:ArticleData", { article: article });
|
||||
// Make sure the target browser is still alive before trying to send data back.
|
||||
if (message.target.messageManager) {
|
||||
message.target.messageManager.sendAsyncMessage("Reader:ArticleData", { article: article });
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
|
|
|
@ -846,3 +846,8 @@ pref("dom.meta-viewport.enabled", true);
|
|||
|
||||
// Enable the OpenH264 plugin support in the addon manager.
|
||||
pref("media.gmp-gmpopenh264.provider.enabled", true);
|
||||
|
||||
// The default color scheme in reader mode (light, dark, print, auto)
|
||||
// auto = color automatically adjusts according to ambient light level
|
||||
// (auto only works on platforms where the 'devicelight' event is enabled)
|
||||
pref("reader.color_scheme", "auto");
|
||||
|
|
|
@ -287,4 +287,7 @@ public class AppConstants {
|
|||
//#else
|
||||
false;
|
||||
//#endif
|
||||
|
||||
public static final boolean MOZ_DRAGGABLE_URLBAR = false;
|
||||
|
||||
}
|
||||
|
|
|
@ -259,6 +259,175 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
private final DynamicToolbar mDynamicToolbar = new DynamicToolbar();
|
||||
|
||||
private DragHelper mDragHelper;
|
||||
|
||||
private class DragHelper implements OuterLayout.DragCallback {
|
||||
private int[] mToolbarLocation = new int[2]; // to avoid creation every time we need to check for toolbar location.
|
||||
// When dragging horizontally, the area of mainlayout between left drag bound and right drag bound can
|
||||
// be dragged. A touch on the right of that area will automatically close the view.
|
||||
private int mStatusBarHeight;
|
||||
|
||||
public DragHelper() {
|
||||
// If a layout round happens from the root, the offset placed by viewdraghelper gets forgotten and
|
||||
// main layout gets replaced to offset 0.
|
||||
((MainLayout) mMainLayout).setLayoutInterceptor(new LayoutInterceptor() {
|
||||
@Override
|
||||
public void onLayout() {
|
||||
if (mRootLayout.isMoving()) {
|
||||
mRootLayout.restoreTargetViewPosition();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragProgress(float progress) {
|
||||
mBrowserToolbar.setToolBarButtonsAlpha(1.0f - progress);
|
||||
mTabsPanel.translateInRange(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getViewToDrag() {
|
||||
return mMainLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since pressing the tabs button slides the main layout, whereas draghelper changes its offset, here we
|
||||
* restore the position of mainlayout as if it was opened by pressing the button. This allows the closing
|
||||
* mechanism to work.
|
||||
*/
|
||||
@Override
|
||||
public void startDrag(boolean wasOpen) {
|
||||
if (wasOpen) {
|
||||
mTabsPanel.setHWLayerEnabled(true);
|
||||
mMainLayout.offsetTopAndBottom(getDragRange());
|
||||
mMainLayout.scrollTo(0, 0);
|
||||
} else {
|
||||
prepareTabsToShow();
|
||||
mBrowserToolbar.hideVirtualKeyboard();
|
||||
}
|
||||
mBrowserToolbar.setContextMenuEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopDrag(boolean stoppingToOpen) {
|
||||
if (stoppingToOpen) {
|
||||
mTabsPanel.setHWLayerEnabled(false);
|
||||
mMainLayout.offsetTopAndBottom(-getDragRange());
|
||||
mMainLayout.scrollTo(0, -getDragRange());
|
||||
} else {
|
||||
mTabsPanel.hideImmediately();
|
||||
mTabsPanel.setHWLayerEnabled(false);
|
||||
}
|
||||
// Re-enabling context menu only while stopping to close.
|
||||
if (stoppingToOpen) {
|
||||
mBrowserToolbar.setContextMenuEnabled(false);
|
||||
} else {
|
||||
mBrowserToolbar.setContextMenuEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDragRange() {
|
||||
return mTabsPanel.getVerticalPanelHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrderedChildIndex(int index) {
|
||||
// See ViewDragHelper's findTopChildUnder method. ViewDragHelper looks for the topmost view in z order
|
||||
// to understand what needs to be dragged. Here we are tampering Toast's index in case it's hidden,
|
||||
// otherwise draghelper would try to drag it.
|
||||
int mainLayoutIndex = mRootLayout.indexOfChild(mMainLayout);
|
||||
if (index > mainLayoutIndex && (mToast == null || !mToast.isVisible())) {
|
||||
return mainLayoutIndex;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDrag(MotionEvent event) {
|
||||
if (!AppConstants.MOZ_DRAGGABLE_URLBAR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if no current tab is active.
|
||||
if (Tabs.getInstance().getSelectedTab() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// currently disabled for tablets.
|
||||
if (HardwareUtils.isTablet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// not enabled in editing mode.
|
||||
if (mBrowserToolbar.isEditing()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isInToolbarBounds((int) event.getRawY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInterceptEventWhileOpen(MotionEvent event) {
|
||||
if (event.getActionMasked() != MotionEvent.ACTION_DOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Need to check if are intercepting a touch on main layout since we might hit a visible toast.
|
||||
if (mRootLayout.findTopChildUnder(event) == mMainLayout &&
|
||||
isInToolbarBounds((int) event.getRawY())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isInToolbarBounds(int y) {
|
||||
mBrowserToolbar.getLocationOnScreen(mToolbarLocation);
|
||||
final int upperLimit = mToolbarLocation[1] + mBrowserToolbar.getMeasuredHeight();
|
||||
final int lowerLimit = mToolbarLocation[1];
|
||||
return (y > lowerLimit && y < upperLimit);
|
||||
}
|
||||
|
||||
public void prepareTabsToShow() {
|
||||
if (ensureTabsPanelExists()) {
|
||||
// If we've just inflated the tabs panel, only show it once the current
|
||||
// layout pass is done to avoid displayed temporary UI states during
|
||||
// relayout.
|
||||
final ViewTreeObserver vto = mTabsPanel.getViewTreeObserver();
|
||||
if (vto.isAlive()) {
|
||||
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
prepareTabsToShow();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
mTabsPanel.prepareToDrag();
|
||||
}
|
||||
}
|
||||
|
||||
public int getLowerLimit() {
|
||||
return getStatusBarHeight();
|
||||
}
|
||||
|
||||
private int getStatusBarHeight() {
|
||||
if (mStatusBarHeight != 0) {
|
||||
return mStatusBarHeight;
|
||||
}
|
||||
final int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
mStatusBarHeight = getResources().getDimensionPixelSize(resourceId);
|
||||
return mStatusBarHeight;
|
||||
}
|
||||
Log.e(LOGTAG, "Unable to find statusbar height");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(final String name, final Context context, final AttributeSet attrs) {
|
||||
final View view;
|
||||
|
@ -658,6 +827,9 @@ public class BrowserApp extends GeckoApp
|
|||
}
|
||||
});
|
||||
|
||||
mDragHelper = new DragHelper();
|
||||
mRootLayout.setDraggableCallback(mDragHelper);
|
||||
|
||||
// Set the maximum bits-per-pixel the favicon system cares about.
|
||||
IconDirectoryEntry.setMaxBPP(GeckoAppShell.getScreenDepth());
|
||||
}
|
||||
|
@ -1358,6 +1530,7 @@ public class BrowserApp extends GeckoApp
|
|||
invalidateOptionsMenu();
|
||||
|
||||
if (mTabsPanel != null) {
|
||||
mRootLayout.reset();
|
||||
updateSideBarState();
|
||||
mTabsPanel.refresh();
|
||||
}
|
||||
|
@ -1381,6 +1554,10 @@ public class BrowserApp extends GeckoApp
|
|||
});
|
||||
}
|
||||
|
||||
private boolean isSideBar() {
|
||||
return (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
|
||||
}
|
||||
|
||||
private void updateSideBarState() {
|
||||
if (NewTabletUI.isEnabled(this)) {
|
||||
return;
|
||||
|
@ -1389,7 +1566,7 @@ public class BrowserApp extends GeckoApp
|
|||
if (mMainLayoutAnimator != null)
|
||||
mMainLayoutAnimator.stop();
|
||||
|
||||
boolean isSideBar = (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
|
||||
boolean isSideBar = isSideBar();
|
||||
final int sidebarWidth = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width);
|
||||
|
||||
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mTabsPanel.getLayoutParams();
|
||||
|
@ -1401,6 +1578,7 @@ public class BrowserApp extends GeckoApp
|
|||
mMainLayout.scrollTo(mainLayoutScrollX, 0);
|
||||
|
||||
mTabsPanel.setIsSideBar(isSideBar);
|
||||
mRootLayout.updateDragHelperParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1732,7 +1910,7 @@ public class BrowserApp extends GeckoApp
|
|||
@Override
|
||||
public void onGlobalLayout() {
|
||||
mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
mTabsPanel.show(panel);
|
||||
showTabs(panel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1821,10 +1999,13 @@ public class BrowserApp extends GeckoApp
|
|||
if (!areTabsShown()) {
|
||||
mTabsPanel.setVisibility(View.INVISIBLE);
|
||||
mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||
mRootLayout.setClosed();
|
||||
mBrowserToolbar.setContextMenuEnabled(true);
|
||||
} else {
|
||||
// Cancel editing mode to return to page content when the TabsPanel closes. We cancel
|
||||
// it here because there are graphical glitches if it's canceled while it's visible.
|
||||
mBrowserToolbar.cancelEdit();
|
||||
mRootLayout.setOpen();
|
||||
}
|
||||
|
||||
mTabsPanel.finishTabsAnimation();
|
||||
|
|
|
@ -161,8 +161,9 @@ public abstract class GeckoApp
|
|||
// after a version upgrade.
|
||||
private static final int CLEANUP_DEFERRAL_SECONDS = 15;
|
||||
|
||||
protected RelativeLayout mRootLayout;
|
||||
protected OuterLayout mRootLayout;
|
||||
protected RelativeLayout mMainLayout;
|
||||
|
||||
protected RelativeLayout mGeckoLayout;
|
||||
private View mCameraView;
|
||||
private OrientationEventListener mCameraOrientationEventListener;
|
||||
|
@ -1267,7 +1268,7 @@ public abstract class GeckoApp
|
|||
setContentView(getLayout());
|
||||
|
||||
// Set up Gecko layout.
|
||||
mRootLayout = (RelativeLayout) findViewById(R.id.root_layout);
|
||||
mRootLayout = (OuterLayout) findViewById(R.id.root_layout);
|
||||
mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
|
||||
mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
|
||||
|
||||
|
@ -2393,11 +2394,24 @@ public abstract class GeckoApp
|
|||
public static class MainLayout extends RelativeLayout {
|
||||
private TouchEventInterceptor mTouchEventInterceptor;
|
||||
private MotionEventInterceptor mMotionEventInterceptor;
|
||||
private LayoutInterceptor mLayoutInterceptor;
|
||||
|
||||
public MainLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
if (mLayoutInterceptor != null) {
|
||||
mLayoutInterceptor.onLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void setLayoutInterceptor(LayoutInterceptor interceptor) {
|
||||
mLayoutInterceptor = interceptor;
|
||||
}
|
||||
|
||||
public void setTouchEventInterceptor(TouchEventInterceptor interceptor) {
|
||||
mTouchEventInterceptor = interceptor;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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;
|
||||
|
||||
public interface LayoutInterceptor {
|
||||
public void onLayout();
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче