Bug 1479570. Get Add a getter to get a docshell from nsIWindowlessBrowser. r=kmag

Differential Revision: https://phabricator.services.mozilla.com/D2669

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Boris Zbarsky 2018-08-03 00:05:07 +00:00
parent 9987b543d1
commit 357b6b1348
20 changed files with 80 additions and 81 deletions

View file

@ -48,9 +48,9 @@ function loadContentWindow(webNavigation, uri) {
async function takeScreenshot(fullWidth, fullHeight, contentWidth, contentHeight, path, url) {
try {
let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
var webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation);
let contentWindow = await loadContentWindow(webNavigation, url);
var windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
// nsIWindowlessBrowser inherits from nsIWebNavigation.
let contentWindow = await loadContentWindow(windowlessBrowser, url);
contentWindow.resizeTo(contentWidth, contentHeight);
let canvas = contentWindow.document.createElementNS("http://www.w3.org/1999/xhtml", "html:canvas");
@ -82,8 +82,8 @@ async function takeScreenshot(fullWidth, fullHeight, contentWidth, contentHeight
} catch (e) {
dump("Failure taking screenshot: " + e + "\n");
} finally {
if (webNavigation) {
webNavigation.close();
if (windowlessBrowser) {
windowlessBrowser.close();
}
}
}

View file

@ -54,8 +54,7 @@ function createFakeAddonWindow({addonId} = {}) {
const principal = Services.scriptSecurityManager
.createCodebasePrincipal(baseURI, {});
const chromeWebNav = Services.appShell.createWindowlessBrowser(true);
const docShell = chromeWebNav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
const { docShell } = chromeWebNav;
docShell.createAboutBlankContentViewer(principal);
const addonWindow = docShell.contentViewer.DOMDocument.defaultView;

View file

@ -51,14 +51,12 @@ function test() {
}
}
var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].
createInstance(Ci.nsIPrincipal);
var webNav = Cc["@mozilla.org/appshell/appShellService;1"].
getService(Ci.nsIAppShellService).
createWindowlessBrowser(true);
var docShell = webNav.
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDocShell);
var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
.getService(Ci.nsIPrincipal);
var webNav = Cc["@mozilla.org/appshell/appShellService;1"]
.getService(Ci.nsIAppShellService)
.createWindowlessBrowser(true);
var docShell = webNav.docShell;
docShell.createAboutBlankContentViewer(systemPrincipal);
var win = docShell.contentViewer.DOMDocument.defaultView;

View file

@ -33,7 +33,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=846906
var interfaceRequestor = windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor);
ok(interfaceRequestor, "Should be able to query interface requestor interface");
var docShell = interfaceRequestor.getInterface(Ci.nsIDocShell);
var docShell = windowlessBrowser.docShell;
ok(docShell, "Should be able to get doc shell interface");
var document = webNavigation.document;

View file

@ -71,10 +71,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1342989
var webNav = Cc["@mozilla.org/appshell/appShellService;1"].
getService(Ci.nsIAppShellService).createWindowlessBrowser(true);
let docShell = webNav.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDocShell);
let docShell = webNav.docShell;
docShell.createAboutBlankContentViewer(
Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal));
Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal));
progressListener.add(docShell, function(success) {
webNav.close();

View file

@ -6,10 +6,9 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
add_task(async function() {
let webNav = Services.appShell.createWindowlessBrowser(false);
let loadContext = webNav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsILoadContext);
let docShell = webNav.docShell;
let docShell = webNav.getInterface(Ci.nsIDocShell);
let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
equal(loadContext.usePrivateBrowsing, false, "Should start out in non-private mode");

View file

@ -69,8 +69,7 @@ function test()
.createCodebasePrincipal(baseURI, {});
let chromeWebNav = Services.appShell.createWindowlessBrowser(true);
let interfaceRequestor = chromeWebNav.QueryInterface(Ci.nsIInterfaceRequestor);
let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell);
let docShell = chromeWebNav.docShell;
docShell.createAboutBlankContentViewer(principal);
info("fake webextension docShell created");

View file

@ -4,8 +4,7 @@ add_task(async function test_windowlessBrowserTroubleshootCrash() {
let webNav = Services.appShell.createWindowlessBrowser(false);
let onLoaded = new Promise((resolve, reject) => {
let docShell = webNav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
let docShell = webNav.docShell;
let listener = {
observe(contentWindow, topic, data) {
let observedDocShell = contentWindow.docShell

View file

@ -18,8 +18,7 @@ add_task(async function() {
let webnav = Services.appShell.createWindowlessBrowser(false);
let docShell = webnav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
let docShell = webnav.docShell;
docShell.createAboutBlankContentViewer(principal);

View file

@ -14,8 +14,7 @@ function getWindowlessBrowser(url) {
let webnav = Services.appShell.createWindowlessBrowser(false);
let docShell = webnav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
let docShell = webnav.docShell;
docShell.createAboutBlankContentViewer(principal);

View file

@ -7,8 +7,7 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
add_task(async function() {
let webnav = Services.appShell.createWindowlessBrowser(false);
let docShell = webnav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
let docShell = webnav.docShell;
docShell.createAboutBlankContentViewer(null);

View file

@ -1077,14 +1077,12 @@ class HiddenXULWindow {
// The windowless browser is a thin wrapper around a docShell that keeps
// its related resources alive. It implements nsIWebNavigation and
// forwards its methods to the underlying docShell, but cannot act as a
// docShell itself. Calling `getInterface(nsIDocShell)` gives us the
// docShell itself. Getting .docShell gives us the
// underlying docShell, and `QueryInterface(nsIWebNavigation)` gives us
// access to the webNav methods that are already available on the
// windowless browser, but contrary to appearances, they are not the same
// object.
this.chromeShell = this._windowlessBrowser
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
this.chromeShell = this._windowlessBrowser.docShell
.QueryInterface(Ci.nsIWebNavigation);
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {

View file

@ -118,9 +118,8 @@ class ContentPage {
let system = Services.scriptSecurityManager.getSystemPrincipal();
let chromeShell = this.windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIWebNavigation);
let chromeShell = this.windowlessBrowser.docShell
.QueryInterface(Ci.nsIWebNavigation);
chromeShell.createAboutBlankContentViewer(system);
chromeShell.useGlobalHistory = false;

View file

@ -708,9 +708,7 @@ this.downloads = class extends ExtensionAPI {
return new Promise((resolve, reject) => {
let chromeWebNav = Services.appShell.createWindowlessBrowser(true);
chromeWebNav
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
chromeWebNav.docShell
.createAboutBlankContentViewer(Services.scriptSecurityManager.getSystemPrincipal());
let img = chromeWebNav.document.createElement("img");

View file

@ -808,8 +808,7 @@ function loadImage(img, data) {
add_task(async function test_getFileIcon() {
let webNav = Services.appShell.createWindowlessBrowser(false);
let docShell = webNav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
let docShell = webNav.docShell;
let system = Services.scriptSecurityManager.getSystemPrincipal();
docShell.createAboutBlankContentViewer(system);

View file

@ -104,7 +104,7 @@ HiddenFrame.prototype = {
}
};
this._webProgress.addProgressListener(this._listener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
let docShell = this._browser.getInterface(Ci.nsIDocShell);
let docShell = this._browser.docShell;
docShell.createAboutBlankContentViewer(Services.scriptSecurityManager.getSystemPrincipal());
docShell.useGlobalHistory = false;
this._browser.loadURI(XUL_PAGE, 0, null, null, null);

View file

@ -18,11 +18,10 @@ registerCleanupFunction(() => { server.stop(() => {})});
// before they are called.
const progressListeners = new Map();
function loadContentWindow(webNavigation, uri) {
function loadContentWindow(windowlessBrowser, uri) {
return new Promise((resolve, reject) => {
webNavigation.loadURI(uri, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
let docShell = webNavigation.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
windowlessBrowser.loadURI(uri, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
let docShell = windowlessBrowser.docShell;
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
let progressListener = {
@ -35,8 +34,6 @@ function loadContentWindow(webNavigation, uri) {
if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
return;
}
let docShell = webNavigation.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
let contentWindow = docShell.domWindow;
webProgress.removeProgressListener(progressListener);
progressListeners.delete(progressListener);
@ -55,8 +52,7 @@ function loadContentWindow(webNavigation, uri) {
add_task(async function test_snapshot() {
let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
let webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation);
let contentWindow = await loadContentWindow(webNavigation, HEADLESS_URL);
let contentWindow = await loadContentWindow(windowlessBrowser, HEADLESS_URL);
const contentWidth = 400;
const contentHeight = 300;
// Verify dimensions.
@ -93,13 +89,13 @@ add_task(async function test_snapshot() {
}
ok(found, "Found blue text on page.");
webNavigation.close();
windowlessBrowser.close();
});
add_task(async function test_snapshot_widget_layers() {
let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
let webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation);
let contentWindow = await loadContentWindow(webNavigation, HEADLESS_URL);
// nsIWindowlessBrowser inherits from nsIWebNavigation.
let contentWindow = await loadContentWindow(windowlessBrowser, HEADLESS_URL);
const contentWidth = 1;
const contentHeight = 2;
// Verify dimensions.
@ -125,14 +121,14 @@ add_task(async function test_snapshot_widget_layers() {
);
ok(true, "Snapshot with widget layers didn't crash.");
webNavigation.close();
windowlessBrowser.close();
});
// Ensure keydown events are triggered on the windowless browser.
add_task(async function test_keydown() {
let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
let webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation);
let contentWindow = await loadContentWindow(webNavigation, HEADLESS_URL);
// nsIWindowlessBrowser inherits from nsIWebNavigation.
let contentWindow = await loadContentWindow(windowlessBrowser, HEADLESS_URL);
let keydown = new Promise((resolve) => {
contentWindow.addEventListener("keydown", () => {
@ -149,15 +145,15 @@ add_task(async function test_keydown() {
await keydown;
ok(true, "Send keydown didn't crash");
webNavigation.close();
windowlessBrowser.close();
});
// Test dragging the mouse on a button to ensure the creation of the drag
// service doesn't crash in headless.
add_task(async function test_mouse_drag() {
let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
let webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation);
let contentWindow = await loadContentWindow(webNavigation, HEADLESS_BUTTON_URL);
// nsIWindowlessBrowser inherits from nsIWebNavigation.
let contentWindow = await loadContentWindow(windowlessBrowser, HEADLESS_BUTTON_URL);
contentWindow.resizeTo(400, 400);
let target = contentWindow.document.getElementById('btn');
@ -175,5 +171,5 @@ add_task(async function test_mouse_drag() {
ok(true, "Send mouse event didn't crash");
webNavigation.close();
windowlessBrowser.close();
});

View file

@ -452,25 +452,10 @@ public:
mInterfaceRequestor = do_QueryInterface(aBrowser);
}
NS_DECL_ISUPPORTS
NS_DECL_NSIWINDOWLESSBROWSER
NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation)
NS_FORWARD_SAFE_NSIINTERFACEREQUESTOR(mInterfaceRequestor)
NS_IMETHOD
Close() override
{
NS_ENSURE_TRUE(!mClosed, NS_ERROR_UNEXPECTED);
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"WindowlessBrowser::Close called when not safe to run scripts");
mClosed = true;
mWebNavigation = nullptr;
mInterfaceRequestor = nullptr;
nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
return window->Destroy();
}
protected:
virtual ~WindowlessBrowser()
{
@ -500,6 +485,33 @@ private:
NS_IMPL_ISUPPORTS(WindowlessBrowser, nsIWindowlessBrowser, nsIWebNavigation, nsIInterfaceRequestor)
NS_IMETHODIMP
WindowlessBrowser::Close()
{
NS_ENSURE_TRUE(!mClosed, NS_ERROR_UNEXPECTED);
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"WindowlessBrowser::Close called when not safe to run scripts");
mClosed = true;
mWebNavigation = nullptr;
mInterfaceRequestor = nullptr;
nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
return window->Destroy();
}
NS_IMETHODIMP
WindowlessBrowser::GetDocShell(nsIDocShell** aDocShell)
{
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mInterfaceRequestor);
if (!docShell) {
return NS_ERROR_NOT_INITIALIZED;
}
docShell.forget(aDocShell);
return NS_OK;
}
NS_IMETHODIMP
nsAppShellService::CreateWindowlessBrowser(bool aIsChrome, nsIWindowlessBrowser **aResult)

View file

@ -6,6 +6,8 @@
#include "nsIWebNavigation.idl"
interface nsIDocShell;
/**
* This interface represents a nsIWebBrowser instance with no associated OS
* window. Its main function is to manage the lifetimes of those windows.
@ -23,5 +25,11 @@ interface nsIWindowlessBrowser : nsIWebNavigation
* reference is released.
*/
void close();
/**
* Get the docshell for this browser. This is the docshell that gets
* navigated when the browser's nsIWebNavigation interface is used.
*/
readonly attribute nsIDocShell docShell;
};

View file

@ -31,8 +31,7 @@ function testWindowlessBrowser(chromePrivileged) {
ok(webNav, "createWindowlessBrowser should return a wevNav");
let interfaceRequestor = webNav.QueryInterface(Ci.nsIInterfaceRequestor);
let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell);
let docShell = webNav.docShell;
ok(docShell, "docShell should be defined");
ok(docShell.contentViewer.DOMDocument.defaultView, "docShell defaultView should be defined");