browser(firefox): use browsingContextID for frame IDs (#3999)
BrowsingContextIDs are consistent across the processes, so we can use them to target frames in both browser and content processes. This will aid browser-side navigation. As a nice side-effect, we can drop a round-trip to the content process for every `requestWillBeSent` event since we *almost* always can attribute all network events to the proper parent frames. I say "almost", because we in fact **fail** to correctly attribute requests from workers that are instantiated by subframes. This, however, is not working in Chromium ATM, so I consider this to be a minor regression that is worth the simplification.
This commit is contained in:
parent
b3497b333e
commit
2631e1a809
|
|
@ -1,2 +1,2 @@
|
|||
1173
|
||||
Changed: yurys@chromium.org Thu Sep 10 12:35:25 PDT 2020
|
||||
1174
|
||||
Changed: lushnikov@chromium.org Tue Sep 29 02:02:37 MDT 2020
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerat
|
|||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
class Helper {
|
||||
|
||||
addObserver(handler, topic) {
|
||||
Services.obs.addObserver(handler, topic);
|
||||
return () => Services.obs.removeObserver(handler, topic);
|
||||
|
|
@ -112,6 +113,12 @@ class Helper {
|
|||
}
|
||||
return '<unknown error>';
|
||||
}
|
||||
|
||||
browsingContextToFrameId(browsingContext) {
|
||||
if (!browsingContext)
|
||||
return undefined;
|
||||
return 'frame-' + browsingContext.id;
|
||||
}
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = [ "Helper" ];
|
||||
|
|
|
|||
|
|
@ -489,10 +489,24 @@ class NetworkRequest {
|
|||
const pageNetwork = this._activePageNetwork();
|
||||
if (!pageNetwork)
|
||||
return;
|
||||
const causeType = this.httpChannel.loadInfo ? this.httpChannel.loadInfo.externalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER;
|
||||
const internalCauseType = this.httpChannel.loadInfo ? this.httpChannel.loadInfo.internalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER;
|
||||
const loadInfo = this.httpChannel.loadInfo;
|
||||
const causeType = loadInfo?.externalContentPolicyType || Ci.nsIContentPolicy.TYPE_OTHER;
|
||||
const internalCauseType = loadInfo?.internalContentPolicyType || Ci.nsIContentPolicy.TYPE_OTHER;
|
||||
|
||||
let browsingContext = loadInfo?.frameBrowsingContext || loadInfo?.browsingContext;
|
||||
// TODO: Unfortunately, requests from web workers don't have frameBrowsingContext or
|
||||
// browsingContext.
|
||||
//
|
||||
// We fail to attribute them to the original frames on the browser side, but we
|
||||
// can use load context top frame to attribute them to the top frame at least.
|
||||
if (!browsingContext) {
|
||||
const loadContext = helper.getLoadContext(this.httpChannel);
|
||||
browsingContext = loadContext?.topFrameElement?.browsingContext;
|
||||
}
|
||||
|
||||
pageNetwork.emit(PageNetwork.Events.Request, {
|
||||
url: this.httpChannel.URI.spec,
|
||||
frameId: helper.browsingContextToFrameId(browsingContext),
|
||||
isIntercepted,
|
||||
requestId: this.requestId,
|
||||
redirectedFrom: this.redirectedFromId,
|
||||
|
|
|
|||
|
|
@ -318,7 +318,7 @@ class Frame {
|
|||
this._runtime = runtime;
|
||||
this._docShell = docShell;
|
||||
this._children = new Set();
|
||||
this._frameId = helper.generateId();
|
||||
this._frameId = helper.browsingContextToFrameId(this._docShell.browsingContext);
|
||||
this._parentFrame = null;
|
||||
this._url = '';
|
||||
if (docShell.domWindow && docShell.domWindow.location)
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
|
||||
const helper = new Helper();
|
||||
|
||||
class NetworkMonitor {
|
||||
constructor(rootDocShell, frameTree) {
|
||||
this._frameTree = frameTree;
|
||||
this._requestDetails = new Map();
|
||||
|
||||
this._eventListeners = [
|
||||
helper.addObserver(this._onRequest.bind(this), 'http-on-opening-request'),
|
||||
];
|
||||
}
|
||||
|
||||
_onRequest(channel) {
|
||||
if (!(channel instanceof Ci.nsIHttpChannel))
|
||||
return;
|
||||
const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||
const loadContext = helper.getLoadContext(httpChannel);
|
||||
if (!loadContext)
|
||||
return;
|
||||
try {
|
||||
const window = loadContext.associatedWindow;
|
||||
const frame = this._frameTree.frameForDocShell(window.docShell);
|
||||
if (!frame)
|
||||
return;
|
||||
const typeId = httpChannel.loadInfo ? httpChannel.loadInfo.internalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER;
|
||||
// Channel ids are not unique. We combine them with the typeId
|
||||
// to better distinguish requests. For example, favicon requests
|
||||
// have the same channel id as their associated document request.
|
||||
const channelKey = httpChannel.channelId + ':' + typeId;
|
||||
this._requestDetails.set(channelKey, {
|
||||
frameId: frame.id(),
|
||||
});
|
||||
} catch (e) {
|
||||
// Accessing loadContext.associatedWindow sometimes throws.
|
||||
}
|
||||
}
|
||||
|
||||
requestDetails(channelKey) {
|
||||
return this._requestDetails.get(channelKey) || null;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._requestDetails.clear();
|
||||
helper.removeListeners(this._eventListeners);
|
||||
}
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ['NetworkMonitor'];
|
||||
this.NetworkMonitor = NetworkMonitor;
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ class FrameData {
|
|||
}
|
||||
|
||||
class PageAgent {
|
||||
constructor(messageManager, browserChannel, sessionId, frameTree, networkMonitor) {
|
||||
constructor(messageManager, browserChannel, sessionId, frameTree) {
|
||||
this._messageManager = messageManager;
|
||||
this._browserChannel = browserChannel;
|
||||
this._sessionId = sessionId;
|
||||
|
|
@ -120,7 +120,6 @@ class PageAgent {
|
|||
this._browserRuntime = browserChannel.connect(sessionId + 'runtime');
|
||||
this._frameTree = frameTree;
|
||||
this._runtime = frameTree.runtime();
|
||||
this._networkMonitor = networkMonitor;
|
||||
|
||||
this._frameData = new Map();
|
||||
this._workerData = new Map();
|
||||
|
|
@ -146,7 +145,6 @@ class PageAgent {
|
|||
navigate: this._navigate.bind(this),
|
||||
reload: this._reload.bind(this),
|
||||
removeScriptToEvaluateOnNewDocument: this._removeScriptToEvaluateOnNewDocument.bind(this),
|
||||
requestDetails: this._requestDetails.bind(this),
|
||||
screenshot: this._screenshot.bind(this),
|
||||
scrollIntoViewIfNeeded: this._scrollIntoViewIfNeeded.bind(this),
|
||||
setCacheDisabled: this._setCacheDisabled.bind(this),
|
||||
|
|
@ -170,10 +168,6 @@ class PageAgent {
|
|||
this._dataTransfer = null;
|
||||
}
|
||||
|
||||
_requestDetails({channelKey}) {
|
||||
return this._networkMonitor.requestDetails(channelKey);
|
||||
}
|
||||
|
||||
async _setEmulatedMedia({type, colorScheme}) {
|
||||
const docShell = this._frameTree.mainFrame().docShell();
|
||||
const cv = docShell.contentViewer;
|
||||
|
|
|
|||
|
|
@ -5,19 +5,17 @@
|
|||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {FrameTree} = ChromeUtils.import('chrome://juggler/content/content/FrameTree.js');
|
||||
const {NetworkMonitor} = ChromeUtils.import('chrome://juggler/content/content/NetworkMonitor.js');
|
||||
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||
const {PageAgent} = ChromeUtils.import('chrome://juggler/content/content/PageAgent.js');
|
||||
|
||||
let frameTree;
|
||||
let networkMonitor;
|
||||
const helper = new Helper();
|
||||
const messageManager = this;
|
||||
|
||||
const sessions = new Map();
|
||||
|
||||
function createContentSession(channel, sessionId) {
|
||||
const pageAgent = new PageAgent(messageManager, channel, sessionId, frameTree, networkMonitor);
|
||||
const pageAgent = new PageAgent(messageManager, channel, sessionId, frameTree);
|
||||
sessions.set(sessionId, [pageAgent]);
|
||||
pageAgent.enable();
|
||||
}
|
||||
|
|
@ -118,7 +116,6 @@ function initialize() {
|
|||
frameTree.addScriptToEvaluateOnNewDocument(script);
|
||||
for (const { name, script } of bindings)
|
||||
frameTree.addBinding(name, script);
|
||||
networkMonitor = new NetworkMonitor(docShell, frameTree);
|
||||
|
||||
const channel = SimpleChannel.createForMessageManager('content::page', messageManager);
|
||||
|
||||
|
|
@ -180,7 +177,6 @@ function initialize() {
|
|||
for (const sessionId of sessions.keys())
|
||||
disposeContentSession(sessionId);
|
||||
|
||||
networkMonitor.dispose();
|
||||
frameTree.dispose();
|
||||
}),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ juggler.jar:
|
|||
content/protocol/AccessibilityHandler.js (protocol/AccessibilityHandler.js)
|
||||
content/content/main.js (content/main.js)
|
||||
content/content/FrameTree.js (content/FrameTree.js)
|
||||
content/content/NetworkMonitor.js (content/NetworkMonitor.js)
|
||||
content/content/PageAgent.js (content/PageAgent.js)
|
||||
content/content/Runtime.js (content/Runtime.js)
|
||||
content/content/WorkerMain.js (content/WorkerMain.js)
|
||||
|
|
|
|||
|
|
@ -16,13 +16,9 @@ const helper = new Helper();
|
|||
class NetworkHandler {
|
||||
constructor(target, session, contentChannel) {
|
||||
this._session = session;
|
||||
this._contentPage = contentChannel.connect(session.sessionId() + 'page');
|
||||
this._httpActivity = new Map();
|
||||
this._enabled = false;
|
||||
this._pageNetwork = NetworkObserver.instance().pageNetworkForTarget(target);
|
||||
this._requestInterception = false;
|
||||
this._eventListeners = [];
|
||||
this._pendingRequstWillBeSentEvents = new Set();
|
||||
}
|
||||
|
||||
async enable() {
|
||||
|
|
@ -51,9 +47,6 @@ class NetworkHandler {
|
|||
this._pageNetwork.enableRequestInterception();
|
||||
else
|
||||
this._pageNetwork.disableRequestInterception();
|
||||
// Right after we enable/disable request interception we need to await all pending
|
||||
// requestWillBeSent events before successfully returning from the method.
|
||||
await Promise.all(Array.from(this._pendingRequstWillBeSentEvents));
|
||||
}
|
||||
|
||||
async resumeInterceptedRequest({requestId, method, headers, postData}) {
|
||||
|
|
@ -69,89 +62,23 @@ class NetworkHandler {
|
|||
}
|
||||
|
||||
dispose() {
|
||||
this._contentPage.dispose();
|
||||
helper.removeListeners(this._eventListeners);
|
||||
}
|
||||
|
||||
_ensureHTTPActivity(requestId) {
|
||||
let activity = this._httpActivity.get(requestId);
|
||||
if (!activity) {
|
||||
activity = {
|
||||
_id: requestId,
|
||||
_lastSentEvent: null,
|
||||
request: null,
|
||||
response: null,
|
||||
complete: null,
|
||||
failed: null,
|
||||
};
|
||||
this._httpActivity.set(requestId, activity);
|
||||
}
|
||||
return activity;
|
||||
}
|
||||
|
||||
_reportHTTPAcitivityEvents(activity) {
|
||||
// State machine - sending network events.
|
||||
if (!activity._lastSentEvent && activity.request) {
|
||||
this._session.emitEvent('Network.requestWillBeSent', activity.request);
|
||||
activity._lastSentEvent = 'requestWillBeSent';
|
||||
}
|
||||
if (activity._lastSentEvent === 'requestWillBeSent' && activity.response) {
|
||||
this._session.emitEvent('Network.responseReceived', activity.response);
|
||||
activity._lastSentEvent = 'responseReceived';
|
||||
}
|
||||
if (activity._lastSentEvent === 'responseReceived' && activity.complete) {
|
||||
this._session.emitEvent('Network.requestFinished', activity.complete);
|
||||
activity._lastSentEvent = 'requestFinished';
|
||||
}
|
||||
if (activity._lastSentEvent && activity.failed) {
|
||||
this._session.emitEvent('Network.requestFailed', activity.failed);
|
||||
activity._lastSentEvent = 'requestFailed';
|
||||
}
|
||||
|
||||
// Clean up if request lifecycle is over.
|
||||
if (activity._lastSentEvent === 'requestFinished' || activity._lastSentEvent === 'requestFailed')
|
||||
this._httpActivity.delete(activity._id);
|
||||
}
|
||||
|
||||
async _onRequest(eventDetails, channelKey) {
|
||||
let pendingRequestCallback;
|
||||
let pendingRequestPromise = new Promise(x => pendingRequestCallback = x);
|
||||
this._pendingRequstWillBeSentEvents.add(pendingRequestPromise);
|
||||
let details = null;
|
||||
try {
|
||||
details = await this._contentPage.send('requestDetails', {channelKey});
|
||||
} catch (e) {
|
||||
pendingRequestCallback();
|
||||
this._pendingRequstWillBeSentEvents.delete(pendingRequestPromise);
|
||||
return;
|
||||
}
|
||||
const frameId = details ? details.frameId : undefined;
|
||||
const activity = this._ensureHTTPActivity(eventDetails.requestId);
|
||||
activity.request = {
|
||||
frameId,
|
||||
...eventDetails,
|
||||
};
|
||||
this._reportHTTPAcitivityEvents(activity);
|
||||
pendingRequestCallback();
|
||||
this._pendingRequstWillBeSentEvents.delete(pendingRequestPromise);
|
||||
this._session.emitEvent('Network.requestWillBeSent', eventDetails);
|
||||
}
|
||||
|
||||
async _onResponse(eventDetails) {
|
||||
const activity = this._ensureHTTPActivity(eventDetails.requestId);
|
||||
activity.response = eventDetails;
|
||||
this._reportHTTPAcitivityEvents(activity);
|
||||
this._session.emitEvent('Network.responseReceived', eventDetails);
|
||||
}
|
||||
|
||||
async _onRequestFinished(eventDetails) {
|
||||
const activity = this._ensureHTTPActivity(eventDetails.requestId);
|
||||
activity.complete = eventDetails;
|
||||
this._reportHTTPAcitivityEvents(activity);
|
||||
this._session.emitEvent('Network.requestFinished', eventDetails);
|
||||
}
|
||||
|
||||
async _onRequestFailed(eventDetails) {
|
||||
const activity = this._ensureHTTPActivity(eventDetails.requestId);
|
||||
activity.failed = eventDetails;
|
||||
this._reportHTTPAcitivityEvents(activity);
|
||||
this._session.emitEvent('Network.requestFailed', eventDetails);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue