merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-09-28 14:10:50 +02:00
commit 999e802809
146 changed files with 2020 additions and 985 deletions

View File

@ -30,6 +30,7 @@ var TrackingProtection = {
gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip");
this.enabledHistogramAdd(this.enabledGlobally);
this.disabledPBMHistogramAdd(!this.enabledInPrivateWindows);
},
uninit() {
@ -62,6 +63,13 @@ var TrackingProtection = {
Services.telemetry.getHistogramById("TRACKING_PROTECTION_ENABLED").add(value);
},
disabledPBMHistogramAdd(value) {
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
return;
}
Services.telemetry.getHistogramById("TRACKING_PROTECTION_PBM_DISABLED").add(value);
},
eventsHistogramAdd(value) {
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
return;

View File

@ -52,7 +52,7 @@ var gGrid = {
this._createSiteFragment();
gLinks.populateCache(() => {
this.refresh();
this._refreshGrid();
this._ready = true;
// If fetching links took longer than loading the page itself then
@ -108,10 +108,21 @@ var gGrid = {
this.node.removeAttribute("locked");
},
/**
* Renders and resizes the gird. _resizeGrid() call is needed to ensure
* that scrollbar disappears when the bottom row becomes empty following
* the block action, or tile display is turmed off via cog menu
*/
refresh() {
this._refreshGrid();
this._resizeGrid();
},
/**
* Renders the grid, including cells and sites.
*/
refresh() {
_refreshGrid() {
let cell = document.createElementNS(HTML_NAMESPACE, "div");
cell.classList.add("newtab-cell");

View File

@ -50,13 +50,19 @@ Site.prototype = {
/**
* Pins the site on its current or a given index.
* @param aIndex The pinned index (optional).
* @return true if link changed type after pin
*/
pin: function Site_pin(aIndex) {
if (typeof aIndex == "undefined")
aIndex = this.cell.index;
this._updateAttributes(true);
gPinnedLinks.pin(this._link, aIndex);
let changed = gPinnedLinks.pin(this._link, aIndex);
if (changed) {
// render site again to remove suggested/sponsored tags
this._render();
}
return changed;
},
/**
@ -180,6 +186,10 @@ Site.prototype = {
titleNode.style.backgroundColor = this.link.titleBgColor;
}
// remove "suggested" attribute to avoid showing "suggested" tag
// after site was pinned or dropped
this.node.removeAttribute("suggested");
if (this.link.targetedSite) {
if (this.node.getAttribute("type") != "sponsored") {
this._querySelector(".newtab-sponsored").textContent =
@ -370,7 +380,10 @@ Site.prototype = {
action = "unpin";
}
else if (!pinned && target.classList.contains("newtab-control-pin")) {
this.pin();
if (this.pin()) {
// suggested link has changed - update rest of the pages
gAllPages.update(gPage);
}
action = "pin";
}
}

View File

@ -464,11 +464,11 @@ html[dir="rtl"] .settings-menu.dropdown-menu {
align-self: center;
}
.feedback-button-container button {
.feedback-button-container > button {
margin: 0 30px;
padding: .5em 2em;
border: none;
background: #4E92DF;
background: #00A9DC;
color: #fff;
cursor: pointer;
}

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill-rule="evenodd" clip-rule="evenodd" fill="#4E92DF" d="M32 0C14.3 0 0 12.6 0 28.1c0 7.7 3.6 14.7 9.3 19.8-1 3.5-3 8.3-6.9 12.9.7 1.2 11.7-3 19.4-6.1 3.2.9 6.6 1.5 10.2 1.5 17.7 0 32-12.6 32-28.1S49.7 0 32 0zm9.6 16.9c2.3 0 4.2 1.9 4.2 4.2 0 2.3-1.9 4.2-4.2 4.2-2.3 0-4.2-1.9-4.2-4.2-.1-2.3 1.8-4.2 4.2-4.2zm-19.3 0c2.3 0 4.2 1.9 4.2 4.2 0 2.3-1.9 4.2-4.2 4.2-2.3 0-4.2-1.9-4.2-4.2-.1-2.3 1.8-4.2 4.2-4.2zM32 47.7h-.1-.1c-8.6 0-18.1-5.5-20.3-14.9 5.8 2.7 13.8 3.8 20.4 3.8 6.6 0 14.7-1.2 20.4-3.8-2.2 9.3-11.7 14.9-20.3 14.9z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill-rule="evenodd" clip-rule="evenodd" fill="#00A9DC" d="M32 0C14.3 0 0 12.6 0 28.1c0 7.7 3.6 14.7 9.3 19.8-1 3.5-3 8.3-6.9 12.9.7 1.2 11.7-3 19.4-6.1 3.2.9 6.6 1.5 10.2 1.5 17.7 0 32-12.6 32-28.1S49.7 0 32 0zm9.6 16.9c2.3 0 4.2 1.9 4.2 4.2 0 2.3-1.9 4.2-4.2 4.2-2.3 0-4.2-1.9-4.2-4.2-.1-2.3 1.8-4.2 4.2-4.2zm-19.3 0c2.3 0 4.2 1.9 4.2 4.2 0 2.3-1.9 4.2-4.2 4.2-2.3 0-4.2-1.9-4.2-4.2-.1-2.3 1.8-4.2 4.2-4.2zM32 47.7h-.2c-8.6 0-18.1-5.5-20.3-14.9 5.8 2.7 13.8 3.8 20.4 3.8 6.6 0 14.7-1.2 20.4-3.8-2.2 9.3-11.7 14.9-20.3 14.9z"/></svg>

Before

Width:  |  Height:  |  Size: 602 B

After

Width:  |  Height:  |  Size: 599 B

View File

@ -38,13 +38,9 @@ loop.OTSdkDriver = (function() {
this.connections = {};
// Metrics object to keep track of the number of connections we have
// Setup the metrics object to keep track of the number of connections we have
// and the amount of streams.
this._metrics = {
connections: 0,
sendStreams: 0,
recvStreams: 0
};
this._resetMetrics();
this.dispatcher.register(this, [
"setupStreamElements",
@ -108,6 +104,17 @@ loop.OTSdkDriver = (function() {
};
},
/**
* Resets the metrics for the driver.
*/
_resetMetrics: function() {
this._metrics = {
connections: 0,
sendStreams: 0,
recvStreams: 0
};
},
/**
* Handles the setupStreamElements action. Saves the required data and
* kicks off the initialising of the publisher.
@ -293,6 +300,9 @@ loop.OTSdkDriver = (function() {
delete this.publisher;
}
// Now reset the metrics as well.
this._resetMetrics();
this._noteConnectionLengthIfNeeded(this._getTwoWayMediaStartTime(), performance.now());
// Also, tidy these variables ready for next time.

View File

@ -113,6 +113,19 @@ describe("loop.OTSdkDriver", function () {
new loop.OTSdkDriver({dispatcher: dispatcher});
}).to.Throw(/sdk/);
});
it("should set the metrics to zero", function() {
driver = new loop.OTSdkDriver({
dispatcher: dispatcher,
sdk: sdk
});
expect(driver._metrics).eql({
connections: 0,
sendStreams: 0,
recvStreams: 0
});
});
});
describe("#setupStreamElements", function() {
@ -474,6 +487,22 @@ describe("loop.OTSdkDriver", function () {
expect(subscribedEvents).eql([]);
});
it("should reset the metrics to zero", function() {
driver._metrics = {
connections: 1,
sendStreams: 2,
recvStreams: 3
};
driver.disconnectSession();
expect(driver._metrics).eql({
connections: 0,
sendStreams: 0,
recvStreams: 0
});
});
it("should dispatch a DataChannelsAvailable action with available = false", function() {
driver.disconnectSession();

View File

@ -27,6 +27,7 @@
var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
var OngoingConversationView = loop.conversationViews.OngoingConversationView;
var DirectCallFailureView = loop.conversationViews.DirectCallFailureView;
var DesktopRoomEditContextView = loop.roomViews.DesktopRoomEditContextView;
var RoomFailureView = loop.roomViews.RoomFailureView;
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
@ -1395,6 +1396,23 @@
)
),
React.createElement(FramedExample, {height: 278.6,
onContentsRendered: invitationRoomStore.activeRoomStore.forcedUpdate,
summary: "Desktop room Edit Context w/Error",
width: 298},
React.createElement("div", {className: "fx-embedded room-invitation-overlay"},
React.createElement(DesktopRoomEditContextView, {
dispatcher: dispatcher,
error: {},
mozLoop: navigator.mozLoop,
onClose: function(){},
roomData: {},
savingContext: false,
show: true}
)
)
),
React.createElement(FramedExample, {dashed: true,
height: 394,
onContentsRendered: desktopRoomStoreLoading.activeRoomStore.forcedUpdate,

View File

@ -27,6 +27,7 @@
var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
var OngoingConversationView = loop.conversationViews.OngoingConversationView;
var DirectCallFailureView = loop.conversationViews.DirectCallFailureView;
var DesktopRoomEditContextView = loop.roomViews.DesktopRoomEditContextView;
var RoomFailureView = loop.roomViews.RoomFailureView;
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
@ -1395,6 +1396,23 @@
</div>
</FramedExample>
<FramedExample height={278.6}
onContentsRendered={invitationRoomStore.activeRoomStore.forcedUpdate}
summary="Desktop room Edit Context w/Error"
width={298}>
<div className="fx-embedded room-invitation-overlay">
<DesktopRoomEditContextView
dispatcher={dispatcher}
error={{}}
mozLoop={navigator.mozLoop}
onClose={function(){}}
roomData={{}}
savingContext={false}
show={true}
/>
</div>
</FramedExample>
<FramedExample dashed={true}
height={394}
onContentsRendered={desktopRoomStoreLoading.activeRoomStore.forcedUpdate}

View File

@ -9,6 +9,7 @@ var gContentWindow;
function test() {
UITourTest();
requestLongerTimeout(2);
}
function getHeartbeatNotification(aId, aChromeWindow = window) {

View File

@ -251,6 +251,7 @@ function UITourTest() {
});
function done() {
info("== Done test, doing shared checks before teardown ==");
executeSoon(() => {
if (gTestTab)
gBrowser.removeTab(gTestTab);
@ -267,12 +268,14 @@ function UITourTest() {
isnot(PanelUI.panel.state, "open", "The panel shouldn't be open");
is(document.getElementById("PanelUI-menu-button").hasAttribute("open"), false, "Menu button should know that the menu is closed");
info("Done shared checks");
executeSoon(nextTest);
});
}
function nextTest() {
if (tests.length == 0) {
info("finished tests in this file");
finish();
return;
}

View File

@ -35,7 +35,12 @@
<!ENTITY performanceUI.bufferStatusFull "The buffer is full. Older samples are now being overwritten.">
<!-- LOCALIZATION NOTE (performanceUI.loadingNotice): This is the label shown
- in the call list view while loading a profile. -->
- in the details view while the profiler is unavailable, for example, while
- in Private Browsing mode. -->
<!ENTITY performanceUI.unavailableNoticePB "Recording a profile is currently unavailable. Please close all private browsing windows and try again.">
<!-- LOCALIZATION NOTE (performanceUI.loadingNotice): This is the label shown
- in the details view while loading a profile. -->
<!ENTITY performanceUI.loadingNotice "Loading…">
<!-- LOCALIZATION NOTE (performanceUI.recordButton): This string is displayed

View File

@ -437,9 +437,6 @@
currentColor calc(100% - 4px),
transparent calc(100% - 4px));
opacity: 0.2;
content: "";
display: -moz-box;
visibility: hidden;
}
#TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-tab::before,
@ -451,7 +448,8 @@
#tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
.tabbrowser-tab:not([visuallyselected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([visuallyselected]):not([beforehovered]):not(:hover)::after {
visibility: visible;
content: "";
display: -moz-box;
}
/* New tab button */

View File

@ -317,10 +317,28 @@ TabTarget.prototype = {
return this._form;
},
// Get a promise of the root form returned by a listTabs request. This promise
// is cached.
get root() {
if (!this._root) {
this._root = this._getRoot();
}
return this._root;
},
_getRoot: function () {
return new Promise((resolve, reject) => {
this.client.listTabs(response => {
if (response.error) {
reject(new Error(response.error + ": " + response.message));
return;
}
resolve(response);
});
});
},
get client() {
return this._client;
},

View File

@ -3,12 +3,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This shared-head.js file is used for multiple directories in devtools.
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
const {gDevTools} = Cu.import("resource:///modules/devtools/client/framework/gDevTools.jsm", {});
const {console} = Cu.import("resource://gre/modules/devtools/shared/Console.jsm", {});
const {ScratchpadManager} = Cu.import("resource:///modules/devtools/client/scratchpad/scratchpad-manager.jsm", {});
const {require} = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
function scopedCuImport(path) {
const scope = {};
Cu.import(path, scope);
return scope;
}
const {Services} = scopedCuImport("resource://gre/modules/Services.jsm");
const {gDevTools} = scopedCuImport("resource:///modules/devtools/client/framework/gDevTools.jsm");
const {console} = scopedCuImport("resource://gre/modules/devtools/shared/Console.jsm");
const {ScratchpadManager} = scopedCuImport("resource:///modules/devtools/client/scratchpad/scratchpad-manager.jsm");
const {require} = scopedCuImport("resource://gre/modules/devtools/shared/Loader.jsm");
const {TargetFactory} = require("devtools/client/framework/target");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const promise = require("promise");
@ -69,6 +78,25 @@ function addTab(url) {
return def.promise;
}
/**
* Remove the given tab.
* @param {Object} tab The tab to be removed.
* @return Promise<undefined> resolved when the tab is successfully removed.
*/
function removeTab(tab) {
info("Removing tab.");
return new Promise(resolve => {
let tabContainer = gBrowser.tabContainer;
tabContainer.addEventListener("TabClose", function onClose(aEvent) {
tabContainer.removeEventListener("TabClose", onClose, false);
info("Tab removed and finished closing.");
resolve();
}, false);
gBrowser.removeTab(tab);
});
}
function synthesizeKeyFromKeyTag(aKeyId, document) {
let key = document.getElementById(aKeyId);
isnot(key, null, "Successfully retrieved the <key> node");

View File

@ -39,32 +39,32 @@ devtools.jar:
content/animationinspector/animation-controller.js (animationinspector/animation-controller.js)
content/animationinspector/animation-panel.js (animationinspector/animation-panel.js)
content/animationinspector/animation-inspector.xhtml (animationinspector/animation-inspector.xhtml)
content/sourceeditor/codemirror/codemirror.js (sourceeditor/codemirror/codemirror.js)
content/sourceeditor/codemirror/codemirror.css (sourceeditor/codemirror/codemirror.css)
content/sourceeditor/codemirror/comment/comment.js (sourceeditor/codemirror/addon/comment/comment.js)
content/sourceeditor/codemirror/edit/trailingspace.js (sourceeditor/codemirror/addon/edit/trailingspace.js)
content/sourceeditor/codemirror/edit/matchbrackets.js (sourceeditor/codemirror/addon/edit/matchbrackets.js)
content/sourceeditor/codemirror/edit/closebrackets.js (sourceeditor/codemirror/addon/edit/closebrackets.js)
content/sourceeditor/codemirror/dialog/dialog.js (sourceeditor/codemirror/addon/dialog/dialog.js)
content/sourceeditor/codemirror/dialog/dialog.css (sourceeditor/codemirror/addon/dialog/dialog.css)
content/sourceeditor/codemirror/fold/foldcode.js (sourceeditor/codemirror/addon/fold/foldcode.js)
content/sourceeditor/codemirror/fold/brace-fold.js (sourceeditor/codemirror/addon/fold/brace-fold.js)
content/sourceeditor/codemirror/fold/comment-fold.js (sourceeditor/codemirror/addon/fold/comment-fold.js)
content/sourceeditor/codemirror/fold/xml-fold.js (sourceeditor/codemirror/addon/fold/xml-fold.js)
content/sourceeditor/codemirror/fold/foldgutter.js (sourceeditor/codemirror/addon/fold/foldgutter.js)
content/sourceeditor/codemirror/hint/show-hint.js (sourceeditor/codemirror/addon/hint/show-hint.js)
content/sourceeditor/codemirror/search/search.js (sourceeditor/codemirror/addon/search/search.js)
content/sourceeditor/codemirror/search/searchcursor.js (sourceeditor/codemirror/addon/search/searchcursor.js)
content/sourceeditor/codemirror/selection/active-line.js (sourceeditor/codemirror/addon/selection/active-line.js)
content/sourceeditor/codemirror/tern/tern.js (sourceeditor/codemirror/addon/tern/tern.js)
content/sourceeditor/codemirror/codemirror.js (sourceeditor/codemirror/lib/codemirror.js)
content/sourceeditor/codemirror/codemirror.css (sourceeditor/codemirror/lib/codemirror.css)
content/sourceeditor/codemirror/mode/javascript.js (sourceeditor/codemirror/mode/javascript.js)
content/sourceeditor/codemirror/mode/xml.js (sourceeditor/codemirror/mode/xml.js)
content/sourceeditor/codemirror/mode/css.js (sourceeditor/codemirror/mode/css.js)
content/sourceeditor/codemirror/mode/htmlmixed.js (sourceeditor/codemirror/mode/htmlmixed.js)
content/sourceeditor/codemirror/mode/clike.js (sourceeditor/codemirror/mode/clike.js)
content/sourceeditor/codemirror/selection/active-line.js (sourceeditor/codemirror/selection/active-line.js)
content/sourceeditor/codemirror/edit/trailingspace.js (sourceeditor/codemirror/edit/trailingspace.js)
content/sourceeditor/codemirror/edit/matchbrackets.js (sourceeditor/codemirror/edit/matchbrackets.js)
content/sourceeditor/codemirror/edit/closebrackets.js (sourceeditor/codemirror/edit/closebrackets.js)
content/sourceeditor/codemirror/comment/comment.js (sourceeditor/codemirror/comment/comment.js)
content/sourceeditor/codemirror/search/searchcursor.js (sourceeditor/codemirror/search/searchcursor.js)
content/sourceeditor/codemirror/search/search.js (sourceeditor/codemirror/search/search.js)
content/sourceeditor/codemirror/dialog/dialog.js (sourceeditor/codemirror/dialog/dialog.js)
content/sourceeditor/codemirror/dialog/dialog.css (sourceeditor/codemirror/dialog/dialog.css)
content/sourceeditor/codemirror/keymap/emacs.js (sourceeditor/codemirror/keymap/emacs.js)
content/sourceeditor/codemirror/keymap/sublime.js (sourceeditor/codemirror/keymap/sublime.js)
content/sourceeditor/codemirror/keymap/vim.js (sourceeditor/codemirror/keymap/vim.js)
content/sourceeditor/codemirror/fold/foldcode.js (sourceeditor/codemirror/fold/foldcode.js)
content/sourceeditor/codemirror/fold/brace-fold.js (sourceeditor/codemirror/fold/brace-fold.js)
content/sourceeditor/codemirror/fold/comment-fold.js (sourceeditor/codemirror/fold/comment-fold.js)
content/sourceeditor/codemirror/fold/xml-fold.js (sourceeditor/codemirror/fold/xml-fold.js)
content/sourceeditor/codemirror/fold/foldgutter.js (sourceeditor/codemirror/fold/foldgutter.js)
content/sourceeditor/codemirror/tern/tern.js (sourceeditor/codemirror/tern/tern.js)
content/sourceeditor/codemirror/hint/show-hint.js (sourceeditor/codemirror/hint/show-hint.js)
content/sourceeditor/codemirror/mozilla.css (sourceeditor/codemirror/mozilla.css)
content/debugger/debugger.xul (debugger/debugger.xul)
content/debugger/debugger.css (debugger/debugger.css)
@ -111,7 +111,7 @@ devtools.jar:
content/performance/views/optimizations-list.js (performance/views/optimizations-list.js)
content/performance/views/recordings.js (performance/views/recordings.js)
content/memory/memory.xhtml (memory/memory.xhtml)
content/memory/controller.js (memory/controller.js)
content/memory/initializer.js (memory/initializer.js)
content/promisedebugger/promise-controller.js (promisedebugger/promise-controller.js)
content/promisedebugger/promise-panel.js (promisedebugger/promise-panel.js)
content/promisedebugger/promise-debugger.xhtml (promisedebugger/promise-debugger.xhtml)

View File

@ -1,5 +1,8 @@
# vim: set filetype=python:
# 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/.
general=General Notification
DevToolsModules(
'snapshot.js',
)

View File

@ -0,0 +1,14 @@
/* 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 { PROMISE } = require("devtools/client/shared/redux/middleware/promise");
const { actions } = require("../constants");
const takeSnapshot = exports.takeSnapshot = function takeSnapshot (front) {
return {
type: actions.TAKE_SNAPSHOT,
[PROMISE]: front.saveHeapSnapshot()
};
};

View File

@ -0,0 +1,9 @@
/* 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 actions = exports.actions = {};
// Fired by UI to request a snapshot from the actor.
actions.TAKE_SNAPSHOT = "take-snapshot";

View File

@ -3,26 +3,26 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
const { loader, require } = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
const { Task } = require("resource://gre/modules/Task.jsm");
const { Heritage, ViewHelpers, WidgetMethods } = require("resource:///modules/devtools/client/shared/widgets/ViewHelpers.jsm");
const Store = require("./store");
/**
* The current target, toolbox and MemoryFront, set by this tool's host.
*/
var gToolbox, gTarget, gFront;
/**
* Initializes the profiler controller and views.
*/
const MemoryController = {
initialize: Task.async(function *() {
const REDUX_METHODS_TO_PIPE = ["dispatch", "subscribe", "getState"];
}),
destroy: Task.async(function *() {
})
const MemoryController = exports.MemoryController = function ({ toolbox, target, front }) {
this.store = Store();
this.toolbox = toolbox;
this.target = target;
this.front = front;
};
REDUX_METHODS_TO_PIPE.map(m =>
MemoryController.prototype[m] = function (...args) { return this.store[m](...args); });
MemoryController.prototype.destroy = function () {
this.store = this.toolbox = this.target = this.front = null;
};

View File

@ -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";
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const { Task } = require("resource://gre/modules/Task.jsm");
const { MemoryController } = require("devtools/client/memory/controller");
/**
* The current target, toolbox and MemoryFront, set by this tool's host.
*/
let gToolbox, gTarget, gFront;
/**
* Initializes the profiler controller and views.
*/
var controller = null;
function initialize () {
return Task.spawn(function *() {
controller = new MemoryController({ toolbox: gToolbox, target: gTarget, front: gFront });
});
}
function destroy () {
return Task.spawn(function *() {
controller.destroy();
});
}

View File

@ -12,23 +12,21 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" href="chrome://browser/skin/" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/themes/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/themes/widgets.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/themes/memory.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/content/devtools/widgets.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/widgets.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/memory.css" type="text/css"/>
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"></script>
src="chrome://devtools/content/shared/theme-switching.js"/>
<script type="application/javascript;version=1.8"
src="controller.js"></script>
src="initializer.js"></script>
</head>
<body class="theme-body">
<toolbar class="devtools-toolbar">
<toolbarbutton id="snapshot-button" class="devtools-toolbarbutton"
tabindex="0"/>
<spacer flex="1"></spacer>
</toolbar>
<splitter class="devtools-horizontal-splitter"/>
<div class="devtools-toolbar">
<div id="snapshot-button" class="devtools-toolbarbutton" />
</div>
<div class="devtools-horizontal-splitter"></div>
<div id="memory-content"
class="devtools-responsive-container"
flex="1">

View File

@ -9,7 +9,6 @@
*/
const { Cc, Ci, Cu, Cr } = require("chrome");
const { L10N } = require("devtools/client/performance/modules/global");
const { Heritage } = require("resource:///modules/devtools/client/shared/widgets/ViewHelpers.jsm");
const { AbstractTreeItem } = require("resource:///modules/devtools/client/shared/widgets/AbstractTreeItem.jsm");

View File

@ -4,11 +4,19 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += [
'actions',
'modules',
'reducers',
]
DevToolsModules(
'constants.js',
'controller.js',
'initializer.js',
'panel.js',
'reducers.js',
'store.js',
)
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']

View File

@ -26,14 +26,19 @@ MemoryPanel.prototype = {
this.panelWin.gToolbox = this._toolbox;
this.panelWin.gTarget = this.target;
this.panelWin.gFront = new MemoryFront(this.target.client, this.target.form);
console.log(this.panelWin, this.panelWin.MemoryController);
return this._opening = this.panelWin.MemoryController.initialize().then(() => {
const rootForm = yield this.target.root;
this.panelWin.gFront = new MemoryFront(this.target.client,
this.target.form,
rootForm);
yield this.panelWin.gFront.attach();
return this._opening = this.panelWin.initialize().then(() => {
this.isReady = true;
this.emit("ready");
return this;
});
return this._opening;
}),
// DevToolPanel API
@ -42,19 +47,21 @@ MemoryPanel.prototype = {
return this._toolbox.target;
},
destroy: function () {
destroy: Task.async(function *() {
// Make sure this panel is not already destroyed.
if (this._destroyer) {
return this._destroyer;
}
return this._destroyer = this.panelWin.MemoryController.destroy().then(() => {
yield this.panelWin.gFront.detach();
return this._destroyer = this.panelWin.destroy().then(() => {
// Destroy front to ensure packet handler is removed from client
this.panelWin.gFront.destroy();
this.panelWin = null;
this.emit("destroyed");
return this;
});
}
})
};
exports.MemoryPanel = MemoryPanel;

View File

@ -0,0 +1 @@
exports.snapshots = require("./reducers/snapshot");

View File

@ -0,0 +1,8 @@
# vim: set filetype=python:
# 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/.
DevToolsModules(
'snapshot.js',
)

View File

@ -0,0 +1,37 @@
const { actions } = require("../constants");
const { PROMISE } = require("devtools/client/shared/redux/middleware/promise");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
function handleTakeSnapshot (state, action) {
switch (action.status) {
case "start":
return [...state, {
id: action.seqId,
status: action.status
}];
case "done":
let snapshot = state.find(s => s.id === action.seqId);
if (!snapshot) {
DevToolsUtils.reportException(`No snapshot with id "${action.seqId}" for TAKE_SNAPSHOT`);
break;
}
snapshot.status = "done";
snapshot.snapshotId = action.value;
return [...state];
case "error":
DevToolsUtils.reportException(`No async state found for ${action.type}`);
}
return [...state];
}
module.exports = function (state=[], action) {
switch (action.type) {
case actions.TAKE_SNAPSHOT:
return handleTakeSnapshot(state, action);
}
return state;
};

View File

@ -0,0 +1,8 @@
const { combineReducers } = require("../shared/vendor/redux");
const createStore = require("../shared/redux/create-store");
const reducers = require("./reducers");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
module.exports = function () {
return createStore({ log: DevToolsUtils.testing })(combineReducers(reducers), {});
};

View File

@ -0,0 +1,7 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
[browser_memory_transferHeapSnapshot_e10s_01.js]

View File

@ -0,0 +1,28 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that we can save a heap snapshot and transfer it over the RDP in e10s
// where the child process is sandboxed and so we have to use
// HeapSnapshotFileActor to get the heap snapshot file.
"use strict";
const TEST_URL = "data:text/html,<html><body></body></html>";
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const memoryFront = panel.panelWin.gFront;
ok(memoryFront, "Should get the MemoryFront");
const snapshotFilePath = yield memoryFront.saveHeapSnapshot({
// Force a copy so that we go through the HeapSnapshotFileActor's
// transferHeapSnapshot request and exercise this code path on e10s.
forceCopy: true
});
ok(!!(yield OS.File.stat(snapshotFilePath)),
"Should have the heap snapshot file");
const snapshot = ChromeUtils.readHeapSnapshot(snapshotFilePath);
ok(snapshot instanceof HeapSnapshot,
"And we should be able to read a HeapSnapshot instance from the file");
});

View File

@ -0,0 +1,65 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Load the shared test helpers into this compartment.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
Services.prefs.setBoolPref("devtools.memory.enabled", true);
/**
* Open the memory panel for the given tab.
*/
this.openMemoryPanel = Task.async(function* (tab) {
info("Opening memory panel.");
const target = TargetFactory.forTab(tab);
const toolbox = yield gDevTools.showToolbox(target, "memory");
info("Memory panel shown successfully.");
let panel = toolbox.getCurrentPanel();
return { tab, panel };
});
/**
* Close the memory panel for the given tab.
*/
this.closeMemoryPanel = Task.async(function* (tab) {
info("Closing memory panel.");
const target = TargetFactory.forTab(tab);
const toolbox = gDevTools.getToolbox(target);
yield toolbox.destroy();
info("Closed memory panel successfully.");
});
/**
* Return a test function that adds a tab with the given url, opens the memory
* panel, runs the given generator, closes the memory panel, removes the tab,
* and finishes.
*
* Example usage:
*
* this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
* // Your tests go here...
* });
*/
function makeMemoryTest(url, generator) {
return Task.async(function* () {
waitForExplicitFinish();
const tab = yield addTab(url);
const results = yield openMemoryPanel(tab);
try {
yield* generator(results);
} catch (err) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(err));
}
yield closeMemoryPanel(tab);
yield removeTab(tab);
finish();
});
}

View File

@ -9,4 +9,4 @@ const Cu = Components.utils;
const Cr = Components.results;
const CC = Components.Constructor;
const { require } = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});

View File

@ -7,11 +7,11 @@ Bug 1067491 - Test taking a census over the RDP.
<meta charset="utf-8">
<title>Census Tree 01</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css" />
<link href="chrome://devtools/skin/themes/light-theme.css" type="text/css" />
<link href="chrome://devtools/skin/themes/common.css" type="text/css" />
<link href="chrome://devtools/skin/themes/widgets.css" type="text/css" />
<link href="chrome://devtools/skin/themes/memory.css" type="text/css" />
<link href="chrome://browser/content/devtools/widgets.css" type="text/css" />
<link href="chrome://browser/skin/devtools/light-theme.css" type="text/css" />
<link href="chrome://browser/skin/devtools/common.css" type="text/css" />
<link href="chrome://browser/skin/devtools/widgets.css" type="text/css" />
<link href="chrome://browser/skin/devtools/memory.css" type="text/css" />
</head>
<body>
<ul id="container" style="width:100%;height:300px;"></ul>

View File

@ -0,0 +1,56 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
var { gDevTools } = Cu.import("resource:///modules/devtools/client/framework/gDevTools.jsm", {});
var { console } = Cu.import("resource://gre/modules/devtools/shared/Console.jsm", {});
var { require } = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
var { TargetFactory } = require("devtools/client/framework/target");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var promise = require("promise");
var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
var { MemoryController } = require("devtools/client/memory/controller");
var { expectState } = require("devtools/server/actors/common");
var HeapSnapshotFileUtils = require("devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
var { addDebuggerToGlobal } = require("resource://gre/modules/jsdebugger.jsm");
var SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
var { setTimeout } = require("sdk/timers");
DevToolsUtils.testing = true;
function initDebugger () {
let global = new Cu.Sandbox(SYSTEM_PRINCIPAL, { freshZone: true });
addDebuggerToGlobal(global);
return new global.Debugger();
}
function StubbedMemoryFront () {
this.dbg = initDebugger();
}
StubbedMemoryFront.prototype.attach = Task.async(function *() {
this.state = "attached";
});
StubbedMemoryFront.prototype.detach = Task.async(function *() {
this.state = "detached";
});
StubbedMemoryFront.prototype.saveHeapSnapshot = expectState("attached", Task.async(function *() {
let path = ThreadSafeChromeUtils.saveHeapSnapshot({ debugger: this.dbg });
return HeapSnapshotFileUtils.getSnapshotIdFromPath(path);
}), "saveHeapSnapshot");
function waitUntilState (store, predicate) {
let deferred = promise.defer();
let unsubscribe = store.subscribe(() => {
if (predicate(store.getState())) {
unsubscribe();
deferred.resolve()
}
});
return deferred.promise;
}

View File

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests the async action creator `takeSnapshot(front)`
*/
let actions = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
yield front.attach();
let controller = new MemoryController({ toolbox: {}, target: {}, front });
let unsubscribe = controller.subscribe(checkState);
let foundPendingState = false;
let foundDoneState = false;
function checkState () {
let state = controller.getState();
if (state.snapshots.length === 1 && state.snapshots[0].status === "start") {
foundPendingState = true;
ok(foundPendingState, "Got state change for pending heap snapshot request");
ok(!(state.snapshots[0].snapshotId), "Snapshot does not yet have a snapshotId");
}
if (state.snapshots.length === 1 && state.snapshots[0].status === "done") {
foundDoneState = true;
ok(foundDoneState, "Got state change for completed heap snapshot request");
ok(state.snapshots[0].snapshotId, "Snapshot fetched with a snapshotId");
}
if (state.snapshots.lenght === 1 && state.snapshots[0].status === "error") {
ok(false, "takeSnapshot's promise returned with an error");
}
}
controller.dispatch(actions.takeSnapshot(front));
yield waitUntilState(controller, () => foundPendingState && foundDoneState);
unsubscribe();
});

View File

@ -0,0 +1,8 @@
[DEFAULT]
tags = devtools
head = head.js
tail =
firefox-appdir = browser
skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_action-take-snapshot.js]

View File

@ -30,6 +30,9 @@ module.exports = {
// When a new recording is being tracked in the panel.
NEW_RECORDING: "Performance:NewRecording",
// When a new recording can't be successfully created when started.
NEW_RECORDING_FAILED: "Performance:NewRecordingFailed",
// When a recording is started or stopped or stopping via the PerformanceController
RECORDING_STATE_CHANGE: "Performance:RecordingStateChange",

View File

@ -89,6 +89,7 @@ var gToolbox, gTarget, gFront;
var startupPerformance = Task.async(function*() {
yield PerformanceController.initialize();
yield PerformanceView.initialize();
PerformanceController.enableFrontEventListeners();
});
/**
@ -97,6 +98,7 @@ var startupPerformance = Task.async(function*() {
var shutdownPerformance = Task.async(function*() {
yield PerformanceController.destroy();
yield PerformanceView.destroy();
PerformanceController.disableFrontEventListeners();
});
/**
@ -131,7 +133,6 @@ var PerformanceController = {
this._prefs = require("devtools/client/performance/modules/global").PREFS;
this._prefs.on("pref-changed", this._onPrefChanged);
gFront.on("*", this._onFrontEvent);
ToolbarView.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
@ -151,7 +152,6 @@ var PerformanceController = {
this._telemetry.destroy();
this._prefs.off("pref-changed", this._onPrefChanged);
gFront.off("*", this._onFrontEvent);
ToolbarView.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
@ -164,6 +164,27 @@ var PerformanceController = {
gDevTools.off("pref-changed", this._onThemeChanged);
},
/**
* Enables front event listeners.
*
* The rationale behind this is given by the async intialization of all the
* frontend components. Even though the panel is considered "open" only after
* both the controller and the view are created, and even though their
* initialization is sequential (controller, then view), the controller might
* start handling backend events before the view finishes if the event
* listeners are added too soon.
*/
enableFrontEventListeners: function() {
gFront.on("*", this._onFrontEvent);
},
/**
* Disables front event listeners.
*/
disableFrontEventListeners: function() {
gFront.off("*", this._onFrontEvent);
},
/**
* Returns the current devtools theme.
*/
@ -205,6 +226,27 @@ var PerformanceController = {
this._prefs[prefName] = prefValue;
},
/**
* Checks whether or not a new recording is supported by the PerformanceFront.
* @return Promise:boolean
*/
canCurrentlyRecord: Task.async(function*() {
// If we're testing the legacy front, the performance actor will exist,
// with `canCurrentlyRecord` method; this ensures we test the legacy path.
if (gFront.LEGACY_FRONT) {
return true;
}
let hasActor = yield gTarget.hasActor("performance");
if (!hasActor) {
return true;
}
let actorCanCheck = yield gTarget.actorHasMethod("performance", "canCurrentlyRecord");
if (!actorCanCheck) {
return true;
}
return (yield gFront.canCurrentlyRecord()).success;
}),
/**
* Starts recording with the PerformanceFront.
*/
@ -221,7 +263,13 @@ var PerformanceController = {
sampleFrequency: this.getPref("profiler-sample-frequency")
};
yield gFront.startRecording(options);
// In some cases, like when the target has a private browsing tab,
// recording is not currently supported because of the profiler module.
// Present a notification in this case alerting the user of this issue.
if (!(yield gFront.startRecording(options))) {
this.emit(EVENTS.NEW_RECORDING_FAILED);
PerformanceView.setState("unavailable");
}
}),
/**

View File

@ -14,27 +14,30 @@ var PerformanceView = {
// that the server has support for determining buffer status.
_bufferStatusSupported: false,
// Mapping of state to selectors for different panes
// of the main profiler view. Used in `PerformanceView.setState()`
// Mapping of state to selectors for different properties and their values,
// from the main profiler view. Used in `PerformanceView.setState()`
states: {
empty: [
{ deck: "#performance-view", pane: "#empty-notice" }
"unavailable": [
{ sel: "#performance-view", opt: "selectedPanel", val: () => $("#unavailable-notice") },
],
recording: [
{ deck: "#performance-view", pane: "#performance-view-content" },
{ deck: "#details-pane-container", pane: "#recording-notice" }
"empty": [
{ sel: "#performance-view", opt: "selectedPanel", val: () => $("#empty-notice") }
],
"recording": [
{ sel: "#performance-view", opt: "selectedPanel", val: () => $("#performance-view-content") },
{ sel: "#details-pane-container", opt: "selectedPanel", val: () => $("#recording-notice") }
],
"console-recording": [
{ deck: "#performance-view", pane: "#performance-view-content" },
{ deck: "#details-pane-container", pane: "#console-recording-notice" }
{ sel: "#performance-view", opt: "selectedPanel", val: () => $("#performance-view-content") },
{ sel: "#details-pane-container", opt: "selectedPanel", val: () => $("#console-recording-notice") }
],
recorded: [
{ deck: "#performance-view", pane: "#performance-view-content" },
{ deck: "#details-pane-container", pane: "#details-pane" }
"recorded": [
{ sel: "#performance-view", opt: "selectedPanel", val: () => $("#performance-view-content") },
{ sel: "#details-pane-container", opt: "selectedPanel", val: () => $("#details-pane") }
],
loading: [
{ deck: "#performance-view", pane: "#performance-view-content" },
{ deck: "#details-pane-container", pane: "#loading-notice" }
"loading": [
{ sel: "#performance-view", opt: "selectedPanel", val: () => $("#performance-view-content") },
{ sel: "#details-pane-container", opt: "selectedPanel", val: () => $("#loading-notice") }
]
},
@ -52,6 +55,7 @@ var PerformanceView = {
this._onRecordingSelected = this._onRecordingSelected.bind(this);
this._onProfilerStatusUpdated = this._onProfilerStatusUpdated.bind(this);
this._onRecordingStateChange = this._onRecordingStateChange.bind(this);
this._onNewRecordingFailed = this._onNewRecordingFailed.bind(this);
for (let button of $$(".record-button")) {
button.addEventListener("click", this._onRecordButtonClick);
@ -64,8 +68,13 @@ var PerformanceView = {
PerformanceController.on(EVENTS.PROFILER_STATUS_UPDATED, this._onProfilerStatusUpdated);
PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
PerformanceController.on(EVENTS.NEW_RECORDING, this._onRecordingStateChange);
PerformanceController.on(EVENTS.NEW_RECORDING_FAILED, this._onNewRecordingFailed);
this.setState("empty");
if (yield PerformanceController.canCurrentlyRecord()) {
this.setState("empty");
} else {
this.setState("unavailable");
}
// Initialize the ToolbarView first, because other views may need access
// to the OptionsView via the controller, to read prefs.
@ -89,6 +98,7 @@ var PerformanceView = {
PerformanceController.off(EVENTS.PROFILER_STATUS_UPDATED, this._onProfilerStatusUpdated);
PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
PerformanceController.off(EVENTS.NEW_RECORDING, this._onRecordingStateChange);
PerformanceController.off(EVENTS.NEW_RECORDING_FAILED, this._onNewRecordingFailed);
yield ToolbarView.destroy();
yield RecordingsView.destroy();
@ -97,16 +107,18 @@ var PerformanceView = {
}),
/**
* Sets the state of the profiler view. Possible options are "empty",
* "recording", "console-recording", "recorded".
* Sets the state of the profiler view. Possible options are "unavailable",
* "empty", "recording", "console-recording", "recorded".
*/
setState: function (state) {
let viewConfig = this.states[state];
if (!viewConfig) {
throw new Error(`Invalid state for PerformanceView: ${state}`);
}
for (let { deck, pane } of viewConfig) {
$(deck).selectedPanel = $(pane);
for (let { sel, opt, val } of viewConfig) {
for (let el of $$(sel)) {
el[opt] = val();
}
}
this._state = state;
@ -114,6 +126,7 @@ var PerformanceView = {
if (state === "console-recording") {
let recording = PerformanceController.getCurrentRecording();
let label = recording.getLabel() || "";
// Wrap the label in quotes if it exists for the commands.
label = label ? `"${label}"` : "";
@ -192,7 +205,7 @@ var PerformanceView = {
*
* @param {boolean} activate
*/
_activateRecordButtons: function (activate) {
_toggleRecordButtons: function (activate) {
for (let button of $$(".record-button")) {
if (activate) {
button.setAttribute("checked", "true");
@ -209,7 +222,7 @@ var PerformanceView = {
let currentRecording = PerformanceController.getCurrentRecording();
let recordings = PerformanceController.getRecordings();
this._activateRecordButtons(recordings.find(r => !r.isConsole() && r.isRecording()));
this._toggleRecordButtons(recordings.find(r => !r.isConsole() && r.isRecording()));
this._lockRecordButtons(recordings.find(r => !r.isConsole() && r.isFinalizing()));
if (currentRecording && currentRecording.isFinalizing()) {
@ -223,6 +236,14 @@ var PerformanceView = {
}
},
/**
* When starting a recording has failed.
*/
_onNewRecordingFailed: function (e) {
this._lockRecordButtons(false);
this._toggleRecordButtons(false);
},
/**
* Handler for clicking the clear button.
*/
@ -238,7 +259,7 @@ var PerformanceView = {
this.emit(EVENTS.UI_STOP_RECORDING);
} else {
this._lockRecordButtons(true);
this._activateRecordButtons(true);
this._toggleRecordButtons(true);
this.emit(EVENTS.UI_START_RECORDING);
}
},

View File

@ -157,6 +157,30 @@
<!-- Recording contents and general notice messages -->
<deck id="performance-view" flex="1">
<!-- A default notice, shown while initially opening the tool.
Keep this element the first child of #performance-view. -->
<hbox id="tool-loading-notice"
class="notice-container"
flex="1">
</hbox>
<!-- "Unavailable" notice, shown when the entire tool is disabled,
for example, when in private browsing mode. -->
<vbox id="unavailable-notice"
class="notice-container"
align="center"
pack="center"
flex="1">
<hbox class="devtools-toolbarbutton-group"
pack="center">
<toolbarbutton class="devtools-toolbarbutton record-button"
label="&performanceUI.startRecording;"
standalone="true"/>
</hbox>
<label class="tool-disabled-message"
value="&performanceUI.unavailableNoticePB;"/>
</vbox>
<!-- "Empty" notice, shown when there's no recordings available -->
<hbox id="empty-notice"
class="notice-container"

View File

@ -8,9 +8,6 @@ support-files =
doc_simple-test.html
head.js
# Commented out tests are profiler tests
# that need to be moved over to performance tool
[browser_aaa-run-first-leaktest.js]
[browser_perf-categories-js-calltree.js]
[browser_perf-clear-01.js]
@ -81,6 +78,7 @@ skip-if = os == 'linux' # bug 1186322
[browser_perf-overview-selection-02.js]
[browser_perf-overview-selection-03.js]
[browser_perf-overview-time-interval.js]
[browser_perf-private-browsing.js]
[browser_perf-states.js]
skip-if = debug # bug 1203888
[browser_perf-refresh.js]

View File

@ -22,7 +22,7 @@ function* spawnTest() {
yield profileEnd;
yield gDevTools.showToolbox(target, "performance");
let panel = toolbox.getCurrentPanel();
let panel = yield toolbox.getCurrentPanel().open();
let { panelWin: { PerformanceController, RecordingsView }} = panel;
let recordings = PerformanceController.getRecordings();

View File

@ -6,8 +6,6 @@
* when it is opened.
*/
var WAIT_TIME = 10;
function* spawnTest() {
let { target, toolbox, console } = yield initConsole(SIMPLE_URL);
let front = toolbox.performance;
@ -15,12 +13,13 @@ function* spawnTest() {
let profileStart = once(front, "recording-started");
console.profile("rust");
yield profileStart;
profileStart = once(front, "recording-started");
console.profile("rust2");
yield profileStart;
yield gDevTools.showToolbox(target, "performance");
let panel = toolbox.getCurrentPanel();
let panel = yield toolbox.getCurrentPanel().open();
let { panelWin: { PerformanceController, RecordingsView }} = panel;
yield waitUntil(() => PerformanceController.getRecordings().length === 2);
@ -39,6 +38,7 @@ function* spawnTest() {
let profileEnd = once(front, "recording-stopped");
console.profileEnd("rust");
yield profileEnd;
profileEnd = once(front, "recording-stopped");
console.profileEnd("rust2");
yield profileEnd;

View File

@ -6,8 +6,6 @@
* also console recordings that have finished before it was opened.
*/
var WAIT_TIME = 10;
function* spawnTest() {
let { target, toolbox, console } = yield initConsole(SIMPLE_URL);
let front = toolbox.performance;
@ -25,7 +23,7 @@ function* spawnTest() {
yield profileStart;
yield gDevTools.showToolbox(target, "performance");
let panel = toolbox.getCurrentPanel();
let panel = yield toolbox.getCurrentPanel().open();
let { panelWin: { PerformanceController, RecordingsView }} = panel;
yield waitUntil(() => PerformanceController.getRecordings().length === 2);

View File

@ -28,7 +28,7 @@ function* spawnTest() {
"performance tab is no longer highlighted when console.profile recording finishes");
yield gDevTools.showToolbox(target, "performance");
let panel = toolbox.getCurrentPanel();
let panel = yield toolbox.getCurrentPanel().open();
let { panelWin: { PerformanceController, RecordingsView }} = panel;
yield startRecording(panel);

View File

@ -20,7 +20,7 @@ function* spawnTest() {
yield profileStart;
yield gDevTools.showToolbox(target, "performance");
let panel = toolbox.getCurrentPanel();
let panel = yield toolbox.getCurrentPanel().open();
let { panelWin: { PerformanceController, RecordingsView }} = panel;
yield waitUntil(() => PerformanceController.getRecordings().length === 2);

View File

@ -0,0 +1,93 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that disables the frontend when in private browsing mode.
*/
let gPanelWinTuples = [];
function* spawnTest() {
yield testNormalWindow();
yield testPrivateWindow();
yield testRecordingFailingInWindow(0);
yield testRecordingFailingInWindow(1);
yield teardownPerfInWindow(1);
yield testRecordingSucceedingInWindow(0);
yield teardownPerfInWindow(0);
gPanelWinTuples = null;
finish();
}
function* createPanelInWindow(options) {
let win = yield addWindow(options);
let tab = yield addTab(SIMPLE_URL, win);
let target = TargetFactory.forTab(tab);
yield target.makeRemote();
let toolbox = yield gDevTools.showToolbox(target, "performance");
yield toolbox.initPerformance();
let panel = yield toolbox.getCurrentPanel().open();
gPanelWinTuples.push({ panel, win });
return { panel, win };
}
function* testNormalWindow() {
let { panel } = yield createPanelInWindow({ private: false });
let { PerformanceView } = panel.panelWin;
is(PerformanceView.getState(), "empty",
"The initial state of the performance panel view is correct (1).");
}
function* testPrivateWindow() {
let { panel } = yield createPanelInWindow({ private: true });
let { PerformanceView } = panel.panelWin;
is(PerformanceView.getState(), "unavailable",
"The initial state of the performance panel view is correct (2).");
}
function* testRecordingFailingInWindow(index) {
let { panel } = gPanelWinTuples[index];
let { EVENTS, PerformanceController } = panel.panelWin;
let onRecordingStarted = () => {
ok(false, "Recording should not start while a private window is present.");
};
PerformanceController.on(EVENTS.RECORDING_STARTED, onRecordingStarted);
let whenFailed = once(PerformanceController, EVENTS.NEW_RECORDING_FAILED);
PerformanceController.startRecording();
yield whenFailed;
ok(true, "Recording has failed.");
PerformanceController.off(EVENTS.RECORDING_STARTED, onRecordingStarted);
}
function* testRecordingSucceedingInWindow(index) {
let { panel } = gPanelWinTuples[index];
let { EVENTS, PerformanceController } = panel.panelWin;
let onRecordingFailed = () => {
ok(false, "Recording should start while now private windows are present.");
};
PerformanceController.on(EVENTS.NEW_RECORDING_FAILED, onRecordingFailed);
yield startRecording(panel);
yield stopRecording(panel);
ok(true, "Recording has succeeded.");
PerformanceController.off(EVENTS.RECORDING_STARTED, onRecordingFailed);
}
function* teardownPerfInWindow(index) {
let { panel, win } = gPanelWinTuples[index];
yield teardown(panel, win);
win.close();
}

View File

@ -6,6 +6,9 @@
*/
function* spawnTest() {
// This test seems to take a long time to cleanup on Ubuntu VMs.
requestLongerTimeout(2);
let { target, panel } = yield initPerformance(SIMPLE_URL);
let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView, DetailsView } = panel.panelWin;
let { WATERFALL_MARKER_SIDEBAR_SAFE_BOUNDS } = require("devtools/client/performance/modules/widgets/marker-view");

View File

@ -105,6 +105,29 @@ registerCleanupFunction(() => {
Cu.forceGC();
});
function whenDelayedStartupFinished(aWindow, aCallback) {
Services.obs.addObserver(function observer(aSubject, aTopic) {
if (aWindow == aSubject) {
Services.obs.removeObserver(observer, aTopic);
executeSoon(aCallback);
}
}, "browser-delayed-startup-finished", false);
}
function addWindow(windowOptions) {
let deferred = Promise.defer();
let win = OpenBrowserWindow(windowOptions);
whenDelayedStartupFinished(win, () => {
executeSoon(() => {
deferred.resolve(win);
});
});
return deferred.promise;
}
function addTab(aUrl, aWindow) {
info("Adding tab: " + aUrl);
@ -233,6 +256,8 @@ function initPerformance(aUrl, tool="performance", targetOps={}) {
// Wait for the performance tool to be spun up
yield toolbox.initPerformance();
// Panel is already initialized after `showToolbox` and `initPerformance`,
// no need to wait for `open` here.
let panel = toolbox.getCurrentPanel();
return { target, panel, toolbox };
});
@ -276,12 +301,12 @@ function consoleExecute (console, method, val) {
return promise;
}
function* teardown(panel) {
function* teardown(panel, win = window) {
info("Destroying the performance tool.");
let tab = panel.target.tab;
yield panel._toolbox.destroy();
yield removeTab(tab);
yield removeTab(tab, win);
}
function idleWait(time) {

View File

@ -7,6 +7,7 @@ const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/
const { thunk } = require("./middleware/thunk");
const { waitUntilService } = require("./middleware/wait-service");
const { log } = require("./middleware/log");
const { promise } = require("./middleware/promise");
/**
* This creates a dispatcher with all the standard middleware in place
@ -20,7 +21,8 @@ const { log } = require("./middleware/log");
module.exports = (opts={}) => {
const middleware = [
thunk,
waitUntilService
waitUntilService,
promise,
];
if (opts.log) {

View File

@ -6,6 +6,7 @@
DevToolsModules(
'log.js',
'promise.js',
'thunk.js',
'wait-service.js',
)

View File

@ -0,0 +1,52 @@
/* 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 uuidgen = require("sdk/util/uuid").uuid;
const {
entries, toObject, reportException, executeSoon
} = require("devtools/shared/DevToolsUtils");
const PROMISE = exports.PROMISE = "@@dispatch/promise";
function promiseMiddleware ({ dispatch, getState }) {
return next => action => {
if (!(PROMISE in action)) {
return next(action);
}
const promise = action[PROMISE];
const seqId = uuidgen().toString();
// Create a new action that doesn't have the promise field and has
// the `seqId` field that represents the sequence id
action = Object.assign(
toObject(entries(action).filter(pair => pair[0] !== PROMISE)), { seqId }
);
dispatch(Object.assign({}, action, { status: "start" }));
promise.then(value => {
executeSoon(() => {
dispatch(Object.assign({}, action, {
status: "done",
value: value
}));
});
}).catch(error => {
executeSoon(() => {
dispatch(Object.assign({}, action, {
status: "error",
error
}));
});
reportException(`@@redux/middleware/promise#${action.type}`, error);
});
// Return the promise so action creators can still compose if they
// want to.
return promise;
};
}
exports.promise = promiseMiddleware;

View File

@ -2,26 +2,27 @@
tags = devtools
subsuite = devtools
support-files =
cm_comment_test.js
cm_doc_test.js
cm_driver.js
cm_emacs_test.js
cm_mode_test.css
cm_mode_test.js
cm_multi_test.js
cm_mode_ruby.js
cm_script_injection_test.js
cm_search_test.js
cm_sublime_test.js
cm_test.js
cm_vim_test.js
codemirror.html
codemirror/comment_test.js
codemirror/doc_test.js
codemirror/driver.js
codemirror/emacs_test.js
codemirror/mode_test.css
codemirror/mode_test.js
codemirror/multi_test.js
codemirror/search_test.js
codemirror/sublime_test.js
codemirror/test.js
codemirror/vim_test.js
codemirror/codemirror.html
codemirror/vimemacs.html
codemirror/mode/javascript/test.js
css_statemachine_testcases.css
css_statemachine_tests.json
css_autocompletion_tests.json
vimemacs.html
head.js
helper_codemirror_runner.js
cm_mode_ruby.js
cm_script_injection_test.js
[browser_editor_autocomplete_basic.js]
[browser_editor_autocomplete_events.js]

View File

@ -4,7 +4,7 @@
"use strict";
const URI = "chrome://mochitests/content/browser/devtools/client/sourceeditor/test/codemirror.html";
const URI = "chrome://mochitests/content/browser/devtools/client/sourceeditor/test/codemirror/codemirror.html";
loadHelperScript("helper_codemirror_runner.js");
function test() {

View File

@ -3,7 +3,7 @@
"use strict";
const URI = "chrome://mochitests/content/browser/devtools/client/sourceeditor/test/vimemacs.html";
const URI = "chrome://mochitests/content/browser/devtools/client/sourceeditor/test/codemirror/vimemacs.html";
loadHelperScript("helper_codemirror_runner.js");
function test() {

View File

@ -58,16 +58,16 @@
<div id=testground></div>
<script src="cm_driver.js"></script>
<script src="cm_test.js"></script>
<script src="cm_comment_test.js"></script>
<script src="cm_doc_test.js"></script>
<script src="cm_driver.js"></script>
<script src="cm_emacs_test.js"></script>
<script src="cm_mode_test.js"></script>
<script src="cm_mode_javascript_test.js"></script>
<script src="cm_multi_test.js"></script>
<script src="cm_search_test.js"></script>
<script src="driver.js"></script>
<script src="test.js"></script>
<script src="comment_test.js"></script>
<script src="doc_test.js"></script>
<script src="driver.js"></script>
<script src="emacs_test.js"></script>
<script src="mode_test.js"></script>
<script src="mode/javascript/test.js"></script>
<script src="multi_test.js"></script>
<script src="search_test.js"></script>
<!-- VIM and Emacs mode tests are in vimemacs.html
<script src="cm_sublime_test.js"></script>

View File

@ -58,10 +58,10 @@
<div id=testground></div>
<script src="cm_driver.js"></script>
<script src="cm_sublime_test.js"></script>
<script src="cm_vim_test.js"></script>
<script src="cm_emacs_test.js"></script>
<script src="driver.js"></script>
<script src="sublime_test.js"></script>
<script src="vim_test.js"></script>
<script src="emacs_test.js"></script>
<!-- Basic tests are in codemirror.html
<script src="cm_driver.js"></script>

View File

@ -0,0 +1,74 @@
/* 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 protocol = require("devtools/server/protocol");
const { method, Arg } = protocol;
const Services = require("Services");
loader.lazyRequireGetter(this, "DevToolsUtils",
"devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "OS", "resource://gre/modules/osfile.jsm", true);
loader.lazyRequireGetter(this, "Task", "resource://gre/modules/Task.jsm", true);
loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
"devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
/**
* The HeapSnapshotFileActor handles transferring heap snapshot files from the
* server to the client. This has to be a global actor in the parent process
* because child processes are sandboxed and do not have access to the file
* system.
*/
exports.HeapSnapshotFileActor = protocol.ActorClass({
typeName: "heapSnapshotFile",
initialize: function (conn, parent) {
if (Services.appInfo &&
(Services.appInfo.processType !==
Services.appInfo.PROCESS_TYPE_DEFAULT)) {
const err = new Error("Attempt to create a HeapSnapshotFileActor in a " +
"child process! The HeapSnapshotFileActor *MUST* " +
"be in the parent process!");
DevToolsUtils.reportException(
"HeapSnapshotFileActor.prototype.initialize", err);
return;
}
protocol.Actor.prototype.initialize.call(this, conn, parent);
},
/**
* @see MemoryFront.prototype.transferHeapSnapshot
*/
transferHeapSnapshot: method(Task.async(function* (snapshotId) {
const snapshotFilePath =
HeapSnapshotFileUtils.getHeapSnapshotTempFilePath(snapshotId);
if (!snapshotFilePath) {
throw new Error(`No heap snapshot with id: ${snapshotId}`);
}
const streamPromise = DevToolsUtils.openFileStream(snapshotFilePath);
const { size } = yield OS.File.stat(snapshotFilePath);
const bulkPromise = this.conn.startBulkSend({
actor: this.actorID,
type: "heap-snapshot",
length: size
});
const [bulk, stream] = yield Promise.all([bulkPromise, streamPromise]);
try {
yield bulk.copyFrom(stream);
} finally {
stream.close();
}
}), {
request: {
snapshotId: Arg(0, "string")
}
}),
});

View File

@ -5,7 +5,6 @@
"use strict";
const { Cc, Ci, Cu, components } = require("chrome");
const { openFileStream } = require("devtools/shared/DevToolsUtils");
const protocol = require("devtools/server/protocol");
const { method, RetVal, Arg, types } = protocol;
const { Memory } = require("devtools/shared/shared/memory");
@ -17,7 +16,6 @@ loader.lazyRequireGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm", true);
loader.lazyRequireGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm", true);
loader.lazyRequireGetter(this, "Task", "resource://gre/modules/Task.jsm", true);
loader.lazyRequireGetter(this, "OS", "resource://gre/modules/osfile.jsm", true);
loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
"devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
loader.lazyRequireGetter(this, "ThreadSafeChromeUtils");
@ -115,35 +113,6 @@ var MemoryActor = exports.MemoryActor = protocol.ActorClass({
}
}),
transferHeapSnapshot: method(Task.async(function* (snapshotId) {
const snapshotFilePath =
HeapSnapshotFileUtils.getHeapSnapshotTempFilePath(snapshotId);
if (!snapshotFilePath) {
throw new Error(`No heap snapshot with id: ${snapshotId}`);
}
const streamPromise = openFileStream(snapshotFilePath);
const { size } = yield OS.File.stat(snapshotFilePath);
const bulkPromise = this.conn.startBulkSend({
actor: this.actorID,
type: "heap-snapshot",
length: size
});
const [bulk, stream] = yield Promise.all([bulkPromise, streamPromise]);
try {
yield bulk.copyFrom(stream);
} finally {
stream.close();
}
}), {
request: {
snapshotId: Arg(0, "string")
}
}),
takeCensus: actorBridge("takeCensus", {
request: {},
response: RetVal("json")
@ -213,10 +182,13 @@ var MemoryActor = exports.MemoryActor = protocol.ActorClass({
});
exports.MemoryFront = protocol.FrontClass(MemoryActor, {
initialize: function(client, form) {
initialize: function(client, form, rootForm = null) {
protocol.Front.prototype.initialize.call(this, client, form);
this._client = client;
this.actorID = form.memoryActor;
this.heapSnapshotFileActorID = rootForm
? rootForm.heapSnapshotFileActor
: null;
this.manage(this);
},
@ -225,9 +197,8 @@ exports.MemoryFront = protocol.FrontClass(MemoryActor, {
* server and client do not share a file system, and return the local file
* path to the heap snapshot.
*
* NB: This will not work with sandboxed child processes, as they do not have
* access to the filesystem and the hep snapshot APIs do not support that use
* case yet.
* Note that this is safe to call for actors inside sandoxed child processes,
* as we jump through the correct IPDL hoops.
*
* @params Boolean options.forceCopy
* Always force a bulk data copy of the saved heap snapshot, even when
@ -258,8 +229,12 @@ exports.MemoryFront = protocol.FrontClass(MemoryActor, {
* @returns Promise<String>
*/
transferHeapSnapshot: protocol.custom(function (snapshotId) {
if (!this.heapSnapshotFileActorID) {
throw new Error("MemoryFront initialized without a rootForm");
}
const request = this._client.request({
to: this.actorID,
to: this.heapSnapshotFileActorID,
type: "transferHeapSnapshot",
snapshotId
});

Some files were not shown because too many files have changed in this diff Show More