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

View file

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

View file

@ -51,14 +51,12 @@ function test() {
} }
} }
var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]. var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
createInstance(Ci.nsIPrincipal); .getService(Ci.nsIPrincipal);
var webNav = Cc["@mozilla.org/appshell/appShellService;1"]. var webNav = Cc["@mozilla.org/appshell/appShellService;1"]
getService(Ci.nsIAppShellService). .getService(Ci.nsIAppShellService)
createWindowlessBrowser(true); .createWindowlessBrowser(true);
var docShell = webNav. var docShell = webNav.docShell;
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDocShell);
docShell.createAboutBlankContentViewer(systemPrincipal); docShell.createAboutBlankContentViewer(systemPrincipal);
var win = docShell.contentViewer.DOMDocument.defaultView; 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); var interfaceRequestor = windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor);
ok(interfaceRequestor, "Should be able to query interface requestor interface"); 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"); ok(docShell, "Should be able to get doc shell interface");
var document = webNavigation.document; 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"]. var webNav = Cc["@mozilla.org/appshell/appShellService;1"].
getService(Ci.nsIAppShellService).createWindowlessBrowser(true); getService(Ci.nsIAppShellService).createWindowlessBrowser(true);
let docShell = webNav.QueryInterface(Ci.nsIInterfaceRequestor). let docShell = webNav.docShell;
getInterface(Ci.nsIDocShell);
docShell.createAboutBlankContentViewer( docShell.createAboutBlankContentViewer(
Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal)); Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal));
progressListener.add(docShell, function(success) { progressListener.add(docShell, function(success) {
webNav.close(); webNav.close();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -104,7 +104,7 @@ HiddenFrame.prototype = {
} }
}; };
this._webProgress.addProgressListener(this._listener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); 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.createAboutBlankContentViewer(Services.scriptSecurityManager.getSystemPrincipal());
docShell.useGlobalHistory = false; docShell.useGlobalHistory = false;
this._browser.loadURI(XUL_PAGE, 0, null, null, null); this._browser.loadURI(XUL_PAGE, 0, null, null, null);

View file

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

View file

@ -452,25 +452,10 @@ public:
mInterfaceRequestor = do_QueryInterface(aBrowser); mInterfaceRequestor = do_QueryInterface(aBrowser);
} }
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_NSIWINDOWLESSBROWSER
NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation) NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation)
NS_FORWARD_SAFE_NSIINTERFACEREQUESTOR(mInterfaceRequestor) 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: protected:
virtual ~WindowlessBrowser() virtual ~WindowlessBrowser()
{ {
@ -500,6 +485,33 @@ private:
NS_IMPL_ISUPPORTS(WindowlessBrowser, nsIWindowlessBrowser, nsIWebNavigation, nsIInterfaceRequestor) 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 NS_IMETHODIMP
nsAppShellService::CreateWindowlessBrowser(bool aIsChrome, nsIWindowlessBrowser **aResult) nsAppShellService::CreateWindowlessBrowser(bool aIsChrome, nsIWindowlessBrowser **aResult)

View file

@ -6,6 +6,8 @@
#include "nsIWebNavigation.idl" #include "nsIWebNavigation.idl"
interface nsIDocShell;
/** /**
* This interface represents a nsIWebBrowser instance with no associated OS * This interface represents a nsIWebBrowser instance with no associated OS
* window. Its main function is to manage the lifetimes of those windows. * window. Its main function is to manage the lifetimes of those windows.
@ -23,5 +25,11 @@ interface nsIWindowlessBrowser : nsIWebNavigation
* reference is released. * reference is released.
*/ */
void close(); 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"); ok(webNav, "createWindowlessBrowser should return a wevNav");
let interfaceRequestor = webNav.QueryInterface(Ci.nsIInterfaceRequestor); let docShell = webNav.docShell;
let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell);
ok(docShell, "docShell should be defined"); ok(docShell, "docShell should be defined");
ok(docShell.contentViewer.DOMDocument.defaultView, "docShell defaultView should be defined"); ok(docShell.contentViewer.DOMDocument.defaultView, "docShell defaultView should be defined");