author | yulia <ystartsev@mozilla.com> |
Tue, 27 Aug 2019 09:07:46 +0000 | |
changeset 553774 | 9a85c28fd033545e63ac5851f71c4d34a9436d28 |
parent 553773 | 5c69342869b413c4249304f8fd016603652fc809 |
child 553775 | 2187d722dc389e70f1f0f93ac0195f31e3fab8c8 |
push id | 2165 |
push user | ffxbld-merge |
push date | Mon, 14 Oct 2019 16:30:58 +0000 |
treeherder | mozilla-release@0eae18af659f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | ochameau |
bugs | 1574159 |
milestone | 70.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/devtools/server/actors/descriptors/frame.js +++ b/devtools/server/actors/descriptors/frame.js @@ -67,20 +67,24 @@ const FrameDescriptorActor = ActorClassW } else { form = await this._connectBrowsingContext(); } return form; }, form() { + const url = this._browsingContext.currentWindowGlobal + ? this._browsingContext.currentWindowGlobal.documentURI.displaySpec + : null; return { actor: this.actorID, id: this.id, - parentId: this._browsingContext.parent + url, + parentID: this._browsingContext.parent ? this._browsingContext.parent.id : null, }; }, destroy() { this._browsingContext = null; this._embedderElement = null;
--- a/devtools/server/actors/targets/browsing-context.js +++ b/devtools/server/actors/targets/browsing-context.js @@ -40,16 +40,22 @@ const L10N = new LocalizationHelper(STRI const { ActorClassWithSpec, Actor, Pool } = require("devtools/shared/protocol"); const { LazyPool, createExtraActors, } = require("devtools/shared/protocol/lazy-pool"); const { browsingContextTargetSpec, } = require("devtools/shared/specs/targets/browsing-context"); +loader.lazyRequireGetter( + this, + "FrameDescriptorActor", + "devtools/server/actors/descriptors/frame", + true +); loader.lazyRequireGetter( this, "ThreadActor", "devtools/server/actors/thread", true ); loader.lazyRequireGetter( @@ -279,16 +285,17 @@ const browsingContextTargetPrototype = { // Supports the logInPage request. logInPage: true, // Supports requests related to rewinding. canRewind, }; this._workerTargetActorList = null; this._workerTargetActorPool = null; + this._frameDescriptorActorPool = null; this._onWorkerTargetActorListChanged = this._onWorkerTargetActorListChanged.bind( this ); }, traits: null, // Optional console API listener options (e.g. used by the WebExtensionActor to @@ -352,16 +359,20 @@ const browsingContextTargetPrototype = { */ get docShell() { throw new Error( "`docShell` getter should be overridden by a subclass of " + "`BrowsingContextTargetActor`" ); }, + get childBrowsingContexts() { + return this.docShell.browsingContext.getChildren(); + }, + /** * Getter for the list of all `docShell`s in the browsing context. * @return {Array} */ get docShells() { return getChildDocShells(this.docShell); }, @@ -659,16 +670,59 @@ const browsingContextTargetPrototype = { return {}; }, listFrames(request) { const windows = this._docShellsToWindows(this.docShells); return { frames: windows }; }, + listRemoteFrames() { + const frames = []; + const contextsToWalk = this.childBrowsingContexts; + + if (contextsToWalk == 0) { + return { frames }; + } + + const pool = new Pool(this.conn); + while (contextsToWalk.length) { + const currentContext = contextsToWalk.pop(); + let frameDescriptor = this._getKnownFrameDescriptor(currentContext.id); + if (!frameDescriptor) { + frameDescriptor = new FrameDescriptorActor(this.conn, currentContext); + } + pool.manage(frameDescriptor); + frames.push(frameDescriptor); + contextsToWalk.push(...currentContext.getChildren()); + } + // Do not destroy the pool before transfering ownership to the newly created + // pool, so that we do not accidently destroy actors that are still in use. + if (this._frameDescriptorActorPool) { + this._frameDescriptorActorPool.destroy(); + } + + this._frameDescriptorActorPool = pool; + + return { frames }; + }, + + _getKnownFrameDescriptor(id) { + // if there is no pool, then we do not have any descriptors + if (!this._frameDescriptorActorPool) { + return null; + } + for (const descriptor of this._frameDescriptorActorPool.poolChildren()) { + if (descriptor.id === id) { + return descriptor; + } + } + return null; + }, + ensureWorkerTargetActorList() { if (this._workerTargetActorList === null) { this._workerTargetActorList = new WorkerTargetActorList(this.conn, { type: Ci.nsIWorkerDebugger.TYPE_DEDICATED, window: this.window, }); } return this._workerTargetActorList; @@ -956,16 +1010,21 @@ const browsingContextTargetPrototype = { this._workerTargetActorList = null; } if (this._workerTargetActorPool !== null) { this._workerTargetActorPool.destroy(); this._workerTargetActorPool = null; } + if (this._frameDescriptorActorPool !== null) { + this._frameDescriptorActorPool.destroy(); + this._frameDescriptorActorPool = null; + } + this._attached = false; this.emit("tabDetached"); return true; }, // Protocol Request Handlers
--- a/devtools/server/actors/targets/parent-process.js +++ b/devtools/server/actors/targets/parent-process.js @@ -97,16 +97,35 @@ Object.defineProperty(parentProcessTarge for (const { docShell } of Services.ww.getWindowEnumerator()) { docShells = docShells.concat(getChildDocShells(docShell)); } return docShells; }, }); +/** + * Getter for the list of all browsingContexts in the parent process. + * We use specialized code in order to retrieve <browser>'s browsing context for + * each browser's tab. BrowsingContext.getChildren method doesn't return the + * tab's BrowsingContext because they are of "content" type, while the root + * BrowsingContext of the parent process target is of "chrome" type. + * + * @return {Array} + */ +Object.defineProperty(parentProcessTargetPrototype, "childBrowsingContexts", { + get: function() { + // Iterate over all `browser` elements that are remote, and return their + // browsing context. + return [ + ...this.window.document.querySelectorAll(`browser[remote="true"]`), + ].map(browser => browser.browsingContext); + }, +}); + parentProcessTargetPrototype.observe = function(subject, topic, data) { BrowsingContextTargetActor.prototype.observe.call(this, subject, topic, data); if (!this.attached) { return; } subject.QueryInterface(Ci.nsIDocShell);
--- a/devtools/server/tests/browser/browser.ini +++ b/devtools/server/tests/browser/browser.ini @@ -14,16 +14,17 @@ support-files = doc_accessibility_infobar.html doc_accessibility_text_label_audit_frame.html doc_accessibility_text_label_audit.html doc_accessibility.html doc_allocations.html doc_force_cc.html doc_force_gc.html doc_innerHTML.html + doc_iframe.html doc_perf.html doc_promise-get-allocation-stack.html doc_promise-get-fulfillment-stack.html doc_promise-get-rejection-stack.html error-actor.js grid.html inspectedwindow-reload-target.sjs inspector-search-data.html @@ -148,13 +149,14 @@ fail-if = fission fail-if = fission [browser_storage_updates.js] fail-if = fission [browser_storage_webext_storage_local.js] [browser_styles_getRuleText.js] [browser_stylesheets_getTextEmpty.js] [browser_stylesheets_nested-iframes.js] [browser_register_actor.js] +[browser_resource_list-remote-frames.js] [browser_webextension_inspected_window.js] [browser_dbg_promises-allocation-stack.js] [browser_dbg_promises-chrome-allocation-stack.js] [browser_dbg_promises-fulfillment-stack.js] [browser_dbg_promises-rejection-stack.js]
new file mode 100644 --- /dev/null +++ b/devtools/server/tests/browser/browser_resource_list-remote-frames.js @@ -0,0 +1,98 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that we can get a stack to a promise's allocation point in the chrome + * process. + */ + +"use strict"; + +add_task(async function() { + await pushPref("fission.autostart", true); + const tabTarget = await addTabTarget(MAIN_DOMAIN + "doc_iframe.html"); + await testLocalListFrames(tabTarget); + await testBrowserListFrames(tabTarget); +}); + +async function testLocalListFrames(tabTarget) { + // at this point, tabTarget is the tab with two iframes, one nested inside + // the other. + const { frames } = await tabTarget.listRemoteFrames(); + is(frames.length, 2, "Got two frames"); + + // Since we do not have access to remote frames yet this will return null. + // This test should be updated when we have access to remote frames. + for (const frame of frames) { + const frameTarget = await frame.getTarget(); + is(frameTarget, null, "We cannot get remote iframe fronts yet"); + } + + // However we can confirm that the newly created iframe is there. + const browser = gBrowser.selectedBrowser; + const oopID = await ContentTask.spawn(browser, {}, async () => { + const oop = content.document.querySelector("iframe"); + return oop.frameLoader.browsingContext.id; + }); + ok( + frames.find(f => f.id === oopID), + "tabTarget.listRemoteFrames returns the oop frame descriptor" + ); +} +async function testBrowserListFrames(tabTarget) { + // Now, we can test against the entire browser. getMainProcess will return + // a target for the parentProcess, and will be able to enumerate over all + // the tabs, the remote iframe, and the pair of frames, one nested inside the other. + const target = await tabTarget.client.mainRoot.getMainProcess(); + await getFrames(target, tabTarget); +} + +async function getFrames(target) { + const { frames } = await target.listRemoteFrames(); + + // Connect to the tab which is being debugged, this is equivilant to the + // browsing context of the tabTarget. The difference between tabTarget's browsing + // context, and this browsing context, is we have less information in tabTarget, + // since it is requesting information from a content process. From the parent + // we can access information like the URL. + const descriptor = frames.find(f => f.url && f.url.includes("doc_iframe")); + + const front = await descriptor.getTarget(); + ok(front.hasActor("console"), "Got the console actor"); + ok(front.hasActor("thread"), "Got the thread actor"); + // Ensure sending at least one request to an actor... + const consoleFront = await front.getFront("console"); + const { result } = await consoleFront.evaluateJS("var a = 42; a"); + is(result, 42, "console.eval worked"); + + // Although we can get metadata about the child frames, + // since we do not have access to remote frames yet, this will return null. + // This test should be updated when we have access to remote frames. + const childFrames = frames.filter(d => d.parentID === descriptor.id); + for (const frame of childFrames) { + const frameTarget = await frame.getTarget(); + is(frameTarget, null, "We cannot get remote iframe fronts yet"); + } + + getFirstFrameAgain(front, descriptor, target); +} + +// Assert that calling descriptor.getTarget returns the same actor. +async function getFirstFrameAgain(firstTargetFront, descriptor, target) { + const targetFront = await descriptor.getTarget(); + + is( + targetFront, + firstTargetFront, + "Second call to getTarget with the same id returns the same form" + ); + + const { frames } = await target.listRemoteFrames(); + const secondDescriptor = frames.find(f => f.id === descriptor.id); + const secondTargetFront = await secondDescriptor.getTarget(); + is( + secondTargetFront, + firstTargetFront, + "Second call to listFrames with the same id returns the same form" + ); +}
new file mode 100644 --- /dev/null +++ b/devtools/server/tests/browser/doc_iframe.html @@ -0,0 +1,15 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!DOCTYPE html> + +<html> + <head> + <meta charset="utf-8"/> + <title>iframe test page</title> + </head> + + <body> + <iframe id="better-not-ask" src="data:text/html,<iframe src='data:text/html,foo'></iframe>"></iframe> + </body> + +</html>
--- a/devtools/shared/fronts/descriptors/frame.js +++ b/devtools/shared/fronts/descriptors/frame.js @@ -24,16 +24,18 @@ class FrameDescriptorFront extends Front super(client); this._frameDescriptorFront = null; this._targetFrontPromise = null; this._client = client; } form(json) { this.id = json.id; + this.url = json.url; + this.parentID = json.parentID; } async _createFrameTarget(form) { let front = null; front = new BrowsingContextTargetFront(this._client); front.actorID = form.actor; front.form(form); this.manage(front);
--- a/devtools/shared/specs/targets/browsing-context.js +++ b/devtools/shared/specs/targets/browsing-context.js @@ -27,16 +27,20 @@ types.addDictType("browsingContextTarget error: "nullable:string", message: "nullable:string", }); types.addDictType("browsingContextTarget.listframes", { frames: "array:browsingContextTarget.window", }); +types.addDictType("browsingContextTarget.listRemoteFrames", { + frames: "array:frameDescriptor", +}); + types.addDictType("browsingContextTarget.window", { id: "string", parentID: "nullable:string", url: "nullable:string", // should be present if not destroying title: "nullable:string", // should be present if not destroying destroy: "nullable:boolean", // not present if not destroying }); @@ -99,16 +103,20 @@ const browsingContextTargetSpecPrototype windowId: Option(0, "string"), }, response: RetVal("browsingContextTarget.switchtoframe"), }, listFrames: { request: {}, response: RetVal("browsingContextTarget.listframes"), }, + listRemoteFrames: { + request: {}, + response: RetVal("browsingContextTarget.listRemoteFrames"), + }, listWorkers: { request: {}, response: RetVal("browsingContextTarget.workers"), }, logInPage: { request: { text: Option(0, "string"), category: Option(0, "string"),