Skip to content

Instantly share code, notes, and snippets.

@codehag
Last active August 7, 2019 16:29
Show Gist options
  • Save codehag/5798f5ebca937699fa659e11542fdbba to your computer and use it in GitHub Desktop.
Save codehag/5798f5ebca937699fa659e11542fdbba to your computer and use it in GitHub Desktop.
// TabDescriptorActor
const TabDescriptorActor = ActorClassWithSpec(descriptorSpec, {
initialize(connection, options = {}) {
Actor.prototype.initialize.call(this, connection);
this.tabList = options.tabList;
this.destroy = this.destroy.bind(this);
},
async getTarget() {
const tabList = this.tabList;
if (!tabList) {
return {
error: "noTabs",
message: "This root actor has no browser tabs.",
};
}
let targetActor;
try {
targetActor = await tabList.getTab(options, { forceUnzombify: true });
} catch (error) {
if (error.error) {
// Pipe expected errors as-is to the client
return error;
}
return {
error: "noTab",
message: "Unexpected error while calling getTab(): " + error,
};
}
targetActor.parentID = this.actorID;
this.manage(targetActor);
return { tab: targetActor.form() };
},
});
// TabDescriptorFront
class TabDescriptorFront extends FrontClassWithSpec(descriptorSpec) {
constructor(client) {
super(client)
}
attachTab(tab) {
this._tab = tab;
this.setupListeners();
}
get tab() {
return this.tab;
}
/**
* Listen to the different events.
*/
setupListeners() {
this.tab.addEventListener("TabClose", handleEvent);
this.tab.ownerDocument.defaultView.addEventListener("unload", handleEvent);
this.tab.addEventListener("TabRemotenessChange", handleEvent);
}
/**
* Teardown event listeners.
*/
teardownListeners() {
if (this.tab.ownerDocument.defaultView) {
this.tab.ownerDocument.defaultView.removeEventListener("unload", handleEvent);
}
this.tab.removeEventListener("TabClose", handleEvent);
this.tab.removeEventListener("TabRemotenessChange", handleEvent);
}
/**
* Handle tabs events.
*/
handleEvent(event) {
switch (event.type) {
case "TabClose":
this.destroy();
break;
case "unload":
this._target.destroy();
break;
case "TabRemotenessChange":
this.onRemotenessChange();
break;
}
}
/**
* Automatically respawn the toolbox when the tab changes between being
* loaded within the parent process and loaded from a content process.
* Process change can go in both ways.
*/
async onRemotenessChange() {
// Responsive design do a crazy dance around tabs and triggers
// remotenesschange events. But we should ignore them as at the end
// the content doesn't change its remoteness.
if (tab.isResponsiveDesignMode) {
return;
}
await this._target.destroy();
this.getTarget();
this.emit("switch-target");
}
getTarget() {
if (this._target) {
return this._target;
}
if (this._targetPromise) {
return this._targetPromise;
}
this._targetPromise = (async () => {
try {
const from = await super.getTarget();
this._target = new BrowsingContextTargetFront(this.conn);
this._target.form(form);
this.manage(this._target);
this._targetPromise = null;
return this._target;
} catch (e) {
throw new Error(`Could not connect to tab target: ${e}`);
}
})();
}
destroy() {
this.tearDownListeners();
this._tab = null;
this._target = null;
this._targetPromise = null;
this.emit("destroy");
super.destroy();
}
}
// changes in forTab:
//
forTab: async function(tab) {
let target = targets.get(tab);
if (target) {
return target;
}
const promise = this.createTargetForTab(tab);
// Immediately set the target's promise in cache to prevent race
targets.set(tab, promise);
target = await promise;
// Then replace the promise with the target object
targets.set(tab, target);
target.attachTab(tab);
target.once("close", () => {
targets.delete(tab);
});
return target;
},
// changes in toolbox instantiation
function Toolbox(
targetDescriptor, // pass a top level target descriptor
selectedTool,
hostType,
contentWindow,
frameId,
msSinceProcessStart
) {
//...
this.init();
}
Toolbox.prototype = {
// ...
get target() {
return this._targetDescriptor._target;
}
init: function() {
await this._targetDescriptor.getTarget();
this.target.on(//...
// etc.
this._targetDescriptor.on("switch-target", //.. do something
this._targetDescriptor.on("destroy", this.destroy) // or something, the descriptor representing the top level target has been destroyed
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment