Skip to content

Commit

Permalink
Fix linux
Browse files Browse the repository at this point in the history
  • Loading branch information
yury-s committed Apr 2, 2020
1 parent 544673b commit 97a2ccd
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 73 deletions.
120 changes: 100 additions & 20 deletions juggler/TargetRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {PageHandler} = ChromeUtils.import("chrome://juggler/content/protocol/Page
const {NetworkHandler} = ChromeUtils.import("chrome://juggler/content/protocol/NetworkHandler.js");
const {RuntimeHandler} = ChromeUtils.import("chrome://juggler/content/protocol/RuntimeHandler.js");
const {AccessibilityHandler} = ChromeUtils.import("chrome://juggler/content/protocol/AccessibilityHandler.js");
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");

const Cc = Components.classes;
const Ci = Components.interfaces;
Expand All @@ -25,7 +26,7 @@ const ALL_PERMISSIONS = [
];

class TargetRegistry {
constructor(mainWindow) {
constructor() {
EventEmitter.decorate(this);

this._browserContextIdToBrowserContext = new Map();
Expand All @@ -41,22 +42,20 @@ class TargetRegistry {

this._defaultContext = new BrowserContext(this, undefined, undefined);

this._mainWindow = mainWindow;
this._targets = new Map();

this._tabToTarget = new Map();
Services.obs.addObserver(this, 'oop-frameloader-crashed');

for (const tab of this._mainWindow.gBrowser.tabs)
this._createTargetForTab(tab);
this._mainWindow.gBrowser.tabContainer.addEventListener('TabOpen', event => {
const onTabOpenListener = event => {
const target = this._createTargetForTab(event.target);
// If we come here, content will have juggler script from the start,
// and we should wait for initial navigation.
target._waitForInitialNavigation = true;
// For pages created before we attach to them, we don't wait for initial
// navigation (target._waitForInitialNavigation is false by default).
});
this._mainWindow.gBrowser.tabContainer.addEventListener('TabClose', event => {
};

const onTabCloseListener = event => {
const tab = event.target;
const target = this._tabToTarget.get(tab);
if (!target)
Expand All @@ -65,8 +64,32 @@ class TargetRegistry {
this._tabToTarget.delete(tab);
target.dispose();
this.emit(TargetRegistry.Events.TargetDestroyed, target);
});
Services.obs.addObserver(this, 'oop-frameloader-crashed');
};

const wmListener = {
onOpenWindow: async window => {
const domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
if (!(domWindow instanceof Ci.nsIDOMChromeWindow))
return;
await this._waitForWindowLoad(domWindow);
for (const tab of domWindow.gBrowser.tabs)
this._createTargetForTab(tab);
domWindow.gBrowser.tabContainer.addEventListener('TabOpen', onTabOpenListener);
domWindow.gBrowser.tabContainer.addEventListener('TabClose', onTabCloseListener);
},
onCloseWindow: window => {
const domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
if (!(domWindow instanceof Ci.nsIDOMChromeWindow))
return;
if (!domWindow.gBrowser)
return;
domWindow.gBrowser.tabContainer.removeEventListener('TabOpen', onTabOpenListener);
domWindow.gBrowser.tabContainer.removeEventListener('TabClose', onTabCloseListener);
for (const tab of domWindow.gBrowser.tabs)
onTabCloseListener({ target: tab });
},
};
Services.wm.addListener(wmListener);
}

defaultContext() {
Expand All @@ -81,13 +104,42 @@ class TargetRegistry {
return this._browserContextIdToBrowserContext.get(browserContextId);
}

async _waitForWindowLoad(window) {
if (window.document.readyState === 'complete')
return;
await new Promise(fulfill => {
window.addEventListener('load', function listener() {
window.removeEventListener('load', listener);
fulfill();
});
});
}

async newPage({browserContextId}) {
let window;
let created = false;
const windowsIt = Services.wm.getEnumerator('navigator:browser');
if (windowsIt.hasMoreElements()) {
window = windowsIt.getNext();
} else {
const features = "chrome,dialog=no,all";
const args = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
args.data = 'about:blank';
window = Services.ww.openWindow(null, AppConstants.BROWSER_CHROME_URL, '_blank', features, args);
created = true;
}
await this._waitForWindowLoad(window);
const browserContext = this.browserContextForId(browserContextId);
const tab = this._mainWindow.gBrowser.addTab('about:blank', {
const tab = window.gBrowser.addTab('about:blank', {
userContextId: browserContext.userContextId,
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});
this._mainWindow.gBrowser.selectedTab = tab;
if (created) {
window.gBrowser.removeTab(window.gBrowser.getTabForBrowser(window.gBrowser.getBrowserAtIndex(0)), {
skipPermitUnload: true,
});
}
window.gBrowser.selectedTab = tab;
const target = this._tabToTarget.get(tab);
await target._contentReadyPromise;
if (browserContext.options.timezoneId) {
Expand Down Expand Up @@ -128,14 +180,25 @@ class TargetRegistry {
return this._targets.get(targetId);
}

_tabForBrowser(browser) {
// TODO: replace all of this with browser -> target map.
const windowsIt = Services.wm.getEnumerator('navigator:browser');
while (windowsIt.hasMoreElements()) {
const window = windowsIt.getNext();
const tab = window.gBrowser.getTabForBrowser(browser);
if (tab)
return { tab, gBrowser: window.gBrowser };
}
}

_targetForBrowser(browser) {
const tab = this._mainWindow.gBrowser.getTabForBrowser(browser);
return tab ? this._tabToTarget.get(tab) : undefined;
const tab = this._tabForBrowser(browser);
return tab ? this._tabToTarget.get(tab.tab) : undefined;
}

browserContextForBrowser(browser) {
const tab = this._mainWindow.gBrowser.getTabForBrowser(browser);
return tab ? this._userContextIdToBrowserContext.get(tab.userContextId) : undefined;
const tab = this._tabForBrowser(browser);
return tab ? this._userContextIdToBrowserContext.get(tab.tab.userContextId) : undefined;
}

_createTargetForTab(tab) {
Expand All @@ -158,6 +221,10 @@ class TargetRegistry {
if (!target)
return;
target.emit('crashed');
this._targets.delete(target.id());
this._tabToTarget.delete(target._tab);
target.dispose();
this.emit(TargetRegistry.Events.TargetDestroyed, target);
return;
}
}
Expand All @@ -182,7 +249,7 @@ class PageTarget {
this._eventListeners = [
helper.addProgressListener(tab.linkedBrowser, navigationListener, Ci.nsIWebProgress.NOTIFY_LOCATION),
helper.addMessageListener(tab.linkedBrowser.messageManager, 'juggler:content-ready', {
receiveMessage: () => this._onContentReady()
receiveMessage: message => this._onContentReady(message.data)
}),
];

Expand Down Expand Up @@ -228,7 +295,8 @@ class PageTarget {
}

async close(runBeforeUnload = false) {
await this._registry._mainWindow.gBrowser.removeTab(this._tab, {
const tab = this._registry._tabForBrowser(this._tab.linkedBrowser);
await tab.gBrowser.removeTab(this._tab, {
skipPermitUnload: !runBeforeUnload,
});
}
Expand All @@ -244,7 +312,9 @@ class PageTarget {
networkHandler.enable();
}

_onContentReady() {
_onContentReady({ userContextId }) {
// TODO: this is the earliest when userContextId is available.
// We should create target here, while listening to onContentReady for every tab.
const sessions = [];
const data = { sessions, target: this };
this._registry.emit(TargetRegistry.Events.PageTargetReady, data);
Expand Down Expand Up @@ -340,10 +410,20 @@ class BrowserContext {
}
}

destroy() {
async destroy() {
if (this.userContextId !== 0) {
ContextualIdentityService.remove(this.userContextId);
ContextualIdentityService.closeContainerTabs(this.userContextId);
if (this.pages.size) {
await new Promise(f => {
const listener = helper.on(this._registry, TargetRegistry.Events.TargetDestroyed, () => {
if (!this.pages.size) {
helper.removeListeners([listener]);
f();
}
});
});
}
}
this._registry._browserContextIdToBrowserContext.delete(this.browserContextId);
this._registry._userContextIdToBrowserContext.delete(this.userContextId);
Expand Down
68 changes: 18 additions & 50 deletions juggler/components/juggler.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const FRAME_SCRIPT = "chrome://juggler/content/content/main.js";

// Command Line Handler
function CommandLineHandler() {
this._port = -1;
};

CommandLineHandler.prototype = {
Expand All @@ -31,31 +30,39 @@ CommandLineHandler.prototype = {
const jugglerFlag = cmdLine.handleFlagWithParam("juggler", false);
if (!jugglerFlag || isNaN(jugglerFlag))
return;
this._port = parseInt(jugglerFlag, 10);
Services.obs.addObserver(this, 'sessionstore-windows-restored');
},

observe: async function(subject, topic) {
Services.obs.removeObserver(this, 'sessionstore-windows-restored');
const port = parseInt(jugglerFlag, 10);
const silent = cmdLine.preventDefault;
if (silent)
Services.startup.enterLastWindowClosingSurvivalArea();

const win = await waitForBrowserWindow();
const targetRegistry = new TargetRegistry(win);
const targetRegistry = new TargetRegistry();
new NetworkObserver(targetRegistry);

const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
const WebSocketServer = require('devtools/server/socket/websocket-server');
this._server = Cc["@mozilla.org/network/server-socket;1"].createInstance(Ci.nsIServerSocket);
this._server.initSpecialConnection(this._port, Ci.nsIServerSocket.KeepWhenOffline | Ci.nsIServerSocket.LoopbackOnly, 4);
this._server.initSpecialConnection(port, Ci.nsIServerSocket.KeepWhenOffline | Ci.nsIServerSocket.LoopbackOnly, 4);

const token = helper.generateId();

let windowsRestoredCallback;
const windowsRestored = new Promise(fulfill => windowsRestoredCallback = fulfill);
const removeObserver = helper.addObserver(() => {
windowsRestoredCallback();
removeObserver();
}, "sessionstore-windows-restored");

this._server.asyncListen({
onSocketAccepted: async(socket, transport) => {
await windowsRestored;
const input = transport.openInputStream(0, 0, 0);
const output = transport.openOutputStream(0, 0, 0);
const webSocket = await WebSocketServer.accept(transport, input, output, "/" + token);
const dispatcher = new Dispatcher(webSocket);
const browserHandler = new BrowserHandler(dispatcher.rootSession(), dispatcher, targetRegistry);
const browserHandler = new BrowserHandler(dispatcher.rootSession(), dispatcher, targetRegistry, () => {
if (silent)
Services.startup.exitLastWindowClosingSurvivalArea();
});
dispatcher.rootSession().registerHandler('Browser', browserHandler);
}
});
Expand All @@ -76,42 +83,3 @@ CommandLineHandler.prototype = {
};

var NSGetFactory = XPCOMUtils.generateNSGetFactory([CommandLineHandler]);

/**
* @return {!Promise<Ci.nsIDOMChromeWindow>}
*/
async function waitForBrowserWindow() {
const windowsIt = Services.wm.getEnumerator('navigator:browser');
if (windowsIt.hasMoreElements())
return waitForWindowLoaded(windowsIt.getNext());

let fulfill;
let promise = new Promise(x => fulfill = x);

const listener = {
onOpenWindow: window => {
if (window instanceof Ci.nsIDOMChromeWindow) {
Services.wm.removeListener(listener);
fulfill(waitForWindowLoaded(window));
}
},
onCloseWindow: () => {}
};
Services.wm.addListener(listener);
return promise;

/**
* @param {!Ci.nsIDOMChromeWindow} window
* @return {!Promise<Ci.nsIDOMChromeWindow>}
*/
function waitForWindowLoaded(window) {
if (window.document.readyState === 'complete')
return window;
return new Promise(fulfill => {
window.addEventListener('load', function listener() {
window.removeEventListener('load', listener);
fulfill(window);
});
});
}
}
5 changes: 4 additions & 1 deletion juggler/content/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ function setOnlineOverrideInDocShell(override) {
}

function initialize() {
let response = sendSyncMessage('juggler:content-ready', {})[0];
const loadContext = docShell.QueryInterface(Ci.nsILoadContext);
const userContextId = loadContext.originAttributes.userContextId;

let response = sendSyncMessage('juggler:content-ready', { userContextId })[0];
if (!response)
response = { sessionIds: [], browserContextOptions: {}, waitForInitialNavigation: false };

Expand Down
6 changes: 4 additions & 2 deletions juggler/protocol/BrowserHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
const helper = new Helper();

class BrowserHandler {
constructor(session, dispatcher, targetRegistry) {
constructor(session, dispatcher, targetRegistry, onclose) {
this._session = session;
this._dispatcher = dispatcher;
this._targetRegistry = targetRegistry;
Expand All @@ -16,6 +16,7 @@ class BrowserHandler {
this._eventListeners = [];
this._createdBrowserContextIds = new Set();
this._attachedSessions = new Map();
this._onclose = onclose;
}

async enable({attachToDefaultContext}) {
Expand Down Expand Up @@ -53,8 +54,8 @@ class BrowserHandler {
async removeBrowserContext({browserContextId}) {
if (!this._enabled)
throw new Error('Browser domain is not enabled');
await this._targetRegistry.browserContextForId(browserContextId).destroy();
this._createdBrowserContextIds.delete(browserContextId);
this._targetRegistry.browserContextForId(browserContextId).destroy();
}

dispose() {
Expand Down Expand Up @@ -110,6 +111,7 @@ class BrowserHandler {
}

async close() {
this._onclose();
let browserWindow = Services.wm.getMostRecentWindow(
"navigator:browser"
);
Expand Down

0 comments on commit 97a2ccd

Please sign in to comment.