зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1311177 - Implement the devtools.network.getHAR API method; r=jdescottes,rickychien,rpl
MozReview-Commit-ID: I9F4tGSwBrt --HG-- extra : rebase_source : ce3adb6ce47e62302b29651a05276d13621679cb
This commit is contained in:
Родитель
6f469dc037
Коммит
e1114707d4
|
@ -25,6 +25,10 @@ this.devtools_network = class extends ExtensionAPI {
|
|||
});
|
||||
};
|
||||
}).api(),
|
||||
|
||||
getHAR: function() {
|
||||
return context.devToolsToolbox.getHARFromNetMonitor();
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
"functions": [
|
||||
{
|
||||
"name": "getHAR",
|
||||
"unsupported": true,
|
||||
"type": "function",
|
||||
"description": "Returns HAR log that contains all known network requests.",
|
||||
"async": "callback",
|
||||
|
|
|
@ -5,60 +5,84 @@
|
|||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {gDevTools} = require("devtools/client/framework/devtools");
|
||||
|
||||
function background() {
|
||||
browser.test.onMessage.addListener(msg => {
|
||||
let code;
|
||||
if (msg === "navigate") {
|
||||
code = "window.wrappedJSObject.location.href = 'http://example.com/';";
|
||||
browser.tabs.executeScript({code});
|
||||
} else if (msg === "reload") {
|
||||
code = "window.wrappedJSObject.location.reload(true);";
|
||||
browser.tabs.executeScript({code});
|
||||
}
|
||||
});
|
||||
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
if (changeInfo.status === "complete" && tab.url === "http://example.com/") {
|
||||
browser.test.sendMessage("tabUpdated");
|
||||
}
|
||||
});
|
||||
browser.test.sendMessage("ready");
|
||||
}
|
||||
|
||||
function devtools_page() {
|
||||
let eventCount = 0;
|
||||
let listener = url => {
|
||||
eventCount++;
|
||||
browser.test.assertEq("http://example.com/", url, "onNavigated received the expected url.");
|
||||
browser.test.sendMessage("onNavigatedFired", eventCount);
|
||||
|
||||
if (eventCount === 2) {
|
||||
eventCount = 0;
|
||||
browser.devtools.network.onNavigated.removeListener(listener);
|
||||
}
|
||||
};
|
||||
browser.devtools.network.onNavigated.addListener(listener);
|
||||
|
||||
let harLogCount = 0;
|
||||
let harListener = async msg => {
|
||||
if (msg !== "getHAR") {
|
||||
return;
|
||||
}
|
||||
|
||||
harLogCount++;
|
||||
|
||||
const harLog = await browser.devtools.network.getHAR();
|
||||
browser.test.sendMessage("getHAR-result", harLog);
|
||||
|
||||
if (harLogCount === 2) {
|
||||
harLogCount = 0;
|
||||
browser.test.onMessage.removeListener(harListener);
|
||||
}
|
||||
};
|
||||
browser.test.onMessage.addListener(harListener);
|
||||
}
|
||||
|
||||
let extData = {
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["tabs", "http://mochi.test/", "http://example.com/"],
|
||||
devtools_page: "devtools_page.html",
|
||||
},
|
||||
files: {
|
||||
"devtools_page.html": `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="devtools_page.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>`,
|
||||
"devtools_page.js": devtools_page,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Test for `chrome.devtools.network.onNavigate()` API
|
||||
*/
|
||||
add_task(async function test_devtools_network_on_navigated() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
|
||||
|
||||
function background() {
|
||||
browser.test.onMessage.addListener(msg => {
|
||||
let code;
|
||||
if (msg === "navigate") {
|
||||
code = "window.wrappedJSObject.location.href = 'http://example.com/';";
|
||||
browser.tabs.executeScript({code});
|
||||
} else if (msg === "reload") {
|
||||
code = "window.wrappedJSObject.location.reload(true);";
|
||||
browser.tabs.executeScript({code});
|
||||
}
|
||||
});
|
||||
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
if (changeInfo.status === "complete" && tab.url === "http://example.com/") {
|
||||
browser.test.sendMessage("tabUpdated");
|
||||
}
|
||||
});
|
||||
browser.test.sendMessage("ready");
|
||||
}
|
||||
|
||||
function devtools_page() {
|
||||
let eventCount = 0;
|
||||
let listener = url => {
|
||||
eventCount++;
|
||||
browser.test.assertEq("http://example.com/", url, "onNavigated received the expected url.");
|
||||
if (eventCount === 2) {
|
||||
browser.devtools.network.onNavigated.removeListener(listener);
|
||||
}
|
||||
browser.test.sendMessage("onNavigatedFired", eventCount);
|
||||
};
|
||||
browser.devtools.network.onNavigated.addListener(listener);
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["tabs", "http://mochi.test/", "http://example.com/"],
|
||||
devtools_page: "devtools_page.html",
|
||||
},
|
||||
files: {
|
||||
"devtools_page.html": `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="devtools_page.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>`,
|
||||
"devtools_page.js": devtools_page,
|
||||
},
|
||||
});
|
||||
let extension = ExtensionTestUtils.loadExtension(extData);
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("ready");
|
||||
|
@ -90,3 +114,49 @@ add_task(async function test_devtools_network_on_navigated() {
|
|||
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test for `chrome.devtools.network.getHAR()` API
|
||||
*/
|
||||
add_task(async function test_devtools_network_get_har() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
|
||||
let extension = ExtensionTestUtils.loadExtension(extData);
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("ready");
|
||||
|
||||
let target = gDevTools.getTargetForTab(tab);
|
||||
|
||||
// Open the Toolbox
|
||||
let toolbox = await gDevTools.showToolbox(target, "webconsole");
|
||||
info("Developer toolbox opened.");
|
||||
|
||||
// Get HAR, it should be empty since the Net panel wasn't selected.
|
||||
const getHAREmptyPromise = extension.awaitMessage("getHAR-result");
|
||||
extension.sendMessage("getHAR");
|
||||
const getHAREmptyResult = await getHAREmptyPromise;
|
||||
is(getHAREmptyResult.log.entries.length, 0, "HAR log should be empty");
|
||||
|
||||
// Select the Net panel.
|
||||
await toolbox.selectTool("netmonitor");
|
||||
|
||||
// Reload the page to collect some HTTP requests.
|
||||
extension.sendMessage("navigate");
|
||||
await extension.awaitMessage("tabUpdated");
|
||||
await extension.awaitMessage("onNavigatedFired");
|
||||
|
||||
// Get HAR, it should not be empty now.
|
||||
const getHARPromise = extension.awaitMessage("getHAR-result");
|
||||
extension.sendMessage("getHAR");
|
||||
const getHARResult = await getHARPromise;
|
||||
is(getHARResult.log.entries.length, 1, "HAR log should not be empty");
|
||||
|
||||
// Shutdown
|
||||
await gDevTools.closeToolbox(target);
|
||||
|
||||
await target.destroy();
|
||||
|
||||
await extension.unload();
|
||||
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
|
|
@ -69,6 +69,8 @@ loader.lazyRequireGetter(this, "viewSource",
|
|||
"devtools/client/shared/view-source");
|
||||
loader.lazyRequireGetter(this, "StyleSheetsFront",
|
||||
"devtools/shared/fronts/stylesheets", true);
|
||||
loader.lazyRequireGetter(this, "buildHarLog",
|
||||
"devtools/client/netmonitor/src/har/har-builder-utils", true);
|
||||
|
||||
loader.lazyGetter(this, "domNodeConstants", () => {
|
||||
return require("devtools/shared/dom-node-constants");
|
||||
|
@ -3006,4 +3008,21 @@ Toolbox.prototype = {
|
|||
viewSource: function (sourceURL, sourceLine) {
|
||||
return viewSource.viewSource(this, sourceURL, sourceLine);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns data (HAR) collected by the Network panel.
|
||||
*/
|
||||
getHARFromNetMonitor: function () {
|
||||
let netPanel = this.getPanel("netmonitor");
|
||||
|
||||
// The panel doesn't have to exist (it must be selected
|
||||
// by the user at least once to be created).
|
||||
// Return default empty HAR file in such case.
|
||||
if (!netPanel) {
|
||||
return Promise.resolve(buildHarLog(Services.appinfo));
|
||||
}
|
||||
|
||||
// Use Netmonitor object to get the current HAR log.
|
||||
return netPanel.panelWin.Netmonitor.getHar();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -24,8 +24,11 @@ const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
|
|||
const { Connector } = require("./src/connector/index");
|
||||
const { configureStore } = require("./src/utils/create-store");
|
||||
const App = createFactory(require("./src/components/App"));
|
||||
const { getDisplayedRequestById } = require("./src/selectors/index");
|
||||
const { EVENTS } = require("./src/constants");
|
||||
const {
|
||||
getDisplayedRequestById,
|
||||
getSortedRequests
|
||||
} = require("./src/selectors/index");
|
||||
|
||||
// Inject EventEmitter into global window.
|
||||
EventEmitter.decorate(window);
|
||||
|
@ -42,7 +45,7 @@ window.connector = connector;
|
|||
/**
|
||||
* Global Netmonitor object in this panel. This object can be consumed
|
||||
* by other panels (e.g. Console is using inspectRequest), by the
|
||||
* Launchpad (bootstrap), etc.
|
||||
* Launchpad (bootstrap), WebExtension API (getHAR), etc.
|
||||
*/
|
||||
window.Netmonitor = {
|
||||
bootstrap({ toolbox, panel }) {
|
||||
|
@ -77,6 +80,25 @@ window.Netmonitor = {
|
|||
return connector.disconnect();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns list of requests currently available in the panel.
|
||||
*/
|
||||
getHar() {
|
||||
let { HarExporter } = require("devtools/client/netmonitor/src/har/har-exporter");
|
||||
let { getLongString, getTabTarget, requestData } = connector;
|
||||
let { form: { title, url } } = getTabTarget();
|
||||
let state = store.getState();
|
||||
|
||||
let options = {
|
||||
getString: getLongString,
|
||||
items: getSortedRequests(state),
|
||||
requestData,
|
||||
title: title || url,
|
||||
};
|
||||
|
||||
return HarExporter.getHar(options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Selects the specified request in the waterfall and opens the details view.
|
||||
* This is a firefox toolbox specific API, which providing an ability to inspect
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* 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";
|
||||
|
||||
/**
|
||||
* Currently supported HAR version.
|
||||
*/
|
||||
const HAR_VERSION = "1.1";
|
||||
|
||||
function buildHarLog(appInfo) {
|
||||
return {
|
||||
log: {
|
||||
version: HAR_VERSION,
|
||||
creator: {
|
||||
name: appInfo.name,
|
||||
version: appInfo.version
|
||||
},
|
||||
browser: {
|
||||
name: appInfo.name,
|
||||
version: appInfo.version
|
||||
},
|
||||
pages: [],
|
||||
entries: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.buildHarLog = buildHarLog;
|
|
@ -13,9 +13,8 @@ const {
|
|||
getUrlQuery,
|
||||
parseQueryString,
|
||||
} = require("../utils/request-utils");
|
||||
|
||||
const { buildHarLog } = require("./har-builder-utils");
|
||||
const L10N = new LocalizationHelper("devtools/client/locales/har.properties");
|
||||
const HAR_VERSION = "1.1";
|
||||
|
||||
/**
|
||||
* This object is responsible for building HAR file. See HAR spec:
|
||||
|
@ -55,38 +54,22 @@ HarBuilder.prototype = {
|
|||
this.promises = [];
|
||||
|
||||
// Build basic structure for data.
|
||||
let log = this.buildLog();
|
||||
let log = buildHarLog(appInfo);
|
||||
|
||||
// Build entries.
|
||||
for (let file of this._options.items) {
|
||||
log.entries.push(await this.buildEntry(log, file));
|
||||
log.log.entries.push(await this.buildEntry(log.log, file));
|
||||
}
|
||||
|
||||
// Some data needs to be fetched from the backend during the
|
||||
// build process, so wait till all is done.
|
||||
await Promise.all(this.promises);
|
||||
|
||||
return { log };
|
||||
return log;
|
||||
},
|
||||
|
||||
// Helpers
|
||||
|
||||
buildLog: function () {
|
||||
return {
|
||||
version: HAR_VERSION,
|
||||
creator: {
|
||||
name: appInfo.name,
|
||||
version: appInfo.version
|
||||
},
|
||||
browser: {
|
||||
name: appInfo.name,
|
||||
version: appInfo.version
|
||||
},
|
||||
pages: [],
|
||||
entries: [],
|
||||
};
|
||||
},
|
||||
|
||||
buildPage: function (file) {
|
||||
let page = {};
|
||||
|
||||
|
|
|
@ -119,6 +119,16 @@ const HarExporter = {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get HAR data as JSON object.
|
||||
*
|
||||
* @param Object options
|
||||
* Configuration object, see save() for detailed description.
|
||||
*/
|
||||
getHar: function (options) {
|
||||
return this.fetchHarData(options).then(JSON.parse);
|
||||
},
|
||||
|
||||
// Helpers
|
||||
|
||||
fetchHarData: function (options) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
DevToolsModules(
|
||||
'har-automation.js',
|
||||
'har-builder-utils.js',
|
||||
'har-builder.js',
|
||||
'har-collector.js',
|
||||
'har-exporter.js',
|
||||
|
|
Загрузка…
Ссылка в новой задаче