This commit is contained in:
Wes Kocher 2015-05-05 14:45:52 -07:00
Родитель 9b63c77800 d43c4e9a53
Коммит f4b5bed2ac
76 изменённых файлов: 912 добавлений и 336 удалений

Просмотреть файл

@ -266,6 +266,10 @@
label="&savePageCmd.label;"
accesskey="&savePageCmd.accesskey2;"
oncommand="gContextMenu.savePageAs();"/>
<menuitem id="context-pocket"
label="&saveToPocketCmd.label;"
accesskey="&saveToPocketCmd.accesskey;"
oncommand="gContextMenu.saveToPocket();"/>
<menu id="context-markpageMenu" label="&social.markpageMenu.label;"
accesskey="&social.markpageMenu.accesskey;">
<menupopup/>

Просмотреть файл

@ -3,6 +3,8 @@
%brandDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
%browserDTD;
<!ENTITY % browserPocketDTD SYSTEM "chrome://browser/content/browser-pocket.dtd" >
%browserPocketDTD;
<!ENTITY % baseMenuDTD SYSTEM "chrome://browser/locale/baseMenuOverlay.dtd" >
%baseMenuDTD;
<!ENTITY % charsetDTD SYSTEM "chrome://global/locale/charsetMenu.dtd" >

Просмотреть файл

@ -387,6 +387,7 @@
oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
onpopupshowing="BookmarkingUI.onMainMenuPopupShowing(event);
BookmarkingUI.updatePocketItemVisibility('menu_');
if (!this.parentNode._placesView)
new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
tooltip="bhTooltip" popupsinherittooltip="true">
@ -460,6 +461,9 @@
</menupopup>
</menu>
#endif
<menuseparator id="menu_pocketSeparator"/>
<menuitem id="menu_pocket" label="&pocketMenuitem.label;"
oncommand="openUILink(Pocket.listURL, event);"/>
<menuseparator id="bookmarksMenuItemsSeparator"/>
<!-- Bookmarks menu items -->
<menuseparator builder="end"

Просмотреть файл

@ -1565,6 +1565,12 @@ let BookmarkingUI = {
PlacesCommandHook.updateBookmarkAllTabsCommand();
},
updatePocketItemVisibility: function BUI_updatePocketItemVisibility(prefix) {
let hidden = !CustomizableUI.getPlacementOfWidget("pocket-button");
document.getElementById(prefix + "pocket").hidden = hidden;
document.getElementById(prefix + "pocketSeparator").hidden = hidden;
},
_showBookmarkedNotification: function BUI_showBookmarkedNotification() {
function getCenteringTransformForRects(rectToPosition, referenceRect) {
let topDiff = referenceRect.top - rectToPosition.top;
@ -1682,6 +1688,7 @@ let BookmarkingUI = {
onPanelMenuViewShowing: function BUI_onViewShowing(aEvent) {
this._updateBookmarkPageMenuItem();
this.updatePocketItemVisibility("panelMenu_");
// Update checked status of the toolbar toggle.
let viewToolbar = document.getElementById("panelMenu_viewBookmarksToolbar");
let personalToolbar = document.getElementById("PersonalToolbar");

Просмотреть файл

@ -0,0 +1,10 @@
<!-- 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/. -->
<!-- This is a temporary file and not meant for localization; later versions
- of Firefox include these strings in browser.dtd -->
<!ENTITY saveToPocketCmd.label "Save Page to Pocket">
<!ENTITY saveToPocketCmd.accesskey "k">
<!ENTITY pocketMenuitem.label "View Pocket List">

Просмотреть файл

@ -883,6 +883,7 @@
oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
onpopupshowing="BookmarkingUI.onPopupShowing(event);
BookmarkingUI.updatePocketItemVisibility('BMB_');
BookmarkingUI.attachPlacesView(event, this);"
tooltip="bhTooltip" popupsinherittooltip="true">
<menuitem id="BMB_viewBookmarksSidebar"
@ -970,6 +971,11 @@
label="&readingList.showSidebar.label;"/>
</menupopup>
</menu>
<menuseparator id="BMB_pocketSeparator"/>
<menuitem id="BMB_pocket"
class="menuitem-iconic bookmark-item subviewbutton"
label="&pocketMenuitem.label;"
oncommand="openUILink(Pocket.listURL, event);"/>
<menuseparator/>
<!-- Bookmarks menu items will go here -->
<menuitem id="BMB_bookmarksShowAll"

Просмотреть файл

@ -177,6 +177,17 @@ nsContextMenu.prototype = {
SimpleServiceDiscovery.services.length > 0 &&
CastingApps.getServicesForVideo(this.target).length > 0;
this.setItemAttr("context-castvideo", "disabled", !shouldShowCast);
let canPocket = false;
if (shouldShow && window.gBrowser &&
this.browser.getTabBrowser() == window.gBrowser) {
let uri = this.browser.currentURI;
canPocket =
CustomizableUI.getPlacementOfWidget("pocket-button") &&
(uri.schemeIs("http") || uri.schemeIs("https") ||
(uri.schemeIs("about") && ReaderMode.getOriginalUrl(uri.spec)));
}
this.showItem("context-pocket", canPocket && window.Pocket && Pocket.isLoggedIn);
},
initViewItems: function CM_initViewItems() {
@ -1621,6 +1632,22 @@ nsContextMenu.prototype = {
saveDocument(this.browser.contentDocumentAsCPOW);
},
saveToPocket: function CM_saveToPocket() {
let pocketWidget = document.getElementById("pocket-button");
let placement = CustomizableUI.getPlacementOfWidget("pocket-button");
if (!placement)
return;
if (placement.area == CustomizableUI.AREA_PANEL) {
PanelUI.show().then(function() {
pocketWidget = document.getElementById("pocket-button");
pocketWidget.doCommand();
});
} else {
pocketWidget.doCommand();
}
},
printFrame: function CM_printFrame() {
PrintUtils.print(this.target.ownerDocument.defaultView, this.browser);
},

Просмотреть файл

@ -1504,12 +1504,8 @@
// Change the "remote" attribute.
let parent = aBrowser.parentNode;
let permanentKey = aBrowser.permanentKey;
parent.removeChild(aBrowser);
aBrowser.setAttribute("remote", aShouldBeRemote ? "true" : "false");
// Tearing down the browser gives a new permanentKey but we want to
// keep the old one. Re-set it explicitly after unbinding from DOM.
aBrowser._permanentKey = permanentKey;
parent.appendChild(aBrowser);
// Restore the progress listener.
@ -1628,6 +1624,7 @@
let isPreloadBrowser = aParams && aParams.isPreloadBrowser;
let b = document.createElementNS(NS_XUL, "browser");
b.permanentKey = {};
b.setAttribute("type", "content-targetable");
b.setAttribute("message", "true");
b.setAttribute("messagemanagergroup", "browsers");
@ -2491,6 +2488,11 @@
// Swap the docshells
ourBrowser.swapDocShells(aOtherBrowser);
// Swap permanentKey properties.
let ourPermanentKey = ourBrowser.permanentKey;
ourBrowser.permanentKey = aOtherBrowser.permanentKey;
aOtherBrowser.permanentKey = ourPermanentKey;
// Restore the progress listener
this.mTabListeners[index] = tabListener =
this.mTabProgressListener(aOurTab, ourBrowser, false);
@ -3876,6 +3878,7 @@
<![CDATA[
let browserStack = document.getAnonymousElementByAttribute(this, "anonid", "browserStack");
this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
this.mCurrentBrowser.permanentKey = {};
this.mCurrentTab = this.tabContainer.firstChild;
const nsIEventListenerService =

Просмотреть файл

@ -19,9 +19,9 @@ function test() {
browser.addEventListener("load", function() {
browser.removeEventListener("load", arguments.callee, true);
gBrowser.removeTab(tab);
ok(isUndoCloseEnabled(), "Undo Close Tab should be enabled.");
finish();
BrowserTestUtils.removeTab(tab).then(() => {
ok(isUndoCloseEnabled(), "Undo Close Tab should be enabled.");
finish();
});
}, true);
}

Просмотреть файл

@ -34,14 +34,15 @@ function preparePendingTab(aCallback) {
let tab = gBrowser.addTab(URL);
whenLoaded(tab.linkedBrowser, function () {
gBrowser.removeTab(tab);
let [{state}] = JSON.parse(SessionStore.getClosedTabData(window));
BrowserTestUtils.removeTab(tab).then(() => {
let [{state}] = JSON.parse(SessionStore.getClosedTabData(window));
tab = gBrowser.addTab("about:blank");
whenLoaded(tab.linkedBrowser, function () {
SessionStore.setTabState(tab, JSON.stringify(state));
ok(tab.hasAttribute("pending"), "tab should be pending");
aCallback(tab);
tab = gBrowser.addTab("about:blank");
whenLoaded(tab.linkedBrowser, function () {
SessionStore.setTabState(tab, JSON.stringify(state));
ok(tab.hasAttribute("pending"), "tab should be pending");
aCallback(tab);
});
});
});
}

Просмотреть файл

@ -10,6 +10,7 @@ add_task(function*() {
let tab = gBrowser.addTab();
tab.linkedBrowser.loadURI(uri);
yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
TabState.flush(tab.linkedBrowser);
let key = tab.linkedBrowser.permanentKey;
let win = gBrowser.replaceTabWithWindow(tab);

Просмотреть файл

@ -12,6 +12,8 @@
<!DOCTYPE page [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % browserPocketDTD SYSTEM "chrome://browser/content/browser-pocket.dtd">
%browserPocketDTD;
<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
%textcontextDTD;
]>

Просмотреть файл

@ -76,6 +76,7 @@ browser.jar:
* content/browser/browser.js (content/browser.js)
* content/browser/browser.xul (content/browser.xul)
content/browser/browser-pocket.properties (content/browser-pocket.properties)
content/browser/browser-pocket.dtd (content/browser-pocket.dtd)
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
* content/browser/chatWindow.xul (content/chatWindow.xul)
content/browser/tab-content.js (content/tab-content.js)

Просмотреть файл

@ -137,6 +137,10 @@
<observes element="readingListSidebar" attribute="checked"/>
<observes element="readingListSidebar" attribute="hidden"/>
</toolbarbutton>
<toolbarseparator id="panelMenu_pocketSeparator"/>
<toolbarbutton id="panelMenu_pocket" label="&pocketMenuitem.label;"
class="subviewbutton cui-withicon"
oncommand="openUILink(Pocket.listURL, event);"/>
<toolbarseparator class="small-separator"/>
<toolbaritem id="panelMenu_bookmarksMenu"
orient="vertical"

Просмотреть файл

@ -1,12 +1,14 @@
#! /usr/bin/env python
import os
import sys
import re
from distutils import spawn
import subprocess
from threading import Thread
import argparse
def find_react_version(lib_dir):
"Finds the React library version number currently used."
for filename in os.listdir(lib_dir):
@ -17,28 +19,82 @@ def find_react_version(lib_dir):
print 'Please checked the %s directory.' % lib_dir
exit(1)
def append_arguments(array1, array2):
"Appends array2 onto the end of array1"
result = array1[:]
result.extend(array2)
return result
def check_jsx(jsx_path):
"Checks to see if jsx is installed or not"
if jsx_path is None:
print 'You do not have the react-tools installed'
print 'Please do $ npm install -g react-tools and make sure it is available in PATH'
exit(1)
def find_react_command():
"Searches for a jsx location and forms a runnable command"
if sys.platform != 'win32':
jsx_path = spawn.find_executable('jsx')
check_jsx(jsx_path)
return [jsx_path]
# Else windows.
def find_excutable_no_extension(fileName):
"""
spawn.find_executable assumes a '.exe' extension on windows
something which jsx doesn't have...
"""
paths = os.environ['PATH'].split(os.pathsep)
for path in paths:
file = os.path.join(path, fileName)
if os.path.isfile(file):
return path
return None
# jsx isn't a true windows executable, so the standard spawn
# processes get upset. Hence, we have to use node to run the
# jsx file direct.
node = spawn.find_executable('node')
if node is None:
print 'You do not have node installed, or it is not in your PATH'
exit(1)
# We need the jsx path to make node happy
jsx_path = find_excutable_no_extension('jsx')
check_jsx(jsx_path)
# This is what node really wants to run.
jsx_path = os.path.join(jsx_path,
"node_modules", "react-tools", "bin", "jsx")
return [node, jsx_path]
SHARED_LIBS_DIR=os.path.join(os.path.dirname(__file__), "content", "shared", "libs")
REACT_VERSION=find_react_version(SHARED_LIBS_DIR)
src_files = [] # files to be compiled
# search for react-tools install
jsx_path = spawn.find_executable('jsx')
if jsx_path is None:
print 'You do not have the react-tools installed'
print 'Please do $ npm install -g react-tools'
exit(1)
run_command = find_react_command()
p = subprocess.Popen([jsx_path, '-V'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, b''):
info = line.rstrip()
if sys.platform == 'win32':
print 'Please ensure you are running react-tools version %s' % REACT_VERSION
print 'You may be already, but we are not currently able to detect it'
else:
p = subprocess.Popen(append_arguments(run_command, ['-V']),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, b''):
info = line.rstrip()
if not info == REACT_VERSION:
print 'You have the wrong version of react-tools installed'
print 'Please use version %s' % REACT_VERSION
exit(1)
if not info == REACT_VERSION:
print 'You have the wrong version of react-tools installed'
print 'Please use version %s' % REACT_VERSION
exit(1)
# parse the CLI arguments
description = 'Loop build tool for JSX files. ' + \
@ -63,7 +119,7 @@ for dirname, dirnames, filenames in os.walk('.'):
def jsx_run_watcher(path):
subprocess.call(['jsx', '-w', '-x', 'jsx', path, path])
subprocess.call(append_arguments(run_command, ['-w', '-x', 'jsx', path, path]))
def start_jsx_watcher_threads(dirs):
@ -111,4 +167,4 @@ if args.watch:
start_jsx_watcher_threads(unique_jsx_dirs)
else:
for d in unique_jsx_dirs:
out = subprocess.call(['jsx', '-x', 'jsx', d, d])
out = subprocess.call(append_arguments(run_command, ['-x', 'jsx', d, d]))

Просмотреть файл

@ -14,7 +14,7 @@
"devDependencies": {
"eslint": "0.20.x",
"eslint-plugin-react": "2.2.x",
"express": "3.x"
"express": "4.x"
},
"scripts": {
"test": "make test",

Просмотреть файл

@ -13,10 +13,16 @@ var express = require('express');
var app = express();
var port = process.env.PORT || 3000;
var loopServerPort = process.env.LOOP_SERVER_PORT || 5000;
var feedbackApiUrl = process.env.LOOP_FEEDBACK_API_URL ||
"https://input.allizom.org/api/v1/feedback";
var feedbackProductName = process.env.LOOP_FEEDBACK_PRODUCT_NAME || "Loop";
var loopServerUrl = process.env.LOOP_SERVER_URL || "http://localhost:5000";
// Remove trailing slashes as double slashes in the url can confuse the server
// responses.
if (loopServerUrl[loopServerUrl.length - 1] === "/") {
loopServerUrl = loopServerUrl.slice(0, -1);
}
function getConfigFile(req, res) {
"use strict";
@ -25,7 +31,7 @@ function getConfigFile(req, res) {
res.send([
"var loop = loop || {};",
"loop.config = loop.config || {};",
"loop.config.serverUrl = 'http://localhost:" + loopServerPort + "/v0';",
"loop.config.serverUrl = '" + loopServerUrl + "/v0';",
"loop.config.feedbackApiUrl = '" + feedbackApiUrl + "';",
"loop.config.feedbackProductName = '" + feedbackProductName + "';",
// XXX Update with the real marketplace url once the FxOS Loop app is
@ -54,7 +60,6 @@ app.get('/content/c/config.js', getConfigFile);
// /ui - for the ui showcase
// /content - for the standalone files.
app.use('/test', express.static(__dirname + '/../test'));
app.use('/ui', express.static(__dirname + '/../ui'));
// This exists exclusively for the unit tests. They are served the
@ -71,6 +76,10 @@ app.use('/content', express.static(__dirname + '/../content'));
app.use('/content/c', express.static(__dirname + '/content'));
app.use('/content/c', express.static(__dirname + '/../content'));
// Two lines for the same reason as /content above.
app.use('/test', express.static(__dirname + '/test'));
app.use('/test', express.static(__dirname + '/../test'));
// As we don't have hashes on the urls, the best way to serve the index files
// appears to be to be to closely filter the url and match appropriately.
function serveIndex(req, res) {

Просмотреть файл

@ -1,8 +1,9 @@
# Loop server configuration
CONTENT_SERVER_PORT = 3001
LOOP_SERVER_PORT = 5001
LOOP_SERVER_URL = "http://localhost:" + str(LOOP_SERVER_PORT)
FIREFOX_PREFERENCES = {
"loop.server": "http://localhost:" + str(LOOP_SERVER_PORT),
"loop.server": LOOP_SERVER_URL + "/v0",
"browser.dom.window.dump.enabled": True,
# Some more changes might be necesarry to have this working in offline mode
"media.peerconnection.default_iceservers": "[]",

Просмотреть файл

@ -21,7 +21,7 @@ CONTENT_SERVER_ENV = os.environ.copy()
# Set PORT so that it does not interfere with any other
# development server that might be running
CONTENT_SERVER_ENV.update({"PORT": str(CONTENT_SERVER_PORT),
"LOOP_SERVER_PORT": str(LOOP_SERVER_PORT)})
"LOOP_SERVER_URL": LOOP_SERVER_URL})
ROOMS_WEB_APP_URL = "http://localhost:" + str(CONTENT_SERVER_PORT) + \
"/content/{token}"
@ -32,7 +32,7 @@ LOOP_SERVER_ENV = os.environ.copy()
# development server that might be running
LOOP_SERVER_ENV.update({"NODE_ENV": "dev",
"PORT": str(LOOP_SERVER_PORT),
"SERVER_ADDRESS": "localhost:" + str(LOOP_SERVER_PORT),
"SERVER_ADDRESS": LOOP_SERVER_URL,
"ROOMS_WEB_APP_URL": ROOMS_WEB_APP_URL})

Просмотреть файл

@ -29,6 +29,8 @@ let Pocket = {
get hostname() Services.prefs.getCharPref("browser.pocket.hostname"),
get listURL() { return "https://" + Pocket.hostname; },
get _accessToken() {
let sessionId, accessToken;
let cookies = Services.cookies.getCookiesFromHost(this.hostname);

Просмотреть файл

@ -16,6 +16,8 @@ browser.jar:
content/browser/pocket/panels/img/pocketlogo@2x.png (panels/img/pocketlogo@2x.png)
content/browser/pocket/panels/img/pocketlogosolo@1x.png (panels/img/pocketlogosolo@1x.png)
content/browser/pocket/panels/img/pocketlogosolo@2x.png (panels/img/pocketlogosolo@2x.png)
content/browser/pocket/panels/img/pocketmenuitem16.png (panels/img/pocketmenuitem16.png)
content/browser/pocket/panels/img/pocketmenuitem16@2x.png (panels/img/pocketmenuitem16@2x.png)
content/browser/pocket/panels/img/pocketmultidevices@1x.png (panels/img/pocketmultidevices@1x.png)
content/browser/pocket/panels/img/pocketmultidevices@2x.png (panels/img/pocketmultidevices@2x.png)
content/browser/pocket/panels/img/signup_firefoxlogo@1x.png (panels/img/signup_firefoxlogo@1x.png)

Двоичные данные
browser/components/pocket/panels/img/pocketmenuitem16.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 278 B

Двоичные данные
browser/components/pocket/panels/img/pocketmenuitem16@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 422 B

Просмотреть файл

@ -10,19 +10,21 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
const ENGINE_FLAVOR = "text/x-moz-search-engine";
document.addEventListener("Initialized", () => {
if (Services.prefs.getBoolPref("browser.search.showOneOffButtons"))
return;
document.getElementById("category-search").hidden = true;
if (document.location.hash == "#search")
document.location.hash = "";
});
var gEngineView = null;
var gSearchPane = {
init: function ()
{
if (!Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
document.getElementById("category-search").hidden = true;
if (document.location.hash == "#search")
document.location.hash = "";
return;
}
gEngineView = new EngineView(new EngineStore());
document.getElementById("engineList").view = gEngineView;
this.buildDefaultEngineDropDown();

Просмотреть файл

@ -92,6 +92,9 @@ const NOTAB_MESSAGES = new Set([
const CLOSED_MESSAGES = new Set([
// For a description see above.
"SessionStore:crashedTabRevived",
// For a description see above.
"SessionStore:update",
]);
// These are tab events that we listen to.
@ -338,6 +341,15 @@ let SessionStoreInternal = {
// they get restored).
_crashedBrowsers: new WeakSet(),
// A map (xul:browser -> nsIFrameLoader) that maps a browser to the last
// associated frameLoader we heard about.
_lastKnownFrameLoader: new WeakMap(),
// A map (xul:browser -> object) that maps a browser associated with a
// recently closed tab to all its necessary state information we need to
// properly handle final update message.
_closedTabs: new WeakMap(),
// whether a setBrowserState call is in progress
_browserSetState: false,
@ -612,14 +624,64 @@ let SessionStoreInternal = {
TabState.setSyncHandler(browser, aMessage.objects.handler);
break;
case "SessionStore:update":
// Ignore messages from <browser> elements that have crashed
// and not yet been revived.
if (this._crashedBrowsers.has(browser.permanentKey)) {
// Ignore messages from <browser> elements that have crashed
// and not yet been revived.
return;
}
// |browser.frameLoader| might be empty if the browser was already
// destroyed and its tab removed. In that case we still have the last
// frameLoader we know about to compare.
let frameLoader = browser.frameLoader ||
this._lastKnownFrameLoader.get(browser.permanentKey);
// If the message isn't targeting the latest frameLoader discard it.
if (frameLoader != aMessage.targetFrameLoader) {
return;
}
// Record telemetry measurements done in the child and update the tab's
// cached state. Mark the window as dirty and trigger a delayed write.
this.recordTelemetry(aMessage.data.telemetry);
TabState.update(browser, aMessage.data);
this.saveStateDelayed(win);
// Handle any updates sent by the child after the tab was closed. This
// might be the final update as sent by the "unload" handler but also
// any async update message that was sent before the child unloaded.
if (this._closedTabs.has(browser.permanentKey)) {
let {closedTabs, tabData} = this._closedTabs.get(browser.permanentKey);
// Update the closed tab's state. This will be reflected in its
// window's list of closed tabs as that refers to the same object.
TabState.copyFromCache({linkedBrowser: browser}, tabData.state);
// Is this the tab's final message?
if (aMessage.data.isFinal) {
// We expect no further updates.
this._closedTabs.delete(browser.permanentKey);
// The tab state no longer needs this reference.
delete tabData.permanentKey;
// Determine whether the tab state is worth saving.
let shouldSave = this._shouldSaveTabState(tabData.state);
let index = closedTabs.indexOf(tabData);
if (shouldSave && index == -1) {
// If the tab state is worth saving and we didn't push it onto
// the list of closed tabs when it was closed (because we deemed
// the state not worth saving) then add it to the window's list
// of closed tabs now.
this.saveClosedTabData(closedTabs, tabData);
} else if (!shouldSave && index > -1) {
// Remove from the list of closed tabs. The update messages sent
// after the tab was closed changed enough state so that we no
// longer consider its data interesting enough to keep around.
this.removeClosedTabData(closedTabs, index);
}
}
}
break;
case "SessionStore:restoreHistoryComplete":
if (this.isCurrentEpoch(browser, aMessage.data.epoch)) {
@ -722,25 +784,26 @@ let SessionStoreInternal = {
* Implement nsIDOMEventListener for handling various window and tab events
*/
handleEvent: function ssi_handleEvent(aEvent) {
var win = aEvent.currentTarget.ownerDocument.defaultView;
let win = aEvent.currentTarget.ownerDocument.defaultView;
let target = aEvent.originalTarget;
switch (aEvent.type) {
case "TabOpen":
this.onTabAdd(win, aEvent.originalTarget);
this.onTabAdd(win, target);
break;
case "TabClose":
// aEvent.detail determines if the tab was closed by moving to a different window
if (!aEvent.detail)
this.onTabClose(win, aEvent.originalTarget);
this.onTabRemove(win, aEvent.originalTarget);
this.onTabClose(win, target);
this.onTabRemove(win, target);
break;
case "TabSelect":
this.onTabSelect(win);
break;
case "TabShow":
this.onTabShow(win, aEvent.originalTarget);
this.onTabShow(win, target);
break;
case "TabHide":
this.onTabHide(win, aEvent.originalTarget);
this.onTabHide(win, target);
break;
case "TabPinned":
case "TabUnpinned":
@ -748,8 +811,15 @@ let SessionStoreInternal = {
this.saveStateDelayed(win);
break;
case "oop-browser-crashed":
this.onBrowserCrashed(win, aEvent.originalTarget);
this.onBrowserCrashed(win, target);
break;
case "XULFrameLoaderCreated":
if (target.tagName == "browser" && target.frameLoader) {
this._lastKnownFrameLoader.set(target.permanentKey, target.frameLoader);
}
break;
default:
throw new Error(`unhandled event ${aEvent.type}?`);
}
this._clearRestoringWindows();
},
@ -943,6 +1013,9 @@ let SessionStoreInternal = {
TAB_EVENTS.forEach(function(aEvent) {
tabbrowser.tabContainer.addEventListener(aEvent, this, true);
}, this);
// Keep track of a browser's latest frameLoader.
aWindow.gBrowser.addEventListener("XULFrameLoaderCreated", this);
},
/**
@ -1046,6 +1119,8 @@ let SessionStoreInternal = {
tabbrowser.tabContainer.removeEventListener(aEvent, this, true);
}, this);
aWindow.gBrowser.removeEventListener("XULFrameLoaderCreated", this);
let winData = this._windows[aWindow.__SSi];
// Collect window data only when *not* closed during shutdown.
@ -1311,6 +1386,11 @@ let SessionStoreInternal = {
let browser = aTab.linkedBrowser;
browser.addEventListener("SwapDocShells", this);
browser.addEventListener("oop-browser-crashed", this);
if (browser.frameLoader) {
this._lastKnownFrameLoader.set(browser.permanentKey, browser.frameLoader);
}
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
}
@ -1365,9 +1445,6 @@ let SessionStoreInternal = {
return;
}
// Flush all data queued in the content script before the tab is gone.
TabState.flush(aTab.linkedBrowser);
// Get the latest data for this tab (generally, from the cache)
let tabState = TabState.collect(aTab);
@ -1377,23 +1454,96 @@ let SessionStoreInternal = {
return;
}
// store closed-tab data for undo
if (this._shouldSaveTabState(tabState)) {
let tabTitle = aTab.label;
let tabbrowser = aWindow.gBrowser;
tabTitle = this._replaceLoadingTitle(tabTitle, tabbrowser, aTab);
// Store closed-tab data for undo.
let tabbrowser = aWindow.gBrowser;
let tabTitle = this._replaceLoadingTitle(aTab.label, tabbrowser, aTab);
let {permanentKey} = aTab.linkedBrowser;
this._windows[aWindow.__SSi]._closedTabs.unshift({
state: tabState,
title: tabTitle,
image: tabbrowser.getIcon(aTab),
pos: aTab._tPos,
closedAt: Date.now()
});
var length = this._windows[aWindow.__SSi]._closedTabs.length;
if (length > this._max_tabs_undo)
this._windows[aWindow.__SSi]._closedTabs.splice(this._max_tabs_undo, length - this._max_tabs_undo);
let tabData = {
permanentKey,
state: tabState,
title: tabTitle,
image: tabbrowser.getIcon(aTab),
pos: aTab._tPos,
closedAt: Date.now()
};
let closedTabs = this._windows[aWindow.__SSi]._closedTabs;
// Determine whether the tab contains any information worth saving. Note
// that there might be pending state changes queued in the child that
// didn't reach the parent yet. If a tab is emptied before closing then we
// might still remove it from the list of closed tabs later.
if (this._shouldSaveTabState(tabState)) {
// Save the tab state, for now. We might push a valid tab out
// of the list but those cases should be extremely rare and
// do probably never occur when using the browser normally.
// (Tests or add-ons might do weird things though.)
this.saveClosedTabData(closedTabs, tabData);
}
// Remember the closed tab to properly handle any last updates included in
// the final "update" message sent by the frame script's unload handler.
this._closedTabs.set(permanentKey, {closedTabs, tabData});
},
/**
* Insert a given |tabData| object into the list of |closedTabs|. We will
* determine the right insertion point based on the .closedAt properties of
* all tabs already in the list. The list will be truncated to contain a
* maximum of |this._max_tabs_undo| entries.
*
* @param closedTabs (array)
* The list of closed tabs for a window.
* @param tabData (object)
* The tabData to be inserted.
*/
saveClosedTabData(closedTabs, tabData) {
// Find the index of the first tab in the list
// of closed tabs that was closed before our tab.
let index = closedTabs.findIndex(tab => {
return tab.closedAt < tabData.closedAt;
});
// If we found no tab closed before our
// tab then just append it to the list.
if (index == -1) {
index = closedTabs.length;
}
// Insert tabData at the right position.
closedTabs.splice(index, 0, tabData);
// Truncate the list of closed tabs, if needed.
if (closedTabs.length > this._max_tabs_undo) {
closedTabs.splice(this._max_tabs_undo, closedTabs.length);
}
},
/**
* Remove the closed tab data at |index| from the list of |closedTabs|. If
* the tab's final message is still pending we will simply discard it when
* it arrives so that the tab doesn't reappear in the list.
*
* @param closedTabs (array)
* The list of closed tabs for a window.
* @param index (uint)
* The index of the tab to remove.
*/
removeClosedTabData(closedTabs, index) {
// Remove the given index from the list.
let [closedTab] = closedTabs.splice(index, 1);
// If the closed tab's state still has a .permanentKey property then we
// haven't seen its final update message yet. Remove it from the map of
// closed tabs so that we will simply discard its last messages and will
// not add it back to the list of closed tabs again.
if (closedTab.permanentKey) {
this._closedTabs.delete(closedTab.permanentKey);
delete closedTab.permanentKey;
}
return closedTab;
},
/**
@ -1696,18 +1846,17 @@ let SessionStoreInternal = {
}
// fetch the data of closed tab, while removing it from the array
let [closedTab] = closedTabs.splice(aIndex, 1);
let closedTabState = closedTab.state;
let {state, pos} = this.removeClosedTabData(closedTabs, aIndex);
// create a new tab
let tabbrowser = aWindow.gBrowser;
let tab = tabbrowser.selectedTab = tabbrowser.addTab();
// restore tab content
this.restoreTab(tab, closedTabState);
this.restoreTab(tab, state);
// restore the tab's position
tabbrowser.moveTabTo(tab, closedTab.pos);
tabbrowser.moveTabTo(tab, pos);
// focus the tab's content area (bug 342432)
tab.linkedBrowser.focus();
@ -1729,7 +1878,7 @@ let SessionStoreInternal = {
}
// remove closed tab from the array
closedTabs.splice(aIndex, 1);
this.removeClosedTabData(closedTabs, aIndex);
},
getClosedWindowCount: function ssi_getClosedWindowCount() {

Просмотреть файл

@ -53,6 +53,10 @@ this.TabState = Object.freeze({
clone: function (tab) {
return TabStateInternal.clone(tab);
},
copyFromCache: function (tab, tabData, options) {
TabStateInternal.copyFromCache(tab, tabData, options);
}
});
@ -218,7 +222,7 @@ let TabStateInternal = {
// Copy data from the tab state cache only if the tab has fully finished
// restoring. We don't want to overwrite data contained in __SS_data.
this._copyFromCache(tab, tabData, options);
this.copyFromCache(tab, tabData, options);
return tabData;
},
@ -233,7 +237,7 @@ let TabStateInternal = {
* @param options (object)
* {includePrivateData: true} to always include private data
*/
_copyFromCache: function (tab, tabData, options = {}) {
copyFromCache: function (tab, tabData, options = {}) {
let data = TabStateCache.get(tab.linkedBrowser);
if (!data) {
return;

Просмотреть файл

@ -116,6 +116,12 @@ let MessageListener = {
},
receiveMessage: function ({name, data}) {
// The docShell might be gone. Don't process messages,
// that will just lead to errors anyway.
if (!docShell) {
return;
}
switch (name) {
case "SessionStore:restoreHistory":
this.restoreHistory(data);
@ -222,7 +228,11 @@ let SyncHandler = {
* can occur by sending data shortly before flushing synchronously.
*/
flushAsync: function () {
MessageQueue.flushAsync();
if (!Services.prefs.getBoolPref("browser.sessionstore.debug")) {
throw new Error("flushAsync() must be used for testing, only.");
}
MessageQueue.send();
}
};
@ -680,9 +690,8 @@ let MessageQueue = {
// Send all data to the parent process.
sendMessage("SessionStore:update", {
id: this._id,
data: data,
telemetry: telemetry
id: this._id, data, telemetry,
isFinal: options.isFinal || false
});
// Increase our unique message ID.
@ -706,20 +715,6 @@ let MessageQueue = {
this._data.clear();
this._lastUpdated.clear();
},
/**
* DO NOT USE - DEBUGGING / TESTING ONLY
*
* This function is used to simulate certain situations where race conditions
* can occur by sending data shortly before flushing synchronously.
*/
flushAsync: function () {
if (!Services.prefs.getBoolPref("browser.sessionstore.debug")) {
throw new Error("flushAsync() must be used for testing, only.");
}
this.send();
}
};
@ -759,6 +754,10 @@ function handleRevivedTab() {
addEventListener("pagehide", handleRevivedTab);
addEventListener("unload", () => {
// Upon frameLoader destruction, send a final update message to
// the parent and flush all data currently held in the child.
MessageQueue.send({isFinal: true});
// If we're browsing from the tab crashed UI to a URI that causes the tab
// to go remote again, we catch this in the unload event handler, because
// swapping out the non-remote browser for a remote one in

Просмотреть файл

@ -65,6 +65,8 @@ support-files =
[browser_aboutPrivateBrowsing.js]
[browser_aboutSessionRestore.js]
[browser_async_remove_tab.js]
run-if = e10s
[browser_attributes.js]
[browser_backup_recovery.js]
[browser_broadcast.js]

Просмотреть файл

@ -1,4 +1,6 @@
function test() {
"use strict";
add_task(function* () {
/** Test for Bug 350525 **/
function test(aLambda) {
@ -9,8 +11,6 @@ function test() {
return false;
}
waitForExplicitFinish();
////////////////////////////
// setWindowValue, et al. //
////////////////////////////
@ -56,7 +56,7 @@ function test() {
ok(test(function() ss.deleteTabValue(tab, key)), "delete non-existent tab value");
// clean up
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
/////////////////////////////////////
// getClosedTabCount, undoCloseTab //
@ -71,29 +71,26 @@ function test() {
// create a new tab
let testURL = "about:";
tab = gBrowser.addTab(testURL);
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);
yield promiseBrowserLoaded(tab.linkedBrowser);
// remove tab
gBrowser.removeTab(tab);
// make sure that the next closed tab will increase getClosedTabCount
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
registerCleanupFunction(() => gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo"));
// getClosedTabCount
var newcount = ss.getClosedTabCount(window);
ok(newcount > count, "after closing a tab, getClosedTabCount has been incremented");
// remove tab
yield promiseRemoveTab(tab);
// undoCloseTab
tab = test(function() ss.undoCloseTab(window, 0));
ok(tab, "undoCloseTab doesn't throw")
// getClosedTabCount
let newcount = ss.getClosedTabCount(window);
ok(newcount > count, "after closing a tab, getClosedTabCount has been incremented");
promiseTabRestored(tab).then(() => {
is(tab.linkedBrowser.currentURI.spec, testURL, "correct tab was reopened");
// undoCloseTab
tab = test(function() ss.undoCloseTab(window, 0));
ok(tab, "undoCloseTab doesn't throw")
// clean up
if (gPrefService.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
gBrowser.removeTab(tab);
finish();
});
});
}
yield promiseTabRestored(tab);
is(tab.linkedBrowser.currentURI.spec, testURL, "correct tab was reopened");
// clean up
gBrowser.removeTab(tab);
});

Просмотреть файл

@ -2,36 +2,40 @@
* 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/. */
function test() {
/** Test for Bug 367052 **/
waitForExplicitFinish();
"use strict";
add_task(function* () {
// make sure that the next closed tab will increase getClosedTabCount
let max_tabs_undo = gPrefService.getIntPref("browser.sessionstore.max_tabs_undo");
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
let closedTabCount = ss.getClosedTabCount(window);
registerCleanupFunction(() => gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo"));
// Empty the list of closed tabs.
while (ss.getClosedTabCount(window)) {
ss.forgetClosedTab(window, 0);
}
// restore a blank tab
let tab = gBrowser.addTab("about:");
promiseBrowserLoaded(tab.linkedBrowser).then(() => {
let history = tab.linkedBrowser.webNavigation.sessionHistory;
ok(history.count >= 1, "the new tab does have at least one history entry");
yield promiseBrowserLoaded(tab.linkedBrowser);
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;
ok(history.count == 0, "the tab was restored without any history whatsoever");
let count = yield promiseSHistoryCount(tab.linkedBrowser);
ok(count >= 1, "the new tab does have at least one history entry");
gBrowser.removeTab(tab);
ok(ss.getClosedTabCount(window) == closedTabCount,
"The closed blank tab wasn't added to Recently Closed Tabs");
yield promiseTabState(tab, {entries: []});
// clean up
if (gPrefService.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
finish();
});
// We may have a different sessionHistory object if the tab
// switched from non-remote to remote.
count = yield promiseSHistoryCount(tab.linkedBrowser);
is(count, 0, "the tab was restored without any history whatsoever");
yield promiseRemoveTab(tab);
is(ss.getClosedTabCount(window), 0,
"The closed blank tab wasn't added to Recently Closed Tabs");
});
function promiseSHistoryCount(browser) {
return ContentTask.spawn(browser, null, function* () {
return docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory.count;
});
}

Просмотреть файл

@ -2,19 +2,15 @@
* 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/. */
/** Private Browsing Test for Bug 394759 **/
let closedWindowCount = 0;
// Prevent VM timers issues, cache now and increment it manually.
let now = Date.now();
"use strict";
const TESTS = [
{ url: "about:config",
key: "bug 394759 Non-PB",
value: "uniq" + (++now) },
value: "uniq" + r() },
{ url: "about:mozilla",
key: "bug 394759 PB",
value: "uniq" + (++now) },
value: "uniq" + r() },
];
function promiseTestOpenCloseWindow(aIsPrivate, aTest) {
@ -33,53 +29,11 @@ function promiseTestOpenCloseWindow(aIsPrivate, aTest) {
function promiseTestOnWindow(aIsPrivate, aValue) {
return Task.spawn(function*() {
let win = yield promiseNewWindowLoaded({ "private": aIsPrivate });
yield promiseCheckClosedWindows(aIsPrivate, aValue);
registerCleanupFunction(() => promiseWindowClosed(win));
});
}
function promiseCheckClosedWindows(aIsPrivate, aValue) {
return Task.spawn(function*() {
let data = JSON.parse(ss.getClosedWindowData())[0];
is(ss.getClosedWindowCount(), 1, "Check that the closed window count hasn't changed");
ok(JSON.stringify(data).indexOf(aValue) > -1,
"Check the closed window data was stored correctly");
});
}
function promiseBlankState() {
return Task.spawn(function*() {
// Set interval to a large time so state won't be written while we setup
// environment.
Services.prefs.setIntPref("browser.sessionstore.interval", 100000);
registerCleanupFunction(() => Services.prefs.clearUserPref("browser.sessionstore.interval"));
// Set up the browser in a blank state. Popup windows in previous tests
// result in different states on different platforms.
let blankState = JSON.stringify({
windows: [{
tabs: [{ entries: [{ url: "about:blank" }] }],
_closedTabs: []
}],
_closedWindows: []
});
ss.setBrowserState(blankState);
// Wait for the sessionstore.js file to be written before going on.
// Note: we don't wait for the complete event, since if asyncCopy fails we
// would timeout.
yield forceSaveState();
closedWindowCount = ss.getClosedWindowCount();
is(closedWindowCount, 0, "Correctly set window count");
// Remove the sessionstore.js file before setting the interval to 0
yield SessionFile.wipe();
// Make sure that sessionstore.js can be forced to be created by setting
// the interval pref to 0.
yield forceSaveState();
registerCleanupFunction(() => promiseWindowClosed(win));
});
}

Просмотреть файл

@ -28,7 +28,7 @@ add_task(function* test_dont_save_passwords() {
yield setInputValue(browser, {id: "passwd", value: PASS});
// Close and restore the tab.
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
tab = ss.undoCloseTab(window, 0);
browser = tab.linkedBrowser;
yield promiseTabRestored(tab);
@ -46,7 +46,6 @@ add_task(function* test_dont_save_passwords() {
ok(!state.includes(PASS), "password has not been written to file " + key)
);
// Cleanup.
gBrowser.removeTab(tab);
});

Просмотреть файл

@ -19,7 +19,7 @@ add_task(function test_restore_nonstandard_input_values() {
yield setFormElementValues(browser, {value: expectedValue});
// Remove tab and check collected form data.
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
let undoItems = JSON.parse(ss.getClosedTabData(window));
let savedFormData = undoItems[0].state.formdata;

Просмотреть файл

@ -1,21 +1,20 @@
function test() {
waitForExplicitFinish();
"use strict";
var tab1 = gBrowser.addTab("data:text/plain;charset=utf-8,foo");
add_task(function* () {
let tab1 = gBrowser.addTab("data:text/plain;charset=utf-8,foo");
gBrowser.pinTab(tab1);
promiseBrowserLoaded(tab1.linkedBrowser).then(() => {
var tab2 = gBrowser.addTab();
gBrowser.pinTab(tab2);
yield promiseBrowserLoaded(tab1.linkedBrowser);
let tab2 = gBrowser.addTab();
gBrowser.pinTab(tab2);
is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 is at the first position");
gBrowser.removeTab(tab1);
tab1 = undoCloseTab();
ok(tab1.pinned, "pinned tab 1 has been restored as a pinned tab");
is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 has been restored to the first position");
is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 is at the first position");
yield promiseRemoveTab(tab1);
gBrowser.removeTab(tab1);
gBrowser.removeTab(tab2);
finish();
});
}
tab1 = undoCloseTab();
ok(tab1.pinned, "pinned tab 1 has been restored as a pinned tab");
is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 has been restored to the first position");
gBrowser.removeTab(tab1);
gBrowser.removeTab(tab2);
});

Просмотреть файл

@ -1,36 +1,19 @@
/* 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/. */
// Tests that an about:blank tab with no history will not be saved into
// session store and thus, it will not show up in Recently Closed Tabs.
let tab;
function test() {
waitForExplicitFinish();
"use strict";
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", 0);
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
add_task(function* () {
let tab = gBrowser.addTab("about:blank");
yield promiseBrowserLoaded(tab.linkedBrowser);
is(ss.getClosedTabCount(window), 0, "should be no closed tabs");
is(tab.linkedBrowser.currentURI.spec, "about:blank",
"we will be removing an about:blank tab");
gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen, true);
let r = `rand-${Math.random()}`;
ss.setTabValue(tab, "foobar", r);
tab = gBrowser.addTab();
}
function onTabOpen(aEvent) {
gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen, true);
// Let other listeners react to the TabOpen event before removing the tab.
executeSoon(function() {
is(gBrowser.browsers[1].currentURI.spec, "about:blank",
"we will be removing an about:blank tab");
gBrowser.removeTab(tab);
is(ss.getClosedTabCount(window), 0, "should still be no closed tabs");
executeSoon(finish);
});
}
yield promiseRemoveTab(tab);
let closedTabData = ss.getClosedTabData(window);
ok(!closedTabData.contains(r), "tab not stored in _closedTabs");
});

Просмотреть файл

@ -30,15 +30,16 @@ function test() {
ok(tab.hidden, "newly created tab is now hidden");
// close and restore hidden tab
gBrowser.removeTab(tab);
tab = ss.undoCloseTab(window, 0);
promiseRemoveTab(tab).then(() => {
tab = ss.undoCloseTab(window, 0);
// check that everything was restored correctly, clean up and finish
whenTabIsLoaded(tab, function () {
is(tab.linkedBrowser.currentURI.spec, "about:mozilla", "restored tab has correct url");
// check that everything was restored correctly, clean up and finish
whenTabIsLoaded(tab, function () {
is(tab.linkedBrowser.currentURI.spec, "about:mozilla", "restored tab has correct url");
gBrowser.removeTab(tab);
finish();
gBrowser.removeTab(tab);
finish();
});
});
});
}

Просмотреть файл

@ -29,7 +29,7 @@ add_task(function* test() {
"CSP should block the script loaded by the clicked data URI");
// close the tab
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
// open new tab and recover the state
tab = ss.undoCloseTab(window, 0);

Просмотреть файл

@ -1,18 +1,10 @@
/* 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";
// Tests that an about:privatebrowsing tab with no history will not
// be saved into session store and thus, it will not show up in
// Recently Closed Tabs.
add_task(function* () {
while (ss.getClosedTabCount(window)) {
ss.forgetClosedTab(window, 0);
}
is(ss.getClosedTabCount(window), 0, "should be no closed tabs");
let tab = gBrowser.addTab("about:privatebrowsing");
let browser = tab.linkedBrowser;
yield promiseBrowserLoaded(browser);
@ -20,6 +12,10 @@ add_task(function* () {
is(gBrowser.browsers[1].currentURI.spec, "about:privatebrowsing",
"we will be removing an about:privatebrowsing tab");
gBrowser.removeTab(tab);
is(ss.getClosedTabCount(window), 0, "should still be no closed tabs");
let r = `rand-${Math.random()}`;
ss.setTabValue(tab, "foobar", r);
yield promiseRemoveTab(tab);
let closedTabData = ss.getClosedTabData(window);
ok(!closedTabData.contains(r), "tab not stored in _closedTabs");
});

Просмотреть файл

@ -0,0 +1,242 @@
"use strict";
function* createTabWithRandomValue(url) {
let tab = gBrowser.addTab(url);
let browser = tab.linkedBrowser;
yield promiseBrowserLoaded(browser);
// Set a random value.
let r = `rand-${Math.random()}`;
ss.setTabValue(tab, "foobar", r);
// Flush to ensure there are no scheduled messages.
TabState.flush(browser);
return {tab, r};
}
function isValueInClosedData(rval) {
return ss.getClosedTabData(window).includes(rval);
}
function restoreClosedTabWithValue(rval) {
let closedTabData = JSON.parse(ss.getClosedTabData(window));
let index = closedTabData.findIndex(function (data) {
return (data.state.extData && data.state.extData.foobar) == rval;
});
if (index == -1) {
throw new Error("no closed tab found for given rval");
}
return ss.undoCloseTab(window, index);
}
function promiseNewLocationAndHistoryEntryReplaced(browser, snippet) {
return ContentTask.spawn(browser, snippet, function* (snippet) {
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
let shistory = webNavigation.sessionHistory;
// Evaluate the snippet that the changes the location.
eval(snippet);
return new Promise(resolve => {
let listener = {
OnHistoryReplaceEntry() {
shistory.removeSHistoryListener(this);
resolve();
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsISHistoryListener,
Ci.nsISupportsWeakReference
])
};
shistory.addSHistoryListener(listener);
/* Keep the weak shistory listener alive. */
addEventListener("unload", function () {
try {
shistory.removeSHistoryListener(listener);
} catch (e) { /* Will most likely fail. */ }
});
});
});
}
function promiseHistoryEntryReplacedNonRemote(browser) {
let {listeners} = promiseHistoryEntryReplacedNonRemote;
return new Promise(resolve => {
let shistory = browser.webNavigation.sessionHistory;
let listener = {
OnHistoryReplaceEntry() {
shistory.removeSHistoryListener(this);
resolve();
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsISHistoryListener,
Ci.nsISupportsWeakReference
])
};
shistory.addSHistoryListener(listener);
listeners.set(browser, listener);
});
}
promiseHistoryEntryReplacedNonRemote.listeners = new WeakMap();
add_task(function* dont_save_empty_tabs() {
let {tab, r} = yield createTabWithRandomValue("about:blank");
// Remove the tab before the update arrives.
let promise = promiseRemoveTab(tab);
// No tab state worth saving.
ok(!isValueInClosedData(r), "closed tab not saved");
yield promise;
// Still no tab state worth saving.
ok(!isValueInClosedData(r), "closed tab not saved");
});
add_task(function* save_worthy_tabs_remote() {
let {tab, r} = yield createTabWithRandomValue("https://example.com/");
ok(tab.linkedBrowser.isRemoteBrowser, "browser is remote");
// Remove the tab before the update arrives.
let promise = promiseRemoveTab(tab);
// Tab state deemed worth saving.
ok(isValueInClosedData(r), "closed tab saved");
yield promise;
// Tab state still deemed worth saving.
ok(isValueInClosedData(r), "closed tab saved");
});
add_task(function* save_worthy_tabs_nonremote() {
let {tab, r} = yield createTabWithRandomValue("about:robots");
ok(!tab.linkedBrowser.isRemoteBrowser, "browser is not remote");
// Remove the tab before the update arrives.
let promise = promiseRemoveTab(tab);
// Tab state deemed worth saving.
ok(isValueInClosedData(r), "closed tab saved");
yield promise;
// Tab state still deemed worth saving.
ok(isValueInClosedData(r), "closed tab saved");
});
add_task(function* save_worthy_tabs_remote_final() {
let {tab, r} = yield createTabWithRandomValue("about:blank");
let browser = tab.linkedBrowser;
ok(browser.isRemoteBrowser, "browser is remote");
// Replace about:blank with a new remote page.
let snippet = 'webNavigation.loadURI("https://example.com/", null, null, null, null)';
yield promiseNewLocationAndHistoryEntryReplaced(browser, snippet);
// Remotness shouldn't have changed.
ok(browser.isRemoteBrowser, "browser is still remote");
// Remove the tab before the update arrives.
let promise = promiseRemoveTab(tab);
// No tab state worth saving (that we know about yet).
ok(!isValueInClosedData(r), "closed tab not saved");
yield promise;
// Turns out there is a tab state worth saving.
ok(isValueInClosedData(r), "closed tab saved");
});
add_task(function* save_worthy_tabs_nonremote_final() {
let {tab, r} = yield createTabWithRandomValue("about:blank");
let browser = tab.linkedBrowser;
ok(browser.isRemoteBrowser, "browser is remote");
// Replace about:blank with a non-remote entry.
browser.loadURI("about:robots");
ok(!browser.isRemoteBrowser, "browser is not remote anymore");
// Wait until the new entry replaces about:blank.
yield promiseHistoryEntryReplacedNonRemote(browser);
// Remove the tab before the update arrives.
let promise = promiseRemoveTab(tab);
// No tab state worth saving (that we know about yet).
ok(!isValueInClosedData(r), "closed tab not saved");
yield promise;
// Turns out there is a tab state worth saving.
ok(isValueInClosedData(r), "closed tab saved");
});
add_task(function* dont_save_empty_tabs_final() {
let {tab, r} = yield createTabWithRandomValue("https://example.com/");
let browser = tab.linkedBrowser;
// Replace the current page with an about:blank entry.
let snippet = 'content.location.replace("about:blank")';
yield promiseNewLocationAndHistoryEntryReplaced(browser, snippet);
// Remove the tab before the update arrives.
let promise = promiseRemoveTab(tab);
// Tab state deemed worth saving (yet).
ok(isValueInClosedData(r), "closed tab saved");
yield promise;
// Turns out we don't want to save the tab state.
ok(!isValueInClosedData(r), "closed tab not saved");
});
add_task(function* undo_worthy_tabs() {
let {tab, r} = yield createTabWithRandomValue("https://example.com/");
ok(tab.linkedBrowser.isRemoteBrowser, "browser is remote");
// Remove the tab before the update arrives.
let promise = promiseRemoveTab(tab);
// Tab state deemed worth saving.
ok(isValueInClosedData(r), "closed tab saved");
// Restore the closed tab before receiving its final message.
tab = restoreClosedTabWithValue(r);
// Wait for the final update message.
yield promise;
// Check we didn't add the tab back to the closed list.
ok(!isValueInClosedData(r), "tab no longer closed");
// Cleanup.
yield promiseRemoveTab(tab);
});
add_task(function* forget_worthy_tabs_remote() {
let {tab, r} = yield createTabWithRandomValue("https://example.com/");
ok(tab.linkedBrowser.isRemoteBrowser, "browser is remote");
// Remove the tab before the update arrives.
let promise = promiseRemoveTab(tab);
// Tab state deemed worth saving.
ok(isValueInClosedData(r), "closed tab saved");
// Forget the closed tab.
ss.forgetClosedTab(window, 0);
// Wait for the final update message.
yield promise;
// Check we didn't add the tab back to the closed list.
ok(!isValueInClosedData(r), "we forgot about the tab");
});

Просмотреть файл

@ -14,7 +14,7 @@ add_task(function flush_on_tabclose() {
let browser = tab.linkedBrowser;
yield modifySessionStorage(browser, {test: "on-tab-close"});
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, "on-tab-close",
@ -59,7 +59,7 @@ add_task(function flush_on_duplicate() {
"sessionStorage data has been flushed when duplicating tabs");
yield promiseTabRestored(tab2);
gBrowser.removeTab(tab2);
yield promiseRemoveTab(tab2);
[{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, "on-duplicate",
"sessionStorage data has been flushed when duplicating tabs");
@ -128,7 +128,7 @@ add_task(function flush_on_tabclose_racy() {
// Flush all data contained in the content script but send it using
// asynchronous messages.
TabState.flushAsync(browser);
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, "on-tab-close-racy",

Просмотреть файл

@ -71,8 +71,8 @@ add_task(function* test_open_and_close() {
// Now close stuff, this should add closeAt
yield promiseWindowClosed(newWin);
gBrowser.removeTab(newTab1);
gBrowser.removeTab(newTab2);
yield promiseRemoveTab(newTab1);
yield promiseRemoveTab(newTab2);
state = CLOSED_STATE = JSON.parse(ss.getBrowserState());

Просмотреть файл

@ -20,7 +20,7 @@ add_task(function* test() {
let tab = win.gBrowser.addTab("about:mozilla");
yield promiseBrowserLoaded(tab.linkedBrowser);
TabState.flush(tab.linkedBrowser);
win.gBrowser.removeTab(win.gBrowser.tabs[0]);
yield promiseRemoveTab(win.gBrowser.tabs[0]);
// Make sure our window is still tracked by sessionstore
// and the window state is as expected.

Просмотреть файл

@ -30,7 +30,7 @@ add_task(function test_formdata() {
yield setInputValue(browser, {id: "txt", value: INNER_VALUE, frame: 0});
// Remove the tab.
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
});
}
@ -119,7 +119,7 @@ add_task(function test_nested() {
yield sendMessage(browser, "ss-test:sendKeyEvent", {key: "m", frame: 0});
// Remove the tab and check that we stored form data correctly.
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
let [{state: {formdata}}] = JSON.parse(ss.getClosedTabData(window));
is(JSON.stringify(formdata), JSON.stringify(FORM_DATA),
"formdata for iframe stored correctly");
@ -156,7 +156,7 @@ add_task(function test_design_mode() {
yield sendMessage(browser, "ss-test:sendKeyEvent", {key: "m"});
// Close and restore the tab.
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
tab = ss.undoCloseTab(window, 0);
browser = tab.linkedBrowser;
yield promiseTabRestored(tab);
@ -167,7 +167,7 @@ add_task(function test_design_mode() {
is(html, expected, "editable document has been restored correctly");
// Close and restore the tab.
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
tab = ss.undoCloseTab(window, 0);
browser = tab.linkedBrowser;
yield promiseTabRestored(tab);
@ -232,7 +232,7 @@ add_task(function test_ccNumbers() {
yield setInputValue(browser, {id: "txt", value: formValue});
// Remove the tab.
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
}
// Test that valid CC numbers are not collected.

Просмотреть файл

@ -32,7 +32,7 @@ add_task(function() {
yield promise;
info("Close then un-close page, 4 loads should take place");
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
let newTab = ss.undoCloseTab(window, 0);
yield waitForLoadsInBrowser(newTab.linkedBrowser, 4);
@ -77,7 +77,7 @@ add_task(function() {
yield promise;
info("iframe: Close then un-close page, 5 loads should take place");
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
let newTab = ss.undoCloseTab(window, 0);
yield waitForLoadsInBrowser(newTab.linkedBrowser, 5);

Просмотреть файл

@ -59,7 +59,7 @@ add_task(function nested_page_style() {
yield promiseBrowserLoaded(browser);
yield enableSubDocumentStyleSheetsForSet(browser, "alternate");
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
let [{state: {pageStyle}}] = JSON.parse(ss.getClosedTabData(window));
let expected = JSON.stringify({children: [{pageStyle: "alternate"}]});

Просмотреть файл

@ -15,7 +15,7 @@ add_task(function test_load_start() {
// Load a new URI but remove the tab before it has finished loading.
browser.loadURI("about:mozilla");
yield promiseContentMessage(browser, "ss-test:OnHistoryReplaceEntry");
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
// Undo close the tab.
tab = ss.undoCloseTab(window, 0);

Просмотреть файл

@ -129,7 +129,7 @@ add_task(function purge_domain() {
add_task(function respect_privacy_level() {
let tab = gBrowser.addTab(URL + "&secure");
yield promiseBrowserLoaded(tab.linkedBrowser);
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
@ -142,7 +142,7 @@ add_task(function respect_privacy_level() {
tab = gBrowser.addTab(URL + "&secure");
yield promiseBrowserLoaded(tab.linkedBrowser);
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
[{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
@ -158,7 +158,7 @@ add_task(function respect_privacy_level() {
yield promiseBrowserLoaded(tab.linkedBrowser);
let tab2 = gBrowser.duplicateTab(tab);
yield promiseTabRestored(tab2);
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
// With privacy_level=2 the |tab| shouldn't have any sessionStorage data.
[{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
@ -166,7 +166,7 @@ add_task(function respect_privacy_level() {
// Restore the default privacy level and close the duplicated tab.
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
gBrowser.removeTab(tab2);
yield promiseRemoveTab(tab2);
// With privacy_level=0 the duplicated |tab2| should persist all data.
[{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));

Просмотреть файл

@ -76,7 +76,7 @@ add_task(function history() {
}
} finally {
if (tab) {
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
}
}
});
@ -93,7 +93,7 @@ add_task(function close_tab() {
let statistics = yield promiseStats();
info("Now closing a tab");
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
tab = null;
let statistics2 = yield promiseStats();
@ -108,7 +108,7 @@ add_task(function close_tab() {
} finally {
if (tab) {
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
}
}
});
@ -202,7 +202,7 @@ add_task(function dom_storage() {
} finally {
if (tab) {
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
}
}
});
@ -235,7 +235,7 @@ add_task(function formdata() {
}
} finally {
if (tab) {
gBrowser.removeTab(tab);
yield promiseRemoveTab(tab);
}
}
});

Просмотреть файл

@ -65,8 +65,10 @@ let historyListener = {
])
};
docShell.QueryInterface(Ci.nsIWebNavigation).
let {sessionHistory} = docShell.QueryInterface(Ci.nsIWebNavigation);
if (sessionHistory) {
sessionHistory.addSHistoryListener(historyListener);
}
/**
* This frame script is only loaded for sessionstore mochitests. It enables us

Просмотреть файл

@ -527,3 +527,7 @@ for (let name of FORM_HELPERS) {
let msg = "ss-test:" + name;
this[name] = (browser, data) => sendMessage(browser, msg, data);
}
function promiseRemoveTab(tab) {
return BrowserTestUtils.removeTab(tab);
}

Просмотреть файл

@ -61,7 +61,8 @@ let WindowMessageHandler = {
// Function: isDocumentLoaded
// Checks if the currently active document is loaded.
isDocumentLoaded: function WMH_isDocumentLoaded(cx) {
let isLoaded = (content.document.readyState != "uninitialized" &&
let isLoaded = (content &&
content.document.readyState != "uninitialized" &&
!webProgress.isLoadingDocument);
sendAsyncMessage(cx.name, {isLoaded: isLoaded});
@ -71,7 +72,8 @@ let WindowMessageHandler = {
// Function: isImageDocument
// Checks if the currently active document is an image document or not.
isImageDocument: function WMH_isImageDocument(cx) {
let isImageDocument = (content.document instanceof Ci.nsIImageDocument);
let isImageDocument = (content &&
content.document instanceof Ci.nsIImageDocument);
sendAsyncMessage(cx.name, {isImageDocument: isImageDocument});
},

Просмотреть файл

@ -89,7 +89,9 @@ let FavIcons = {
tabImage = this._favIconService.getFaviconLinkForIcon(tabImageURI).spec;
}
tabImage = PlacesUtils.getImageURLForResolution(window, tabImage);
if (tabImage) {
tabImage = PlacesUtils.getImageURLForResolution(window, tabImage);
}
callback(tabImage);
},

Просмотреть файл

@ -24,19 +24,20 @@ function onTabViewWindowLoaded() {
is(groupItems.length, 1, "There is only one group");
is(groupItems[0].getChildren().length, 3, "The group has three tab items");
gBrowser.removeTab(tabTwo);
ok(TabView.isVisible(), "Tab View is still visible after removing a tab");
is(groupItems[0].getChildren().length, 2, "The group has two tab items");
BrowserTestUtils.removeTab(tabTwo).then(() => {
ok(TabView.isVisible(), "Tab View is still visible after removing a tab");
is(groupItems[0].getChildren().length, 2, "The group has two tab items");
restoreTab(function (tabTwo) {
ok(TabView.isVisible(), "Tab View is still visible after restoring a tab");
is(groupItems[0].getChildren().length, 3, "The group still has three tab items");
restoreTab(function (tabTwo) {
ok(TabView.isVisible(), "Tab View is still visible after restoring a tab");
is(groupItems[0].getChildren().length, 3, "The group still has three tab items");
// clean up and finish
hideTabView(function () {
gBrowser.removeTab(tabOne);
gBrowser.removeTab(tabTwo);
finish();
// clean up and finish
hideTabView(function () {
gBrowser.removeTab(tabOne);
gBrowser.removeTab(tabTwo);
finish();
});
});
});
}

Просмотреть файл

@ -56,18 +56,19 @@ function test() {
assertNumberOfTabs(2);
afterAllTabsLoaded(function () {
win.gBrowser.removeTab(tab);
assertNumberOfTabs(1);
assertNumberOfPinnedTabs(0);
BrowserTestUtils.removeTab(tab).then(() => {
assertNumberOfTabs(1);
assertNumberOfPinnedTabs(0);
restoreTab(function () {
prefix = 'unpinned-restored';
assertValidPrerequisites();
assertGroupItemPreserved();
restoreTab(function () {
prefix = 'unpinned-restored';
assertValidPrerequisites();
assertGroupItemPreserved();
createBlankTab();
afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab, win);
}, 0, win);
createBlankTab();
afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab, win);
}, 0, win);
});
}, win);
}

Просмотреть файл

@ -70,17 +70,24 @@ function test() {
let groupItem = getGroupItem(1);
let tabItem = groupItem.getChild(0);
// Wait until the tab has been removed but close it ourselves.
let promise = BrowserTestUtils.removeTab(tabItem.tab, {dontRemove: true});
// Close the tab.
EventUtils.synthesizeMouseAtCenter(
tabItem.$close[0], {}, TabView.getContentWindow());
assertNumberOfTabsInGroup(groupItem, 1);
restoreTab(function () {
assertNumberOfTabsInGroup(groupItem, 2);
promise.then(() => {
assertNumberOfTabsInGroup(groupItem, 1);
activateFirstGroupItem();
gBrowser.removeTab(gBrowser.tabs[1]);
gBrowser.removeTab(gBrowser.tabs[1]);
hideTabView(finishTest);
restoreTab(function () {
assertNumberOfTabsInGroup(groupItem, 2);
activateFirstGroupItem();
gBrowser.removeTab(gBrowser.tabs[1]);
gBrowser.removeTab(gBrowser.tabs[1]);
hideTabView(finishTest);
});
});
}

Просмотреть файл

@ -661,7 +661,7 @@ this.BrowserUITelemetry = {
"copyvideourl", "copyaudiourl", "saveimage", "shareimage", "sendimage",
"setDesktopBackground", "viewimageinfo", "viewimagedesc", "savevideo",
"sharevideo", "saveaudio", "video-saveimage", "sendvideo", "sendaudio",
"ctp-play", "ctp-hide", "sharepage", "savepage", "markpageMenu",
"ctp-play", "ctp-hide", "sharepage", "savepage", "pocket", "markpageMenu",
"viewbgimage", "undo", "cut", "copy", "paste", "delete", "selectall",
"keywordfield", "searchselect", "shareselect", "frame", "showonlythisframe",
"openframeintab", "openframe", "reloadframe", "bookmarkframe", "saveframe",

Просмотреть файл

@ -155,6 +155,7 @@ HiddenBrowser.prototype = {
this._hiddenFrame.get().then(aFrame => {
let doc = aFrame.document;
this._browser = doc.createElementNS(XUL_NS, "browser");
this._browser.permanentKey = {};
this._browser.setAttribute("type", "content");
this._browser.setAttribute("src", CUSTOMIZATION_URL);
this._browser.style.width = "400px";

Просмотреть файл

@ -523,6 +523,12 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
list-style-image: url("chrome://browser/skin/readinglist/readinglist-icon.svg");
}
#panelMenu_pocket,
#menu_pocket,
#BMB_pocket {
list-style-image: url("chrome://browser/content/pocket/panels/img/pocketmenuitem16.png");
}
#menu_openDownloads {
list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-moz-image-region: rect(0px 16px 16px 0px);

Просмотреть файл

@ -568,6 +568,24 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
list-style-image: url("chrome://browser/skin/readinglist/readinglist-icon.svg");
}
#panelMenu_pocket,
#menu_pocket,
#BMB_pocket {
list-style-image: url("chrome://browser/content/pocket/panels/img/pocketmenuitem16.png");
}
@media (min-resolution: 2dppx) {
#panelMenu_pocket,
#menu_pocket,
#BMB_pocket {
list-style-image: url("chrome://browser/content/pocket/panels/img/pocketmenuitem16@2x.png");
}
#panelMenu_pocket > .toolbarbutton-icon {
width: 16px;
}
}
/* ----- PRIMARY TOOLBAR BUTTONS ----- */
toolbar .toolbarbutton-1:not([type="menu-button"]),

Просмотреть файл

@ -2297,6 +2297,12 @@ notification[value="loop-sharing-notification"] .messageImage {
-moz-image-region: auto;
}
#panelMenu_pocket,
#menu_pocket,
#BMB_pocket {
list-style-image: url("chrome://browser/content/pocket/panels/img/pocketmenuitem16.png");
}
/* ::::: Keyboard UI Panel ::::: */
.KUI-panel {

Просмотреть файл

@ -34,6 +34,7 @@
#include "nsIDOMXULSelectCntrlItemEl.h"
#include "nsIDocument.h"
#include "nsLayoutStylesheetCache.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/EventStates.h"
@ -1601,6 +1602,10 @@ nsXULElement::LoadSrc()
slots->mFrameLoader = nsFrameLoader::Create(this, false);
NS_ENSURE_TRUE(slots->mFrameLoader, NS_OK);
(new AsyncEventDispatcher(this,
NS_LITERAL_STRING("XULFrameLoaderCreated"),
/* aBubbles */ true))->RunDOMEventWhenSafe();
if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::prerendered,
NS_LITERAL_STRING("true"), eIgnoreCase)) {
nsresult rv = slots->mFrameLoader->SetIsPrerendered();

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 954 B

После

Ширина:  |  Высота:  |  Размер: 681 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 675 B

После

Ширина:  |  Высота:  |  Размер: 493 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.3 KiB

После

Ширина:  |  Высота:  |  Размер: 931 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.8 KiB

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Просмотреть файл

@ -72,9 +72,9 @@
<ImageView
android:id="@+id/enabled_confirmation"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/img_check"
android:visibility="gone" />

Просмотреть файл

Просмотреть файл

@ -339,5 +339,27 @@ this.BrowserTestUtils = {
synthesizeMouseAtPoint(offsetX, offsetY, event, browser)
{
return BrowserTestUtils.synthesizeMouse(null, offsetX, offsetY, event, browser);
},
/**
* Removes the given tab from its parent tabbrowser and
* waits until its final message has reached the parent.
*/
removeTab(tab, options = {}) {
let dontRemove = options && options.dontRemove;
return new Promise(resolve => {
let {messageManager: mm, frameLoader} = tab.linkedBrowser;
mm.addMessageListener("SessionStore:update", function onMessage(msg) {
if (msg.targetFrameLoader == frameLoader && msg.data.isFinal) {
mm.removeMessageListener("SessionStore:update", onMessage);
resolve();
}
}, true);
if (!dontRemove && !tab.closing) {
tab.ownerDocument.defaultView.gBrowser.removeTab(tab);
}
});
}
};

Просмотреть файл

@ -880,6 +880,8 @@ this.PlacesUtils = {
* Set the POST data associated with a bookmark, if any.
* Used by POST keywords.
* @param aBookmarkId
*
* @deprecated Use PlacesUtils.keywords.insert() API instead.
*/
setPostDataForBookmark(aBookmarkId, aPostData) {
if (!aPostData)
@ -923,6 +925,8 @@ this.PlacesUtils = {
* Get the POST data associated with a bookmark, if any.
* @param aBookmarkId
* @returns string of POST data if set for aBookmarkId. null otherwise.
*
* @deprecated Use PlacesUtils.keywords.fetch() API instead.
*/
getPostDataForBookmark(aBookmarkId) {
let stmt = PlacesUtils.history.DBConnection.createStatement(

Просмотреть файл

@ -533,18 +533,24 @@ interface nsINavBookmarksService : nsISupports
*
* Use an empty keyword to clear the keyword associated with the URI.
* In both of these cases, succeeds but does nothing if the URL/keyword is not found.
*
* @deprecated Use PlacesUtils.keywords.insert() API instead.
*/
void setKeywordForBookmark(in long long aItemId, in AString aKeyword);
/**
* Retrieves the keyword for the given bookmark. Will be void string
* (null in JS) if no such keyword is found.
*
* @deprecated Use PlacesUtils.keywords.fetch() API instead.
*/
AString getKeywordForBookmark(in long long aItemId);
/**
* Returns the URI associated with the given keyword. Empty if no such
* keyword is found.
*
* @deprecated Use PlacesUtils.keywords.fetch() API instead.
*/
nsIURI getURIForKeyword(in AString keyword);

Просмотреть файл

@ -2445,6 +2445,8 @@ nsNavBookmarks::GetURIForKeyword(const nsAString& aUserCasedKeyword,
NS_ENSURE_TRUE(!aUserCasedKeyword.IsEmpty(), NS_ERROR_INVALID_ARG);
*aURI = nullptr;
PLACES_WARN_DEPRECATED();
// Shortcuts are always lowercased internally.
nsAutoString keyword(aUserCasedKeyword);
ToLowerCase(keyword);

Просмотреть файл

@ -402,11 +402,6 @@
]]></getter>
</property>
<field name="_permanentKey">({})</field>
<property name="permanentKey" readonly="true"
onget="return this._permanentKey;"/>
<property name="outerWindowID" readonly="true">
<getter><![CDATA[
return this.contentWindow
@ -1096,8 +1091,7 @@
"_docShell",
"_webBrowserFind",
"_contentWindow",
"_webNavigation",
"_permanentKey"
"_webNavigation"
];
if (this.isRemoteBrowser) {

Просмотреть файл

@ -134,9 +134,12 @@
readonly="true"/>
<property name="contentDocument" readonly="true"
onget="return this.webNavigation.document;"/>
<property name="docShell"
onget="return this.boxObject.docShell;"
readonly="true"/>
<property name="docShell" readonly="true">
<getter><![CDATA[
let frameLoader = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
return frameLoader ? frameLoader.docShell : null;
]]></getter>
</property>
<property name="currentURI"
readonly="true"
onget="return this.webNavigation.currentURI;"/>

Просмотреть файл

@ -85,9 +85,12 @@
<binding id="iframe" role="outerdoc">
<implementation>
<property name="docShell"
readonly="true"
onget="return this.boxObject.docShell"/>
<property name="docShell" readonly="true">
<getter><![CDATA[
let frameLoader = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
return frameLoader ? frameLoader.docShell : null;
]]></getter>
</property>
<property name="contentWindow"
readonly="true"
onget="return this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow);"/>

Просмотреть файл

@ -4746,6 +4746,10 @@ BreakpointActor.prototype = {
actorPrefix: "breakpoint",
condition: null,
disconnect: function () {
this.removeScripts();
},
hasScript: function (aScript) {
return this.scripts.has(aScript);
},

Просмотреть файл

@ -49,6 +49,16 @@ function waitForPage(pages) {
});
}
function swapDocShells(browser1, browser2) {
// Swap frameLoaders.
browser1.swapDocShells(browser2);
// Swap permanentKeys.
let tmp = browser1.permanentKey;
browser1.permanentKey = browser2.permanentKey;
browser2.permanentKey = tmp;
}
// Test that opening a page creates a port, sends the load event and then
// navigating to a new page sends the unload event. Going back should create a
// new port
@ -193,7 +203,7 @@ add_task(function* browser_switch() {
port1.removeMessageListener("Cookie", failOnMessage);
is(message.data.value, "om nom nom", "Should have the right cookie");
browser1.swapDocShells(browser2);
swapDocShells(browser1, browser2);
is(port1.browser, browser2, "Should have noticed the swap");
is(port2.browser, browser1, "Should have noticed the swap");
@ -210,7 +220,7 @@ add_task(function* browser_switch() {
port1.removeMessageListener("Cookie", failOnMessage);
is(message.data.value, "om nom nom", "Should have the right cookie");
browser1.swapDocShells(browser2);
swapDocShells(browser1, browser2);
is(port1.browser, browser1, "Should have noticed the swap");
is(port2.browser, browser2, "Should have noticed the swap");