--- a/devtools/client/performance/new/components/Perf.js
+++ b/devtools/client/performance/new/components/Perf.js
@@ -78,25 +78,16 @@ class Perf extends Component {
recordingState = isActive
? OTHER_IS_RECORDING
: AVAILABLE_TO_RECORD;
}
}
this.setState({ isSupportedPlatform, recordingState });
});
- // We don't yet know what state the profile is in, find out.
- this.props.perfFront.isActive().then(isActive => {
- if (this.state.recordingState === NOT_YET_KNOWN) {
- this.setState({
- recordingState: isActive ? OTHER_IS_RECORDING : AVAILABLE_TO_RECORD
- });
- }
- });
-
// Handle when the profiler changes state. It might be us, it might be someone else.
this.props.perfFront.on("profiler-started", this.handleProfilerStarting);
this.props.perfFront.on("profiler-stopped", this.handleProfilerStopping);
this.props.perfFront.on("profile-locked-for-private-browsing",
this.handlePrivateBrowsingStarting);
this.props.perfFront.on("profile-unlocked-from-private-browsing",
this.handlePrivateBrowsingEnding);
}
--- a/devtools/client/performance/new/frame-script.js
+++ b/devtools/client/performance/new/frame-script.js
@@ -1,15 +1,19 @@
/* 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";
-/* eslint-env browser */
/* global addMessageListener, addEventListener, content */
+/**
+ * This frame script injects itself into perf-html.io and injects the profile
+ * into the page. It is mostly taken from the Gecko Profiler Addon implementation.
+ */
+
const TRANSFER_EVENT = "devtools:perf-html-transfer-profile";
let gProfile = null;
addMessageListener(TRANSFER_EVENT, e => {
gProfile = e.data;
connectToPage();
addEventListener("DOMContentLoaded", connectToPage);
@@ -20,54 +24,63 @@ function connectToPage() {
if (unsafeWindow.connectToGeckoProfiler) {
unsafeWindow.connectToGeckoProfiler(makeAccessibleToPage({
getProfile: () => Promise.resolve(gProfile),
getSymbolTable: (debugName, breakpadId) => getSymbolTable(debugName, breakpadId),
}, unsafeWindow));
}
}
+/**
+ * For now, do not try to symbolicate. Reject any attempt.
+ */
function getSymbolTable(debugName, breakpadId) {
return new Promise((resolve, reject) => {
reject();
});
}
-// Security stuff below, you usually don't need to read this.
+// The following functions handle the security of cloning the object into the page.
-// Create a promise that can be used in the page.
+/**
+ * Create a promise that can be used in the page.
+ */
function createPromiseInPage(fun, contentGlobal) {
function funThatClonesObjects(resolve, reject) {
return fun(result => resolve(Components.utils.cloneInto(result, contentGlobal)),
error => reject(Components.utils.cloneInto(error, contentGlobal)));
}
return new contentGlobal.Promise(Components.utils.exportFunction(funThatClonesObjects,
contentGlobal));
}
-// Returns a function that calls the original function and tries to make the
-// return value available to the page.
+/**
+ * Returns a function that calls the original function and tries to make the
+ * return value available to the page.
+ */
function wrapFunction(fun, contentGlobal) {
return function () {
let result = fun.apply(this, arguments);
if (typeof result === "object") {
if (("then" in result) && (typeof result.then === "function")) {
// fun returned a promise.
return createPromiseInPage((resolve, reject) =>
result.then(resolve, reject), contentGlobal);
}
return Components.utils.cloneInto(result, contentGlobal);
}
return result;
};
}
-// Pass a simple object containing values that are objects or functions.
-// The objects or functions are wrapped in such a way that they can be
-// consumed by the page.
+/**
+ * Pass a simple object containing values that are objects or functions.
+ * The objects or functions are wrapped in such a way that they can be
+ * consumed by the page.
+ */
function makeAccessibleToPage(obj, contentGlobal) {
let result = Components.utils.createObjectIn(contentGlobal);
for (let field in obj) {
switch (typeof obj[field]) {
case "function":
Components.utils.exportFunction(
wrapFunction(obj[field], contentGlobal), result, { defineAs: field });
break;
--- a/devtools/client/performance/new/moz.build
+++ b/devtools/client/performance/new/moz.build
@@ -7,12 +7,12 @@ DIRS += [
'components',
]
DevToolsModules(
'initializer.js',
'panel.js',
)
-BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
+MOCHITEST_CHROME_MANIFESTS += ['test/chrome/chrome.ini']
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
deleted file mode 100644
--- a/devtools/client/performance/new/test/browser.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[DEFAULT]
-tags = devtools
-subsuite = devtools
-support-files =
- head.js
- doc_mount_react.html
-
-[browser_perf-state-01.js]
-[browser_perf-state-02.js]
-[browser_perf-state-03.js]
deleted file mode 100644
--- a/devtools/client/performance/new/test/browser_perf-state-01.js
+++ /dev/null
@@ -1,49 +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";
-
-/**
- * Tests the normal workflow of starting and stopping the profiler through the Perf
- * component.
- */
-addPerfTest(async ({ document, browserRequire, mountElement, perfFront }) => {
- const Perf = browserRequire("devtools/client/performance/new/components/Perf");
- const React = browserRequire("devtools/client/shared/vendor/react");
- const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
-
- // Inject a function which will allow us to receive the profile.
- let profile;
- function receiveProfile(profileIn) {
- profile = profileIn;
- }
-
- const element = React.createElement(Perf, { perfFront, receiveProfile });
- const perfComponent = ReactDOM.render(element, mountElement);
-
- is(perfComponent.state.recordingState, "not-yet-known",
- "The component at first is in an unknown state.");
-
- await perfFront.flushAsyncQueue();
- is(perfComponent.state.recordingState, "available-to-record",
- "After talking to the actor, we're ready to record.");
-
- document.querySelector("button").click();
- is(perfComponent.state.recordingState, "request-to-start-recording",
- "Sent in a request to start recording.");
-
- await perfFront.flushAsyncQueue();
- is(perfComponent.state.recordingState, "recording",
- "The actor has started its recording");
-
- document.querySelector("button").click();
- is(perfComponent.state.recordingState, "request-to-get-profile-and-stop-profiler",
- "We have requested to stop the profiler.");
-
- await perfFront.flushAsyncQueue();
- is(perfComponent.state.recordingState, "available-to-record",
- "The profiler is available to record again.");
-
- await perfFront.flushAsyncQueue();
- is(typeof profile, "object", "Got a profile");
-});
deleted file mode 100644
--- a/devtools/client/performance/new/test/browser_perf-state-02.js
+++ /dev/null
@@ -1,37 +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";
-
-/**
- * Tests the perf component for when the profiler is already started.
- */
-addPerfTest(async ({ document, browserRequire, mountElement, perfFront }) => {
- const Perf = browserRequire("devtools/client/performance/new/components/Perf");
- const React = browserRequire("devtools/client/shared/vendor/react");
- const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
-
- const element = React.createElement(Perf, { perfFront });
-
- ok(true, "Start the profiler before initiliazing the component, to simulate" +
- "the profiler being controlled by another tool.");
-
- perfFront.startProfiler();
- await perfFront.flushAsyncQueue();
-
- const perfComponent = ReactDOM.render(element, mountElement);
- is(perfComponent.state.recordingState, "not-yet-known",
- "The component at first is in an unknown state.");
-
- await perfFront.flushAsyncQueue();
- is(perfComponent.state.recordingState, "other-is-recording",
- "The profiler is not available to record.");
-
- document.querySelector("button").click();
- is(perfComponent.state.recordingState, "request-to-stop-profiler",
- "We can request to stop the profiler.");
-
- await perfFront.flushAsyncQueue();
- is(perfComponent.state.recordingState, "available-to-record",
- "The profiler is now available to record.");
-});
deleted file mode 100644
--- a/devtools/client/performance/new/test/browser_perf-state-03.js
+++ /dev/null
@@ -1,40 +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";
-
-/**
- * Tests the workflow of what happens when a third party tool interrupts a recording.
- */
-addPerfTest(async ({ document, browserRequire, mountElement, perfFront }) => {
- const Perf = browserRequire("devtools/client/performance/new/components/Perf");
- const React = browserRequire("devtools/client/shared/vendor/react");
- const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
-
- const element = React.createElement(Perf, { perfFront });
-
- const perfComponent = ReactDOM.render(element, mountElement);
- is(perfComponent.state.recordingState, "not-yet-known",
- "The component at first is in an unknown state.");
-
- await perfFront.flushAsyncQueue();
- is(perfComponent.state.recordingState, "available-to-record",
- "After talking to the actor, we're ready to record.");
-
- document.querySelector("button").click();
- is(perfComponent.state.recordingState, "request-to-start-recording",
- "Sent in a request to start recording.");
-
- await perfFront.flushAsyncQueue();
- is(perfComponent.state.recordingState, "recording",
- "The actor has started its recording");
-
- ok(true, "Simulate a third party stopping the profiler.");
- perfFront.stopProfilerAndDiscardProfile();
- await perfFront.flushAsyncQueue();
-
- ok(perfComponent.state.recordingUnexpectedlyStopped,
- "The profiler unexpectedly stopped.");
- is(perfComponent.state.recordingState, "available-to-record",
- "However, the profiler is available to record again.");
-});
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/new/test/chrome/chrome.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+ head.js
+
+[test_perf-state-01.html]
+[test_perf-state-02.html]
+[test_perf-state-03.html]
+[test_perf-state-04.html]
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/new/test/chrome/head.js
@@ -0,0 +1,136 @@
+/* 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";
+
+/* exported addPerfTest, MockPerfFront */
+/* globals URL_ROOT */
+
+const { BrowserLoader } = Components.utils.import("resource://devtools/client/shared/browser-loader.js", {});
+var { require } = BrowserLoader({
+ baseURI: "resource://devtools/client/performance/new/",
+ window
+});
+
+const EventEmitter = require("devtools/shared/event-emitter");
+const { perfDescription } = require("devtools/shared/specs/perf");
+const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const flags = require("devtools/shared/flags");
+
+flags.testing = true;
+let EXPECTED_DTU_ASSERT_FAILURE_COUNT = 0;
+SimpleTest.registerCleanupFunction(function () {
+ if (DevToolsUtils.assertionFailureCount !== EXPECTED_DTU_ASSERT_FAILURE_COUNT) {
+ ok(false, "Should have had the expected number of DevToolsUtils.assert() failures." +
+ "Expected " + EXPECTED_DTU_ASSERT_FAILURE_COUNT +
+ ", got " + DevToolsUtils.assertionFailureCount);
+ }
+});
+
+/**
+ * Handle test setup and teardown while catching errors.
+ */
+function addPerfTest(asyncTest) {
+ window.onload = async () => {
+ try {
+ await asyncTest();
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+ };
+}
+
+/**
+ * The Gecko Profiler is a rather heavy-handed component that uses a lot of resources.
+ * In order to get around that, and have quick component tests we provide a mock of
+ * the performance front. It also has a method called flushAsyncQueue() that will
+ * flush any queued async calls to deterministically run our tests.
+ */
+class MockPerfFront extends EventEmitter {
+ constructor() {
+ super();
+ this._isActive = false;
+ this._asyncQueue = [];
+
+ // Tests can update these two values directly as needed.
+ this.mockIsSupported = true;
+ this.mockIsLocked = false;
+
+ // Wrap all async methods in a flushable queue, so that tests can control
+ // when the responses come back.
+ this.isActive = this._wrapInAsyncQueue(this.isActive);
+ this.startProfiler = this._wrapInAsyncQueue(this.startProfiler);
+ this.stopProfilerAndDiscardProfile = this._wrapInAsyncQueue(
+ this.stopProfilerAndDiscardProfile);
+ this.getProfileAndStopProfiler = this._wrapInAsyncQueue(
+ this.getProfileAndStopProfiler);
+ }
+
+ /**
+ * Provide a flushable queue mechanism for all async work. The work piles up
+ * and then is evaluated at once when _flushPendingQueue is called.
+ */
+ _wrapInAsyncQueue(fn) {
+ if (typeof fn !== "function") {
+ throw new Error("_wrapInAsyncQueue requires a function");
+ }
+ return (...args) => {
+ return new Promise(resolve => {
+ this._asyncQueue.push(() => {
+ resolve(fn.apply(this, args));
+ });
+ });
+ };
+ }
+
+ flushAsyncQueue() {
+ const pending = this._asyncQueue;
+ this._asyncQueue = [];
+ pending.forEach(fn => fn());
+ // Wait for the next frame before continuing
+ return new Promise(requestAnimationFrame);
+ }
+
+ startProfiler() {
+ this._isActive = true;
+ // Defer this so it doesn't happen immediately.
+ this.emit("profiler-started");
+ }
+
+ getProfileAndStopProfiler() {
+ this._isActive = false;
+ // Defer this so it doesn't happen immediately.
+ this.emit("profiler-stopped");
+ // Return a fake profile.
+ return {};
+ }
+
+ stopProfilerAndDiscardProfile() {
+ this._isActive = false;
+ // Defer this so it doesn't happen immediately.
+ this.emit("profiler-stopped");
+ }
+
+ isActive() {
+ return this._isActive;
+ }
+
+ isSupportedPlatform() {
+ return this.mockIsSupported;
+ }
+
+ isLockedForPrivateBrowsing() {
+ return this.mockIsLocked;
+ }
+}
+
+// Do a quick validation to make sure that our Mock has the same methods as a spec.
+const mockKeys = Object.getOwnPropertyNames(MockPerfFront.prototype);
+Object.getOwnPropertyNames(perfDescription.methods).forEach(methodName => {
+ if (!mockKeys.includes(methodName)) {
+ throw new Error(`The MockPerfFront is missing the method "${methodName}" from the ` +
+ "actor's spec. It should be added to the mock.");
+ }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/new/test/chrome/test_perf-state-01.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!-- 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/. -->
+<head>
+ <meta charset="utf-8">
+ <title>Perf component test</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+ <div id="container"></div>
+
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+ "use strict";
+
+ /**
+ * Test the normal workflow of starting and stopping the profiler through the
+ * Perf component.
+ */
+ addPerfTest(async () => {
+ const Perf = require("devtools/client/performance/new/components/Perf");
+ const React = require("devtools/client/shared/vendor/react");
+ const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+ const perfFront = new MockPerfFront();
+ const container = document.querySelector("#container");
+
+ // Inject a function which will allow us to receive the profile.
+ let profile;
+ function receiveProfile(profileIn) {
+ profile = profileIn;
+ }
+
+ const element = React.createElement(Perf, { perfFront, receiveProfile });
+ const perfComponent = ReactDOM.render(element, container);
+ is(perfComponent.state.recordingState, "not-yet-known",
+ "The component at first is in an unknown state.");
+
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "available-to-record",
+ "After talking to the actor, we're ready to record.");
+
+ const button = container.querySelector("button");
+ ok(button, "Selected the button to click.");
+ button.click();
+ is(perfComponent.state.recordingState, "request-to-start-recording",
+ "Sent in a request to start recording.");
+
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "recording",
+ "The actor has started its recording");
+
+ button.click();
+ is(perfComponent.state.recordingState,
+ "request-to-get-profile-and-stop-profiler",
+ "We have requested to stop the profiler.");
+
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "available-to-record",
+ "The profiler is available to record again.");
+ await perfFront.flushAsyncQueue();
+ is(typeof profile, "object", "Got a profile");
+ });
+ </script>
+ </pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/new/test/chrome/test_perf-state-02.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!-- 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/. -->
+<head>
+ <meta charset="utf-8">
+ <title>Perf component test</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+ <div id="container"></div>
+
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+ "use strict";
+
+ /**
+ * Test the perf component when the profiler is already started.
+ */
+ addPerfTest(async () => {
+ const Perf = require("devtools/client/performance/new/components/Perf");
+ const React = require("devtools/client/shared/vendor/react");
+ const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+ const perfFront = new MockPerfFront();
+ const container = document.querySelector("#container");
+
+ ok(true, "Start the profiler before initiliazing the component, to simulate" +
+ "the profiler being controlled by another tool.");
+
+ perfFront.startProfiler();
+ await perfFront.flushAsyncQueue();
+
+ const receiveProfile = () => {};
+ const element = React.createElement(Perf, { perfFront, receiveProfile });
+ const perfComponent = ReactDOM.render(element, container);
+ is(perfComponent.state.recordingState, "not-yet-known",
+ "The component at first is in an unknown state.");
+
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "other-is-recording",
+ "The profiler is not available to record.");
+
+ const button = container.querySelector("button");
+ ok(button, "Selected a button on the component");
+ button.click();
+ is(perfComponent.state.recordingState, "request-to-stop-profiler",
+ "We can request to stop the profiler.");
+
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "available-to-record",
+ "The profiler is now available to record.");
+ });
+ </script>
+ </pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/new/test/chrome/test_perf-state-03.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!-- 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/. -->
+<head>
+ <meta charset="utf-8">
+ <title>Perf component test</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+ <div id="container"></div>
+
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+ "use strict";
+
+ /**
+ * Test the perf component for when the profiler is already started.
+ */
+ addPerfTest(async () => {
+ const Perf = require("devtools/client/performance/new/components/Perf");
+ const React = require("devtools/client/shared/vendor/react");
+ const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+ const perfFront = new MockPerfFront();
+ const container = document.querySelector("#container");
+
+ const receiveProfile = () => {};
+ const element = React.createElement(Perf, { perfFront, receiveProfile });
+ const perfComponent = ReactDOM.render(element, container);
+
+ is(perfComponent.state.recordingState, "not-yet-known",
+ "The component at first is in an unknown state.");
+
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "available-to-record",
+ "After talking to the actor, we're ready to record.");
+
+ document.querySelector("button").click();
+ is(perfComponent.state.recordingState, "request-to-start-recording",
+ "Sent in a request to start recording.");
+
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "recording",
+ "The actor has started its recording");
+
+ ok(true, "Simulate a third party stopping the profiler.");
+ perfFront.stopProfilerAndDiscardProfile();
+ await perfFront.flushAsyncQueue();
+
+ ok(perfComponent.state.recordingUnexpectedlyStopped,
+ "The profiler unexpectedly stopped.");
+ is(perfComponent.state.recordingState, "available-to-record",
+ "However, the profiler is available to record again.");
+ });
+ </script>
+ </pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/new/test/chrome/test_perf-state-04.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!-- 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/. -->
+<head>
+ <meta charset="utf-8">
+ <title>Perf component test</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+ <div id="container"></div>
+
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+ "use strict";
+
+ /**
+ * Test that the profiler gets disabled during private browsing.
+ */
+ addPerfTest(async () => {
+ const Perf = require("devtools/client/performance/new/components/Perf");
+ const React = require("devtools/client/shared/vendor/react");
+ const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+ const perfFront = new MockPerfFront();
+ const container = document.querySelector("#container");
+
+ perfFront.mockIsLocked = true;
+
+ const receiveProfile = () => {};
+ const element = React.createElement(Perf, { perfFront, receiveProfile });
+ const perfComponent = ReactDOM.render(element, container);
+
+ is(perfComponent.state.recordingState, "not-yet-known",
+ "The component at first is in an unknown state.");
+
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "locked-for-private-browsing",
+ "After talking to the actor, it's locked for private browsing.");
+
+ perfFront.mockIsLocked = false;
+ perfFront.emit("profile-unlocked-from-private-browsing");
+
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "available-to-record",
+ "After the profiler is unlocked, it's available to record.");
+
+ document.querySelector("button").click();
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "recording",
+ "The actor has started its recording");
+
+ perfFront.mockIsLocked = true;
+ perfFront.emit("profile-locked-for-private-browsing");
+ await perfFront.flushAsyncQueue();
+ is(perfComponent.state.recordingState, "locked-for-private-browsing",
+ "The recording stops when going into private browsing mode.");
+ });
+ </script>
+ </pre>
+</body>
+</html>
deleted file mode 100644
--- a/devtools/client/performance/new/test/doc_mount_react.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="utf-8">
- <title>Perf Test</title>
- </head>
- <body>
- <div id="root"></div>
- </body>
-</html>
deleted file mode 100644
--- a/devtools/client/performance/new/test/head.js
+++ /dev/null
@@ -1,140 +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";
-
-/* exported addPerfTest */
-/* globals URL_ROOT */
-
-// Load the shared test helpers into this compartment.
-Services.scriptloader.loadSubScript(
- "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
- this);
-
-const EventEmitter = require("devtools/shared/event-emitter");
-const { Hosts } = require("devtools/client/framework/toolbox-hosts");
-const { DOMHelpers } = Cu.import("resource://devtools/client/shared/DOMHelpers.jsm", {});
-const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
-
-/**
- * The perf tests require some common setup. This does that setup, and injects a few
- * common requirements. It is a thin wrapper around add_task().
- */
-function addPerfTest(asyncTestFn) {
- add_task(async () => {
- waitForExplicitFinish();
-
- const [host, window, document] = await createHost(URL_ROOT + "doc_mount_react.html");
- const mountElement = document.querySelector("#root");
- const perfFront = new MockPerfFront();
- const { require: browserRequire } = BrowserLoader({
- baseURI: "resource://devtools/client/performance/new/test/",
- window
- });
-
- // Run the test.
- try {
- await asyncTestFn({
- browserRequire,
- document,
- mountElement,
- perfFront
- });
- } catch (error) {
- console.error(error);
- ok(false, "The test threw an error.");
- }
-
- // Clean up.
- host.destroy();
- finish();
- });
-}
-
-/**
- * The Gecko Profiler is a rather heavy-handed component that uses a lot of resources.
- * In order to get around that, and have quick component tests we provide a mock of
- * the performance front. It also has a method called flushAsyncQueue() that will
- * flush any queued async calls to deterministically run our tests.
- */
-class MockPerfFront extends EventEmitter {
- constructor() {
- super();
- this._isActive = false;
- this._asyncQueue = [];
-
- // Wrap all async methods in a flushable queue, so that tests can control
- // when the responses come back.
- this.isActive = this._wrapInAsyncQueue(this.isActive);
- this.startProfiler = this._wrapInAsyncQueue(this.startProfiler);
- this.stopProfilerAndDiscardProfile = this._wrapInAsyncQueue(
- this.stopProfilerAndDiscardProfile);
- this.getProfileAndStopProfiler = this._wrapInAsyncQueue(
- this.getProfileAndStopProfiler);
- }
-
- /**
- * Provide a flushable queue mechanism for all async work. The work piles up
- * and then is evaluated at once when _flushPendingQueue is called.
- */
- _wrapInAsyncQueue(fn) {
- if (typeof fn !== "function") {
- throw new Error("_wrapInAsyncQueue requires a function");
- }
- return (...args) => {
- return new Promise(resolve => {
- this._asyncQueue.push(() => {
- resolve(fn.apply(this, args));
- });
- });
- };
- }
-
- flushAsyncQueue() {
- const pending = this._asyncQueue;
- this._asyncQueue = [];
- pending.forEach(fn => fn());
- return Promise.resolve();
- }
-
- isActive() {
- return this._isActive;
- }
-
- startProfiler() {
- this._isActive = true;
- // Defer this so it doesn't happen immediately.
- this.emit("profiler-started");
- }
-
- getProfileAndStopProfiler() {
- this._isActive = false;
- // Defer this so it doesn't happen immediately.
- this.emit("profiler-stopped");
- // Return a fake profile.
- return {};
- }
-
- stopProfilerAndDiscardProfile() {
- this._isActive = false;
- // Defer this so it doesn't happen immediately.
- this.emit("profiler-stopped");
- }
-}
-
-/**
- * Create a DevTools host on the bottom of the window with the given URL. This quickly
- * gives us a document that the test can directly manipulate.
- */
-async function createHost(url) {
- const host = new Hosts.bottom(gBrowser.selectedTab); // eslint-disable-line new-cap
- const iframe = await host.create();
-
- await new Promise(resolve => {
- const domHelper = new DOMHelpers(iframe.contentWindow);
- iframe.setAttribute("src", url);
- domHelper.onceDOMReady(resolve);
- });
-
- return [host, iframe.contentWindow, iframe.contentDocument];
-}
--- a/devtools/shared/specs/perf.js
+++ b/devtools/shared/specs/perf.js
@@ -1,16 +1,16 @@
/* 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 { RetVal, generateActorSpec } = require("devtools/shared/protocol");
-const perfSpec = generateActorSpec({
+const perfDescription = {
typeName: "perf",
events: {
"profiler-started": {
type: "profiler-started"
},
"profiler-stopped": {
type: "profiler-stopped"
@@ -52,11 +52,15 @@ const perfSpec = generateActorSpec({
response: { value: RetVal("boolean") }
},
isLockedForPrivateBrowsing: {
request: {},
response: { value: RetVal("boolean") }
}
}
-});
+};
+
+exports.perfDescription = perfDescription;
+
+const perfSpec = generateActorSpec(perfDescription);
exports.perfSpec = perfSpec;