Skip to content

Commit

Permalink
feat(firefox): basic support for Network (#3988)
Browse files Browse the repository at this point in the history
This patch introduces basic Request and Response events for
page. It also teaches navigation methods, e.g. `page.goto` to return
navigation response.
  • Loading branch information
aslushnikov committed Feb 13, 2019
1 parent fb9d404 commit afb9355
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 56 deletions.
13 changes: 12 additions & 1 deletion experimental/puppeteer-firefox/lib/Events.js
Expand Up @@ -10,6 +10,10 @@ const Events = {
Load: 'load',
PageError: 'pageerror',
Popup: 'popup',
Request: 'request',
Response: 'response',
RequestFinished: 'requestfinished',
RequestFailed: 'requestfailed',
},
Browser: {
TargetCreated: 'targetcreated',
Expand All @@ -30,8 +34,15 @@ const Events = {
Load: Symbol('Events.FrameManager.Load'),
DOMContentLoaded: Symbol('Events.FrameManager.DOMContentLoaded'),
FrameAttached: Symbol('Events.FrameManager.FrameAttached'),
FrameNavigated: Symbol('Events.FrameManager.FrameNavigated'),
FrameDetached: Symbol('Events.FrameManager.FrameDetached'),
}
},

NetworkManager: {
Request: Symbol('Events.NetworkManager.Request'),
Response: Symbol('Events.NetworkManager.Response'),
RequestFinished: Symbol('Events.NetworkManager.RequestFinished'),
},
};

module.exports = {Events};
16 changes: 16 additions & 0 deletions experimental/puppeteer-firefox/lib/FrameManager.js
Expand Up @@ -24,6 +24,8 @@ class FrameManager extends EventEmitter {
helper.addEventListener(this._session, 'Page.eventFired', this._onEventFired.bind(this)),
helper.addEventListener(this._session, 'Page.frameAttached', this._onFrameAttached.bind(this)),
helper.addEventListener(this._session, 'Page.frameDetached', this._onFrameDetached.bind(this)),
helper.addEventListener(this._session, 'Page.navigationCommitted', this._onNavigationCommitted.bind(this)),
helper.addEventListener(this._session, 'Page.sameDocumentNavigation', this._onSameDocumentNavigation.bind(this)),
];
}

Expand All @@ -48,6 +50,20 @@ class FrameManager extends EventEmitter {
}
}

_onNavigationCommitted(params) {
const frame = this._frames.get(params.frameId);
frame._navigated(params.url, params.name, params.navigationId);
frame._DOMContentLoadedFired = false;
frame._loadFired = false;
this.emit(Events.FrameManager.FrameNavigated, frame);
}

_onSameDocumentNavigation(params) {
const frame = this._frames.get(params.frameId);
frame._url = params.url;
this.emit(Events.FrameManager.FrameNavigated, frame);
}

_onFrameAttached(params) {
const frame = new Frame(this._session, this, this._page, params.frameId, this._timeoutSettings);
const parentFrame = this._frames.get(params.parentFrameId) || null;
Expand Down
174 changes: 174 additions & 0 deletions experimental/puppeteer-firefox/lib/NetworkManager.js
@@ -0,0 +1,174 @@
const {helper} = require('./helper');
const util = require('util');
const EventEmitter = require('events');
const {Events} = require('./Events');

class NetworkManager extends EventEmitter {
constructor(session, frameManager) {
super();
this._session = session;

this._requests = new Map();
this._frameManager = frameManager;

this._eventListeners = [
helper.addEventListener(session, 'Page.requestWillBeSent', this._onRequestWillBeSent.bind(this)),
helper.addEventListener(session, 'Page.responseReceived', this._onResponseReceived.bind(this)),
helper.addEventListener(session, 'Page.requestFinished', this._onRequestFinished.bind(this)),
];
}

_onRequestWillBeSent(event) {
const frame = this._frameManager && event.frameId ? this._frameManager.frame(event.frameId) : null;
if (!frame)
return;
let redirectChain = [];
const redirected = event.redirectedFrom ? this._requests.get(event.redirectedFrom) : null;
if (redirected) {
redirectChain = redirected._redirectChain;
redirectChain.push(redirected);
this._requests.delete(redirected._id);
}
const request = new Request(frame, redirectChain, event);
this._requests.set(request._id, request);
this.emit(Events.NetworkManager.Request, request);
}

_onResponseReceived(event) {
const request = this._requests.get(event.requestId);
if (!request)
return;
const response = new Response(request, event);
request._response = response;
this.emit(Events.NetworkManager.Response, response);
}

_onRequestFinished(event) {
const request = this._requests.get(event.requestId);
if (!request)
return;
// Keep redirected requests in the map for future reference in redirectChain.
const isRedirected = request.response().status() >= 300 && request.response().status() <= 399;
if (!isRedirected)
this._requests.delete(request._id);
this.emit(Events.NetworkManager.RequestFinished, request);
}

dispose() {
helper.removeEventListeners(this._eventListeners);
}
}

/**
*
* document, stylesheet, image, media, font, script, texttrack, xhr, fetch, eventsource, websocket, manifest, other.
*/
const causeToResourceType = {
TYPE_INVALID: 'other',
TYPE_OTHER: 'other',
TYPE_SCRIPT: 'script',
TYPE_IMAGE: 'image',
TYPE_STYLESHEET: 'stylesheet',
TYPE_OBJECT: 'other',
TYPE_DOCUMENT: 'document',
TYPE_SUBDOCUMENT: 'document',
TYPE_REFRESH: 'document',
TYPE_XBL: 'other',
TYPE_PING: 'other',
TYPE_XMLHTTPREQUEST: 'xhr',
TYPE_OBJECT_SUBREQUEST: 'other',
TYPE_DTD: 'other',
TYPE_FONT: 'font',
TYPE_MEDIA: 'media',
TYPE_WEBSOCKET: 'websocket',
TYPE_CSP_REPORT: 'other',
TYPE_XSLT: 'other',
TYPE_BEACON: 'other',
TYPE_FETCH: 'fetch',
TYPE_IMAGESET: 'images',
TYPE_WEB_MANIFEST: 'manifest',
};

class Request {
constructor(frame, redirectChain, payload) {
this._frame = frame;
this._id = payload.requestId;
this._redirectChain = redirectChain;
this._url = payload.url;
this._response = null;
this._isNavigationRequest = payload.isNavigationRequest;
this._method = payload.method;
this._resourceType = causeToResourceType[payload.cause] || 'other';
}

redirectChain() {
return this._redirectChain.slice();
}

resourceType() {
return this._resourceType;
}

url() {
return this._url;
}

method() {
return this._method;
}

isNavigationRequest() {
return this._isNavigationRequest;
}

frame() {
return this._frame;
}

response() {
return this._response;
}
}

class Response {
constructor(request, payload) {
this._request = request;
this._remoteIPAddress = payload.remoteIPAddress;
this._remotePort = payload.remotePort;
this._status = payload.status;
this._statusText = payload.statusText;
}

status() {
return this._status;
}

statusText() {
return this._statusText;
}

ok() {
return this._status >= 200 && this._status <= 299;
}

remoteAddress() {
return {
ip: this._remoteIPAddress,
port: this._remotePort,
};
}

frame() {
return this._request.frame();
}

url() {
return this._request.url();
}

request() {
return this._request;
}
}

module.exports = {NetworkManager, Request, Response};

0 comments on commit afb9355

Please sign in to comment.