зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1602173: Capture attempts to load pages and redirect back to the browser when needed. r=Gijs
Differential Revision: https://phabricator.services.mozilla.com/D56286 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
5577453668
Коммит
e5bd7eee3c
|
@ -279,6 +279,17 @@ let ACTORS = {
|
|||
allFrames: true,
|
||||
},
|
||||
|
||||
SiteSpecificBrowser: {
|
||||
parent: {
|
||||
moduleURI: "resource:///actors/SiteSpecificBrowserParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "resource:///actors/SiteSpecificBrowserChild.jsm",
|
||||
},
|
||||
|
||||
allFrames: true,
|
||||
},
|
||||
|
||||
UITour: {
|
||||
parent: {
|
||||
moduleURI: "resource:///modules/UITourParent.jsm",
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/* 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";
|
||||
|
||||
const EXPORTED_SYMBOLS = ["SiteSpecificBrowserChild"];
|
||||
|
||||
const { SiteSpecificBrowserBase } = ChromeUtils.import(
|
||||
"resource:///modules/SiteSpecificBrowserService.jsm"
|
||||
);
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { E10SUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/E10SUtils.jsm"
|
||||
);
|
||||
|
||||
class SiteSpecificBrowserChild extends JSWindowActorChild {
|
||||
receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case "SetSSB":
|
||||
// Note that this sets the webbrowserchrome for the top-level browser
|
||||
// child in this page. This means that any inner-frames loading in
|
||||
// different processes will not be handled correctly. Fixing this will
|
||||
// happen in bug 1602849.
|
||||
this.docShell.browserChild.webBrowserChrome = new WebBrowserChrome(
|
||||
message.data
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getActor(docShell) {
|
||||
return docShell.domWindow
|
||||
.getWindowGlobalChild()
|
||||
.getActor("SiteSpecificBrowser");
|
||||
}
|
||||
|
||||
// JS actors can't generally be XPCOM objects so we must use a separate class.
|
||||
class WebBrowserChrome {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
get ssb() {
|
||||
return SiteSpecificBrowserBase.get(this.id);
|
||||
}
|
||||
|
||||
// nsIWebBrowserChrome3
|
||||
|
||||
/**
|
||||
* This gets called when a user clicks on a link or submits a form. We can use
|
||||
* it to see where the resulting page load will occur and if needed redirect
|
||||
* it to a different target.
|
||||
*
|
||||
* @param {string} originalTarget the target intended for the load.
|
||||
* @param {nsIURI} linkURI the URI that will be loaded.
|
||||
* @param {Node} linkNode the element causing the load.
|
||||
* @param {boolean} isAppTab whether the source docshell is marked as an
|
||||
* app tab.
|
||||
* @return {string} the target to use for the load.
|
||||
*/
|
||||
onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
|
||||
// Our actor is for the top-level frame in the page while this may be being
|
||||
// called for a navigation in an inner frame. First we have to find the
|
||||
// browsing context for the frame doing the load.
|
||||
|
||||
let docShell = linkNode.ownerGlobal.docShell;
|
||||
let bc = docShell.browsingContext;
|
||||
|
||||
// Which browsing context is this link targetting?
|
||||
let target = originalTarget ? bc.findWithName(originalTarget) : bc;
|
||||
|
||||
if (target) {
|
||||
// If we found a target then it must be one of the frames within this
|
||||
// frame tree since we don't support popup windows.
|
||||
if (target.parent) {
|
||||
// An inner frame, continue.
|
||||
return originalTarget;
|
||||
}
|
||||
|
||||
// A top-level load. If our SSB cannot load this URI then start the
|
||||
// process of opening it into a new tab somewhere.
|
||||
return this.ssb.canLoad(linkURI) ? originalTarget : "_blank";
|
||||
}
|
||||
|
||||
// An attempt to open a new window/tab. If the new URI can be loaded by our
|
||||
// SSB then load it at the top-level. Note that we override the requested
|
||||
// target so that this page can't reach the new context.
|
||||
return this.ssb.canLoad(linkURI) ? "_top" : "_blank";
|
||||
}
|
||||
|
||||
/**
|
||||
* A load is about to occur in a frame. This is an opportunity to stop it
|
||||
* and redirect it somewhere.
|
||||
*
|
||||
* @param {nsIDocShell} docShell the current docshell.
|
||||
* @param {nsIURI} uri the URI that will be loaded.
|
||||
* @param {nsIReferrerInfo} referrerInfo the referrer info.
|
||||
* @param {boolean} hasPostData whether there is POST data
|
||||
* for the load.
|
||||
* @param {nsIPrincipal} triggeringPrincipal the triggering principal.
|
||||
* @param {nsIContentSecurityPolicy} csp the content security policy.
|
||||
* @return {boolean} whether the load should proceed or not.
|
||||
*/
|
||||
shouldLoadURI(
|
||||
docShell,
|
||||
uri,
|
||||
referrerInfo,
|
||||
hasPostData,
|
||||
triggeringPrincipal,
|
||||
csp
|
||||
) {
|
||||
// As above, our actor is for the top-level frame in the page however we
|
||||
// are passed the docshell potentially handling the load here so we can
|
||||
// do the right thing.
|
||||
|
||||
// We only police loads at the top level.
|
||||
if (docShell.browsingContext.parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.ssb.canLoad(uri)) {
|
||||
// Should only have got this far for a window.location manipulation.
|
||||
|
||||
getActor(docShell).sendAsyncMessage("RetargetOutOfScopeURIToBrowser", {
|
||||
uri: uri.spec,
|
||||
referrerInfo: E10SUtils.serializeReferrerInfo(referrerInfo),
|
||||
triggeringPrincipal: E10SUtils.serializePrincipal(
|
||||
triggeringPrincipal ||
|
||||
Services.scriptSecurityManager.createNullPrincipal({})
|
||||
),
|
||||
csp: csp ? E10SUtils.serializeCSP(csp) : null,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple check for whether this is the correct process to load this URI.
|
||||
*
|
||||
* @param {nsIURI} uri the URI that will be loaded.
|
||||
* @return {boolean} whether the load should proceed or not.
|
||||
*/
|
||||
shouldLoadURIInThisProcess(uri) {
|
||||
return this.ssb.canLoad(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs us to start a fresh process to load this URI. Usually used for
|
||||
* large allocation sites. SSB does not support this.
|
||||
*
|
||||
* @param {nsIDocShell} docShell the current docshell.
|
||||
* @param {nsIURI} uri the URI that will be loaded.
|
||||
* @param {nsIReferrerInfo} referrerInfo the referrer info.
|
||||
* @param {nsIPrincipal} triggeringPrincipal the triggering principal.
|
||||
* @param {Number} loadFlags the load flags.
|
||||
* @param {nsIContentSecurityPolicy} csp the content security policy.
|
||||
* @return {boolean} whether the load should proceed or not.
|
||||
*/
|
||||
reloadInFreshProcess(
|
||||
docShell,
|
||||
uri,
|
||||
referrerInfo,
|
||||
triggeringPrincipal,
|
||||
loadFlags,
|
||||
csp
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
|
||||
/* 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";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["SiteSpecificBrowserParent"];
|
||||
|
||||
const { BrowserWindowTracker } = ChromeUtils.import(
|
||||
"resource:///modules/BrowserWindowTracker.jsm"
|
||||
);
|
||||
const { E10SUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/E10SUtils.jsm"
|
||||
);
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
|
||||
class SiteSpecificBrowserParent extends JSWindowActorParent {
|
||||
receiveMessage(message) {
|
||||
switch (message.name) {
|
||||
case "RetargetOutOfScopeURIToBrowser":
|
||||
// The content process found a URI that needs to be loaded in the main
|
||||
// browser.
|
||||
let triggeringPrincipal = E10SUtils.deserializePrincipal(
|
||||
message.data.triggeringPrincipal
|
||||
);
|
||||
let referrerInfo = E10SUtils.deserializeReferrerInfo(
|
||||
message.data.referrerInfo
|
||||
);
|
||||
let csp = E10SUtils.deserializeCSP(message.data.csp);
|
||||
|
||||
// Attempt to find an existing window to open it in.
|
||||
let win = BrowserWindowTracker.getTopWindow();
|
||||
if (win) {
|
||||
win.gBrowser.selectedTab = win.gBrowser.addTab(message.data.uri, {
|
||||
triggeringPrincipal,
|
||||
csp,
|
||||
referrerInfo,
|
||||
});
|
||||
} else {
|
||||
let sa = Cc["@mozilla.org/array;1"].createInstance(
|
||||
Ci.nsIMutableArray
|
||||
);
|
||||
|
||||
let wuri = Cc["@mozilla.org/supports-string;1"].createInstance(
|
||||
Ci.nsISupportsString
|
||||
);
|
||||
wuri.data = message.data.uri;
|
||||
|
||||
sa.appendElement(wuri);
|
||||
sa.appendElement(null); // unused (bug 871161)
|
||||
sa.appendElement(referrerInfo);
|
||||
sa.appendElement(null); // postData
|
||||
sa.appendElement(null); // allowThirdPartyFixup
|
||||
sa.appendElement(null); // userContextId
|
||||
sa.appendElement(null); // originPrincipal
|
||||
sa.appendElement(null); // originStoragePrincipal
|
||||
sa.appendElement(triggeringPrincipal);
|
||||
sa.appendElement(null); // allowInheritPrincipal
|
||||
sa.appendElement(csp);
|
||||
|
||||
Services.ww.openWindow(
|
||||
null,
|
||||
AppConstants.BROWSER_CHROME_URL,
|
||||
null,
|
||||
"chrome,dialog=no,all",
|
||||
sa
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,24 +2,194 @@
|
|||
* 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/. */
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"SiteSpecificBrowser",
|
||||
"resource:///modules/SiteSpecificBrowserService.jsm"
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
SiteSpecificBrowser: "resource:///modules/SiteSpecificBrowserService.jsm",
|
||||
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
|
||||
});
|
||||
|
||||
let gSSBBrowser = null;
|
||||
let gSSB = null;
|
||||
|
||||
function init() {
|
||||
gSSB = SiteSpecificBrowser.get(window.arguments[0]);
|
||||
|
||||
window.browserDOMWindow = new BrowserDOMWindow();
|
||||
|
||||
gSSBBrowser = document.createXULElement("browser");
|
||||
gSSBBrowser.setAttribute("id", "browser");
|
||||
gSSBBrowser.setAttribute("type", "content");
|
||||
gSSBBrowser.setAttribute("remote", "true");
|
||||
gSSBBrowser.setAttribute("nodefaultsrc", "true");
|
||||
document.getElementById("browser-container").appendChild(gSSBBrowser);
|
||||
|
||||
// Give our actor the SSB's ID.
|
||||
let actor = gSSBBrowser.browsingContext.currentWindowGlobal.getActor(
|
||||
"SiteSpecificBrowser"
|
||||
);
|
||||
actor.sendAsyncMessage("SetSSB", gSSB.id);
|
||||
|
||||
gSSBBrowser.src = gSSB.startURI.spec;
|
||||
}
|
||||
|
||||
window.addEventListener("load", init, true);
|
||||
class BrowserDOMWindow {
|
||||
/**
|
||||
* Called when a page in the main process needs a new window to display a new
|
||||
* page in.
|
||||
*
|
||||
* @param {nsIURI?} uri
|
||||
* @param {Window} opener
|
||||
* @param {Number} where
|
||||
* @param {Number} flags
|
||||
* @param {nsIPrincipal} triggeringPrincipal
|
||||
* @param {nsIContentSecurityPolicy?} csp
|
||||
* @return {BrowsingContext} the BrowsingContext the URI should be loaded in.
|
||||
*/
|
||||
createContentWindow(uri, opener, where, flags, triggeringPrincipal, csp) {
|
||||
console.error(
|
||||
"createContentWindow should never be called from a remote browser"
|
||||
);
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from a page in the main process to open a new URI.
|
||||
*
|
||||
* @param {nsIURI} uri
|
||||
* @param {Window} opener
|
||||
* @param {Number} where
|
||||
* @param {Number} flags
|
||||
* @param {nsIPrincipal} triggeringPrincipal
|
||||
* @param {nsIContentSecurityPolicy?} csp
|
||||
* @return {BrowsingContext} the BrowsingContext the URI should be loaded in.
|
||||
*/
|
||||
openURI(uri, opener, where, flags, triggeringPrincipal, csp) {
|
||||
console.error("openURI should never be called from a remote browser");
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a new frame to load some content in.
|
||||
*
|
||||
* @param {nsIURI?} uri
|
||||
* @param {nsIOpenURIInFrameParams} params
|
||||
* @param {Number} where
|
||||
* @param {Number} flags
|
||||
* @param {Number} nextRemoteTabId
|
||||
* @param {string} name
|
||||
* @param {boolean} shouldOpen should the load start or not.
|
||||
* @return {Element} the frame element the URI should be loaded in.
|
||||
*/
|
||||
getContentWindowOrOpenURIInFrame(
|
||||
uri,
|
||||
params,
|
||||
where,
|
||||
flags,
|
||||
nextRemoteTabId,
|
||||
name,
|
||||
shouldOpen
|
||||
) {
|
||||
// It's been determined that this load needs to happen in a new frame.
|
||||
// Either onBeforeLinkTraversal set this correctly or this is the result
|
||||
// of a window.open call.
|
||||
|
||||
// If this ssb can load the url then just load it internally.
|
||||
if (gSSB.canLoad(uri)) {
|
||||
return gSSBBrowser;
|
||||
}
|
||||
|
||||
// Try and find a browser window to open in.
|
||||
let win = BrowserWindowTracker.getTopWindow({
|
||||
private: params.isPrivate,
|
||||
allowPopups: false,
|
||||
});
|
||||
|
||||
if (win) {
|
||||
// Just hand off to the window's handler
|
||||
win.focus();
|
||||
return win.browserDOMWindow.openURIInFrame(
|
||||
shouldOpen ? uri : null,
|
||||
params,
|
||||
where,
|
||||
flags,
|
||||
nextRemoteTabId,
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
// We need to open a new browser window and a tab in it. That's an
|
||||
// asychronous operation but luckily if we return null here the platform
|
||||
// handles doing that for us.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an nsFrameLoaderOwner to load some new content in.
|
||||
*
|
||||
* @param {nsIURI?} uri
|
||||
* @param {nsIOpenURIInFrameParams} params
|
||||
* @param {Number} where
|
||||
* @param {Number} flags
|
||||
* @param {Number} nextRemoteTabId
|
||||
* @param {string} name
|
||||
* @return {Element} the frame element the URI should be loaded in.
|
||||
*/
|
||||
createContentWindowInFrame(uri, params, where, flags, nextRemoteTabId, name) {
|
||||
return this.getContentWindowOrOpenURIInFrame(
|
||||
uri,
|
||||
params,
|
||||
where,
|
||||
flags,
|
||||
nextRemoteTabId,
|
||||
name,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new nsFrameLoaderOwner and load some content into it.
|
||||
*
|
||||
* @param {nsIURI} uri
|
||||
* @param {nsIOpenURIInFrameParams} params
|
||||
* @param {Number} where
|
||||
* @param {Number} flags
|
||||
* @param {Number} nextRemoteTabId
|
||||
* @param {string} name
|
||||
* @return {Element} the frame element the URI is loading in.
|
||||
*/
|
||||
openURIInFrame(uri, params, where, flags, nextRemoteTabId, name) {
|
||||
return this.getContentWindowOrOpenURIInFrame(
|
||||
uri,
|
||||
params,
|
||||
where,
|
||||
flags,
|
||||
nextRemoteTabId,
|
||||
name,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
isTabContentWindow(window) {
|
||||
// This method is probably not needed anymore: bug 1602915
|
||||
return gSSBBrowser.contentWindow == window;
|
||||
}
|
||||
|
||||
canClose() {
|
||||
return BrowserUtils.canCloseWindow(window);
|
||||
}
|
||||
|
||||
get tabCount() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
BrowserDOMWindow.prototype.QueryInterface = ChromeUtils.generateQI([
|
||||
Ci.nsIBrowserDOMWindow,
|
||||
]);
|
||||
|
||||
window.addEventListener("DOMContentLoaded", init, true);
|
||||
|
|
|
@ -14,3 +14,8 @@ XPCOM_MANIFESTS += [
|
|||
EXTRA_JS_MODULES += [
|
||||
'SiteSpecificBrowserService.jsm',
|
||||
]
|
||||
|
||||
FINAL_TARGET_FILES.actors += [
|
||||
'SiteSpecificBrowserChild.jsm',
|
||||
'SiteSpecificBrowserParent.jsm',
|
||||
]
|
||||
|
|
|
@ -2,9 +2,16 @@
|
|||
support-files =
|
||||
head.js
|
||||
test_page.html
|
||||
empty_page.html
|
||||
prefs =
|
||||
browser.ssb.enabled=true
|
||||
|
||||
[browser_ssb_direct.js]
|
||||
[browser_ssb_lasttab.js]
|
||||
[browser_ssb_menu.js]
|
||||
[browser_ssb_newtab.js]
|
||||
[browser_ssb_newwindow.js]
|
||||
[browser_ssb_open.js]
|
||||
[browser_ssb_windowlocation.js]
|
||||
[browser_ssb_windowopen.js]
|
||||
skip-if = true # It is unclear what we want to do here.
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
async function testDirectLoad(target, checker) {
|
||||
let ssb = await openSSB(gHttpsTestRoot + "test_page.html#" + target);
|
||||
|
||||
let promise = checker(ssb);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#direct",
|
||||
{},
|
||||
getBrowser(ssb)
|
||||
);
|
||||
|
||||
await promise;
|
||||
await BrowserTestUtils.closeWindow(ssb);
|
||||
}
|
||||
|
||||
// A link that should load inside the ssb
|
||||
add_task(async function local() {
|
||||
await testDirectLoad(gHttpsTestRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
await expectSSBLoad(ssb);
|
||||
Assert.equal(
|
||||
getBrowser(ssb).currentURI.spec,
|
||||
gHttpsTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// A link to an insecure site should load outside the ssb
|
||||
add_task(async function insecure() {
|
||||
await testDirectLoad(gHttpTestRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// A link to a different host should load outside the ssb
|
||||
add_task(async function external() {
|
||||
await testDirectLoad(gHttpsOtherRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpsOtherRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
async function testNewTabLoad(target, checker) {
|
||||
let ssb = await openSSB(gHttpsTestRoot + "test_page.html#" + target);
|
||||
|
||||
let promise = checker(ssb);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#new-tab",
|
||||
{},
|
||||
getBrowser(ssb)
|
||||
);
|
||||
|
||||
await promise;
|
||||
await BrowserTestUtils.closeWindow(ssb);
|
||||
}
|
||||
|
||||
// A link that should load inside the ssb
|
||||
add_task(async function local() {
|
||||
await testNewTabLoad(gHttpsTestRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
await expectSSBLoad(ssb);
|
||||
Assert.equal(
|
||||
getBrowser(ssb).currentURI.spec,
|
||||
gHttpsTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// A link to an insecure site should load outside the ssb
|
||||
add_task(async function insecure() {
|
||||
await testNewTabLoad(gHttpTestRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// A link to a different host should load outside the ssb
|
||||
add_task(async function external() {
|
||||
await testNewTabLoad(gHttpsOtherRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpsOtherRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
async function testNewWindowLoad(target, checker) {
|
||||
let ssb = await openSSB(gHttpsTestRoot + "test_page.html#" + target);
|
||||
|
||||
let promise = checker(ssb);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#new-window",
|
||||
{},
|
||||
getBrowser(ssb)
|
||||
);
|
||||
|
||||
await promise;
|
||||
await BrowserTestUtils.closeWindow(ssb);
|
||||
}
|
||||
|
||||
// A link that should load inside the ssb
|
||||
add_task(async function local() {
|
||||
await testNewWindowLoad(gHttpsTestRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
await expectSSBLoad(ssb);
|
||||
Assert.equal(
|
||||
getBrowser(ssb).currentURI.spec,
|
||||
gHttpsTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// A link to an insecure site should load outside the ssb
|
||||
add_task(async function insecure() {
|
||||
await testNewWindowLoad(gHttpTestRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// A link to a different host should load outside the ssb
|
||||
add_task(async function external() {
|
||||
await testNewWindowLoad(gHttpsOtherRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpsOtherRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,72 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
async function testWindowLocationLoad(target, checker) {
|
||||
let ssb = await openSSB(gHttpsTestRoot + "test_page.html#" + target);
|
||||
|
||||
let promise = checker(ssb);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#window-location",
|
||||
{},
|
||||
getBrowser(ssb)
|
||||
);
|
||||
|
||||
await promise;
|
||||
await BrowserTestUtils.closeWindow(ssb);
|
||||
}
|
||||
|
||||
// A link that should load inside the ssb
|
||||
add_task(async function local() {
|
||||
await testWindowLocationLoad(
|
||||
gHttpsTestRoot + "empty_page.html",
|
||||
async ssb => {
|
||||
try {
|
||||
await expectSSBLoad(ssb);
|
||||
Assert.equal(
|
||||
getBrowser(ssb).currentURI.spec,
|
||||
gHttpsTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// A link to an insecure site should load outside the ssb
|
||||
add_task(async function insecure() {
|
||||
await testWindowLocationLoad(gHttpTestRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// A link to a different host should load outside the ssb
|
||||
add_task(async function external() {
|
||||
await testWindowLocationLoad(
|
||||
gHttpsOtherRoot + "empty_page.html",
|
||||
async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpsOtherRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
async function testWindowOpenLoad(target, checker) {
|
||||
let ssb = await openSSB(gHttpsTestRoot + "test_page.html#" + target);
|
||||
|
||||
let promise = checker(ssb);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#window-open",
|
||||
{},
|
||||
getBrowser(ssb)
|
||||
);
|
||||
|
||||
await promise;
|
||||
await BrowserTestUtils.closeWindow(ssb);
|
||||
}
|
||||
|
||||
// A link that should load inside the ssb
|
||||
add_task(async function local() {
|
||||
await testWindowOpenLoad(gHttpsTestRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
await expectSSBLoad(ssb);
|
||||
Assert.equal(
|
||||
getBrowser(ssb).currentURI.spec,
|
||||
gHttpsTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// A link to an insecure site should load outside the ssb
|
||||
add_task(async function insecure() {
|
||||
await testWindowOpenLoad(gHttpTestRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpTestRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// A link to a different host should load outside the ssb
|
||||
add_task(async function external() {
|
||||
await testWindowOpenLoad(gHttpsOtherRoot + "empty_page.html", async ssb => {
|
||||
try {
|
||||
let tab = await expectTabLoad(ssb);
|
||||
Assert.equal(
|
||||
tab.linkedBrowser.currentURI.spec,
|
||||
gHttpsOtherRoot + "empty_page.html",
|
||||
"Should have loaded the right uri."
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
} catch (e) {
|
||||
// Any error will already have logged a failure.
|
||||
}
|
||||
});
|
||||
});
|
|
@ -13,9 +13,34 @@ const gHttpsTestRoot = getRootDirectory(gTestPath).replace(
|
|||
"https://example.com/"
|
||||
);
|
||||
|
||||
// A different secure site to use.
|
||||
const gHttpsOtherRoot = getRootDirectory(gTestPath).replace(
|
||||
"chrome://mochitests/content/",
|
||||
"https://example.org/"
|
||||
);
|
||||
|
||||
// The chrome url for the SSB UI.
|
||||
const SSB_WINDOW = "chrome://browser/content/ssb/ssb.html";
|
||||
|
||||
// Directly opens an SSB for the given URI. Resolves to the SSB DOM window after
|
||||
// the SSB content has loaded.
|
||||
async function openSSB(uri) {
|
||||
if (!(uri instanceof Ci.nsIURI)) {
|
||||
uri = Services.io.newURI(uri);
|
||||
}
|
||||
|
||||
let openPromise = BrowserTestUtils.domWindowOpened(null, async domwin => {
|
||||
await BrowserTestUtils.waitForEvent(domwin, "load");
|
||||
return domwin.location.toString() == SSB_WINDOW;
|
||||
});
|
||||
|
||||
SiteSpecificBrowserService.launchFromURI(uri);
|
||||
|
||||
let ssbwin = await openPromise;
|
||||
await BrowserTestUtils.browserLoaded(getBrowser(ssbwin), true, uri.spec);
|
||||
return ssbwin;
|
||||
}
|
||||
|
||||
// Simulates opening a SSB from the main browser window. Resolves to the SSB
|
||||
// DOM window after the SSB content has loaded.
|
||||
async function openSSBFromBrowserWindow(win = window) {
|
||||
|
@ -34,7 +59,7 @@ async function openSSBFromBrowserWindow(win = window) {
|
|||
Assert.ok(!openItem.hidden, "Open menu item should not be hidden");
|
||||
|
||||
let openPromise = BrowserTestUtils.domWindowOpened(null, async domwin => {
|
||||
await BrowserTestUtils.waitForEvent(domwin, "load");
|
||||
await BrowserTestUtils.waitForEvent(domwin, "DOMContentLoaded");
|
||||
return domwin.location.toString() == SSB_WINDOW;
|
||||
});
|
||||
|
||||
|
@ -49,3 +74,135 @@ async function openSSBFromBrowserWindow(win = window) {
|
|||
function getBrowser(ssbwin) {
|
||||
return ssbwin.document.getElementById("browser");
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a load in response to an attempt to navigate from the SSB. It
|
||||
* listens for new tab opens in the main window, new window opens and loads in
|
||||
* the SSB itself. It returns a promise.
|
||||
*
|
||||
* The `where` argument is a string saying where the load is expected to
|
||||
* happen, "ssb", "tab" or "window". The promise rejects if the load happens
|
||||
* somewhere else and the offending new item (tab or window) get closed. When
|
||||
* the load is seen in the correct location different things are returned
|
||||
* depending on `where`. For "tab" the new tab is returned (it will have
|
||||
* finished loading), for "window" the new window is returned (it will have
|
||||
* finished loading). The "ssb" case doesn't return anything.
|
||||
*
|
||||
* Generally use the methods below this as they look more obvious.
|
||||
*/
|
||||
function expectLoadSomewhere(ssb, where, win = window) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Listens for a new tab opening in the main window.
|
||||
const tabListener = async ({ target: tab }) => {
|
||||
cleanup();
|
||||
|
||||
await BrowserTestUtils.browserLoaded(
|
||||
tab.linkedBrowser,
|
||||
true,
|
||||
uri => uri != "about:blank"
|
||||
);
|
||||
|
||||
if (where != "tab") {
|
||||
Assert.ok(
|
||||
false,
|
||||
`Did not expect ${
|
||||
tab.linkedBrowser.currentURI.spec
|
||||
} to load in a new tab.`
|
||||
);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
reject(new Error("Page unexpectedly loaded in a new tab."));
|
||||
return;
|
||||
}
|
||||
Assert.ok(
|
||||
true,
|
||||
`${tab.linkedBrowser.currentURI.spec} loaded in a new tab as expected.`
|
||||
);
|
||||
resolve(tab);
|
||||
};
|
||||
win.gBrowser.tabContainer.addEventListener("TabOpen", tabListener);
|
||||
|
||||
// Listens for new top-level windows.
|
||||
const winObserver = async (domwin, topic) => {
|
||||
if (topic != "domwindowopened") {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
await BrowserTestUtils.waitForEvent(
|
||||
domwin,
|
||||
"load",
|
||||
uri => uri != "about:blank"
|
||||
);
|
||||
|
||||
if (where != "window") {
|
||||
Assert.ok(false, `Did not expect a new ${domwin.location} to open.`);
|
||||
await BrowserTestUtils.closeWindow(domwin);
|
||||
reject(new Error("New window unexpectedly opened."));
|
||||
return;
|
||||
}
|
||||
Assert.ok(true, `${domwin.location} opened as expected.`);
|
||||
resolve(domwin);
|
||||
};
|
||||
Services.ww.registerNotification(winObserver);
|
||||
|
||||
BrowserTestUtils.browserLoaded(
|
||||
getBrowser(ssb),
|
||||
true,
|
||||
uri => uri != "about:blank"
|
||||
).then(() => {
|
||||
cleanup();
|
||||
|
||||
if (where != "ssb") {
|
||||
Assert.ok(
|
||||
false,
|
||||
`Did not expect ${
|
||||
getBrowser(ssb).currentURI.spec
|
||||
} to load in the ssb window.`
|
||||
);
|
||||
reject(new Error("Page unexpectedly loaded in the ssb window."));
|
||||
return;
|
||||
}
|
||||
Assert.ok(
|
||||
true,
|
||||
`${
|
||||
getBrowser(ssb).currentURI.spec
|
||||
} loaded in the ssb window as expected.`
|
||||
);
|
||||
resolve();
|
||||
}, reject);
|
||||
|
||||
// Makes sure that no notifications fire after the test is done. We assume
|
||||
// that the SSB window will be closed between tests and so don't need to
|
||||
// unregister the load listener for the SSB browser itself.
|
||||
const cleanup = () => {
|
||||
win.gBrowser.tabContainer.removeEventListener("TabOpen", tabListener);
|
||||
Services.ww.unregisterNotification(winObserver);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a load to occur in the ssb window but rejects if a new tab or
|
||||
* window get opened.
|
||||
*/
|
||||
function expectSSBLoad(ssb, win = window) {
|
||||
return expectLoadSomewhere(ssb, "ssb", win);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a new tab to be opened and loaded. Rejects if a new window is
|
||||
* opened or the ssb loads something before. Resolves with the new loaded tab.
|
||||
*/
|
||||
function expectTabLoad(ssb, win = window) {
|
||||
return expectLoadSomewhere(ssb, "tab", win);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a new window to be opened and loaded. Rejects if a new tab is
|
||||
* opened or the ssb loads something before. Resolves with the new loaded
|
||||
* window.
|
||||
*/
|
||||
function expectWindowOpen(ssb, win = window) {
|
||||
return expectLoadSomewhere(ssb, "window", win);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript">
|
||||
function initialize() {
|
||||
let target = window.location.hash;
|
||||
if (target.length < 2) {
|
||||
return;
|
||||
}
|
||||
target = target.substring(1);
|
||||
|
||||
let anchor = document.getElementById("direct");
|
||||
anchor.href = target;
|
||||
|
||||
anchor = document.getElementById("new-tab");
|
||||
anchor.href = target;
|
||||
|
||||
anchor = document.getElementById("new-window");
|
||||
anchor.href = target;
|
||||
|
||||
anchor = document.getElementById("window-open");
|
||||
anchor.onclick = (e) => {
|
||||
window.open(target, "foo", "height=300,width=400");
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
anchor = document.getElementById("window-location");
|
||||
anchor.onclick = (e) => {
|
||||
window.location = target;
|
||||
e.preventDefault();
|
||||
};
|
||||
}
|
||||
|
||||
window.addEventListener("load", initialize, true);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p><a id="direct">Direct link</a></p>
|
||||
<p><a id="new-tab" target="_blank">New tab link</a></p>
|
||||
<p><a id="new-window" target="foo">New window link</a></p>
|
||||
<p><a id="window-open" href="#">window.open call</a></p>
|
||||
<p><a id="window-location" href="#">window.location manipulation</a></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Загрузка…
Ссылка в новой задаче