зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound.
This commit is contained in:
Коммит
839040eeee
2
CLOBBER
2
CLOBBER
|
@ -18,4 +18,4 @@
|
|||
# Modifying this file will now automatically clobber the buildbot machines \o/
|
||||
#
|
||||
|
||||
Bug 947080 - bug 937317 required clobber on windows (relanding).
|
||||
Bug 933585 - clobber required on windows
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"revision": "d10e0357c63bb565db8bdd3c23f062bfb9e21315",
|
||||
"revision": "63d432c3395f95c0ba19578487b796c1707042bd",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -869,7 +869,7 @@ let PlacesToolbarHelper = {
|
|||
return document.getElementById("PlacesToolbar");
|
||||
},
|
||||
|
||||
init: function PTH_init() {
|
||||
init: function PTH_init(forceToolbarOverflowCheck) {
|
||||
let viewElt = this._viewElt;
|
||||
if (!viewElt || viewElt._placesView)
|
||||
return;
|
||||
|
@ -886,6 +886,9 @@ let PlacesToolbarHelper = {
|
|||
return;
|
||||
|
||||
new PlacesToolbar(this._place);
|
||||
if (forceToolbarOverflowCheck) {
|
||||
viewElt._placesView.updateOverflowStatus();
|
||||
}
|
||||
},
|
||||
|
||||
customizeStart: function PTH_customizeStart() {
|
||||
|
@ -900,7 +903,7 @@ let PlacesToolbarHelper = {
|
|||
|
||||
customizeDone: function PTH_customizeDone() {
|
||||
this._isCustomizing = false;
|
||||
this.init();
|
||||
this.init(true);
|
||||
},
|
||||
|
||||
onPlaceholderCommand: function () {
|
||||
|
|
|
@ -1037,32 +1037,14 @@ PlacesToolbar.prototype = {
|
|||
this.updateChevron();
|
||||
break;
|
||||
case "overflow":
|
||||
if (aEvent.target != aEvent.currentTarget)
|
||||
if (!this._isOverflowStateEventRelevant(aEvent))
|
||||
return;
|
||||
|
||||
// Ignore purely vertical overflows.
|
||||
if (aEvent.detail == 0)
|
||||
return;
|
||||
|
||||
// Attach the popup binding to the chevron popup if it has not yet
|
||||
// been initialized.
|
||||
if (!this._chevronPopup.hasAttribute("type")) {
|
||||
this._chevronPopup.setAttribute("place", this.place);
|
||||
this._chevronPopup.setAttribute("type", "places");
|
||||
}
|
||||
this._chevron.collapsed = false;
|
||||
this.updateChevron();
|
||||
this._onOverflow();
|
||||
break;
|
||||
case "underflow":
|
||||
if (aEvent.target != aEvent.currentTarget)
|
||||
if (!this._isOverflowStateEventRelevant(aEvent))
|
||||
return;
|
||||
|
||||
// Ignore purely vertical underflows.
|
||||
if (aEvent.detail == 0)
|
||||
return;
|
||||
|
||||
this.updateChevron();
|
||||
this._chevron.collapsed = true;
|
||||
this._onUnderflow();
|
||||
break;
|
||||
case "TabOpen":
|
||||
case "TabClose":
|
||||
|
@ -1103,6 +1085,35 @@ PlacesToolbar.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
updateOverflowStatus: function() {
|
||||
if (this._rootElt.scrollLeftMax > 0) {
|
||||
this._onOverflow();
|
||||
} else {
|
||||
this._onUnderflow();
|
||||
}
|
||||
},
|
||||
|
||||
_isOverflowStateEventRelevant: function PT_isOverflowStateEventRelevant(aEvent) {
|
||||
// Ignore events not aimed at ourselves, as well as purely vertical ones:
|
||||
return aEvent.target == aEvent.currentTarget && aEvent.detail > 0;
|
||||
},
|
||||
|
||||
_onOverflow: function PT_onOverflow() {
|
||||
// Attach the popup binding to the chevron popup if it has not yet
|
||||
// been initialized.
|
||||
if (!this._chevronPopup.hasAttribute("type")) {
|
||||
this._chevronPopup.setAttribute("place", this.place);
|
||||
this._chevronPopup.setAttribute("type", "places");
|
||||
}
|
||||
this._chevron.collapsed = false;
|
||||
this.updateChevron();
|
||||
},
|
||||
|
||||
_onUnderflow: function PT_onUnderflow() {
|
||||
this.updateChevron();
|
||||
this._chevron.collapsed = true;
|
||||
},
|
||||
|
||||
updateChevron: function PT_updateChevron() {
|
||||
// If the chevron is collapsed there's nothing to update.
|
||||
if (this._chevron.collapsed)
|
||||
|
|
|
@ -1508,12 +1508,34 @@ let SessionStoreInternal = {
|
|||
throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
|
||||
},
|
||||
|
||||
/**
|
||||
* Restores the given state |aState| for a given window |aWindow|.
|
||||
*
|
||||
* @param aWindow (xul window)
|
||||
* The window that the given state will be restored to.
|
||||
* @param aState (string)
|
||||
* The state that will be applied to the given window.
|
||||
* @param aOverwrite (bool)
|
||||
* When true, existing tabs in the given window will be re-used or
|
||||
* removed. When false, only new tabs will be added, no existing ones
|
||||
8 will be removed or overwritten.
|
||||
*/
|
||||
setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite) {
|
||||
if (!aWindow.__SSi) {
|
||||
throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
this.restoreWindow(aWindow, aState, {overwriteTabs: aOverwrite});
|
||||
let winState = JSON.parse(aState);
|
||||
if (!winState) {
|
||||
throw Components.Exception("Invalid state string: not JSON", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
if (!winState.windows || !winState.windows[0]) {
|
||||
throw Components.Exception("Invalid window state passed", Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
let state = {windows: [winState.windows[0]]};
|
||||
this.restoreWindow(aWindow, state, {overwriteTabs: aOverwrite});
|
||||
},
|
||||
|
||||
getTabState: function ssi_getTabState(aTab) {
|
||||
|
@ -2280,7 +2302,7 @@ let SessionStoreInternal = {
|
|||
* @param aWindow
|
||||
* Window reference
|
||||
* @param aState
|
||||
* JS object or its eval'able source
|
||||
* JS object
|
||||
* @param aOptions
|
||||
* {overwriteTabs: true} to overwrite existing tabs w/ new ones
|
||||
* {isFollowUp: true} if this is not the restoration of the 1st window
|
||||
|
@ -2300,17 +2322,10 @@ let SessionStoreInternal = {
|
|||
if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
|
||||
this.onLoad(aWindow);
|
||||
|
||||
try {
|
||||
var root = typeof aState == "string" ? JSON.parse(aState) : aState;
|
||||
if (!root.windows[0]) {
|
||||
this._sendRestoreCompletedNotifications();
|
||||
return; // nothing to restore
|
||||
}
|
||||
}
|
||||
catch (ex) { // invalid state object - don't restore anything
|
||||
debug(ex);
|
||||
var root = aState;
|
||||
if (!root.windows[0]) {
|
||||
this._sendRestoreCompletedNotifications();
|
||||
return;
|
||||
return; // nothing to restore
|
||||
}
|
||||
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
|
||||
|
|
|
@ -78,44 +78,38 @@ function test() {
|
|||
}
|
||||
}
|
||||
|
||||
// open a window and add the above closed window list
|
||||
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
|
||||
newWin.addEventListener("load", function(aEvent) {
|
||||
this.removeEventListener("load", arguments.callee, false);
|
||||
gPrefService.setIntPref("browser.sessionstore.max_windows_undo",
|
||||
test_state._closedWindows.length);
|
||||
ss.setWindowState(newWin, JSON.stringify(test_state), true);
|
||||
gPrefService.setIntPref("browser.sessionstore.max_windows_undo",
|
||||
test_state._closedWindows.length);
|
||||
ss.setBrowserState(JSON.stringify(test_state), true);
|
||||
|
||||
let closedWindows = JSON.parse(ss.getClosedWindowData());
|
||||
is(closedWindows.length, test_state._closedWindows.length,
|
||||
"Closed window list has the expected length");
|
||||
is(countByTitle(closedWindows, FORGET),
|
||||
test_state._closedWindows.length - remember_count,
|
||||
"The correct amount of windows are to be forgotten");
|
||||
is(countByTitle(closedWindows, REMEMBER), remember_count,
|
||||
"Everything is set up.");
|
||||
let closedWindows = JSON.parse(ss.getClosedWindowData());
|
||||
is(closedWindows.length, test_state._closedWindows.length,
|
||||
"Closed window list has the expected length");
|
||||
is(countByTitle(closedWindows, FORGET),
|
||||
test_state._closedWindows.length - remember_count,
|
||||
"The correct amount of windows are to be forgotten");
|
||||
is(countByTitle(closedWindows, REMEMBER), remember_count,
|
||||
"Everything is set up.");
|
||||
|
||||
// all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
|
||||
ok(testForError(function() ss.forgetClosedWindow(-1)),
|
||||
"Invalid window for forgetClosedWindow throws");
|
||||
ok(testForError(function() ss.forgetClosedWindow(test_state._closedWindows.length + 1)),
|
||||
"Invalid window for forgetClosedWindow throws");
|
||||
// all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
|
||||
ok(testForError(function() ss.forgetClosedWindow(-1)),
|
||||
"Invalid window for forgetClosedWindow throws");
|
||||
ok(testForError(function() ss.forgetClosedWindow(test_state._closedWindows.length + 1)),
|
||||
"Invalid window for forgetClosedWindow throws");
|
||||
|
||||
// Remove third window, then first window
|
||||
ss.forgetClosedWindow(2);
|
||||
ss.forgetClosedWindow(null);
|
||||
// Remove third window, then first window
|
||||
ss.forgetClosedWindow(2);
|
||||
ss.forgetClosedWindow(null);
|
||||
|
||||
closedWindows = JSON.parse(ss.getClosedWindowData());
|
||||
is(closedWindows.length, remember_count,
|
||||
"The correct amount of windows were removed");
|
||||
is(countByTitle(closedWindows, FORGET), 0,
|
||||
"All windows specifically forgotten were indeed removed");
|
||||
is(countByTitle(closedWindows, REMEMBER), remember_count,
|
||||
"... and windows not specifically forgetten weren't.");
|
||||
closedWindows = JSON.parse(ss.getClosedWindowData());
|
||||
is(closedWindows.length, remember_count,
|
||||
"The correct amount of windows were removed");
|
||||
is(countByTitle(closedWindows, FORGET), 0,
|
||||
"All windows specifically forgotten were indeed removed");
|
||||
is(countByTitle(closedWindows, REMEMBER), remember_count,
|
||||
"... and windows not specifically forgetten weren't.");
|
||||
|
||||
// clean up
|
||||
newWin.close();
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
|
||||
finish();
|
||||
}, false);
|
||||
// clean up
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -686,9 +686,9 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
// The container is not empty and an actual item was selected.
|
||||
DebuggerView.setEditorLocation(sourceItem.value);
|
||||
|
||||
// Set window title.
|
||||
let script = sourceItem.value.split(" -> ").pop();
|
||||
document.title = L10N.getFormatStr("DebuggerWindowScriptTitle", script);
|
||||
// Set window title. No need to split the url by " -> " here, because it was
|
||||
// already sanitized when the source was added.
|
||||
document.title = L10N.getFormatStr("DebuggerWindowScriptTitle", sourceItem.value);
|
||||
|
||||
DebuggerView.maybeShowBlackBoxMessage();
|
||||
this.updateToolbarButtonsState();
|
||||
|
@ -1085,38 +1085,14 @@ let SourceUtils = {
|
|||
|
||||
try {
|
||||
// Use an nsIURL to parse all the url path parts.
|
||||
var uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
|
||||
let url = aUrl.split(" -> ").pop();
|
||||
var uri = Services.io.newURI(url, null, null).QueryInterface(Ci.nsIURL);
|
||||
} catch (e) {
|
||||
// This doesn't look like a url, or nsIURL can't handle it.
|
||||
return "";
|
||||
}
|
||||
|
||||
let { scheme, directory, fileName } = uri;
|
||||
let hostPort;
|
||||
// Add-on SDK jar: URLs will cause accessing hostPort to throw.
|
||||
if (scheme != "jar") {
|
||||
hostPort = uri.hostPort;
|
||||
}
|
||||
let lastDir = directory.split("/").reverse()[1];
|
||||
let group = [];
|
||||
|
||||
// Only show interesting schemes, http is implicit.
|
||||
if (scheme != "http") {
|
||||
group.push(scheme);
|
||||
}
|
||||
// Hostnames don't always exist for files or some resource urls.
|
||||
// e.g. file://foo/bar.js or resource:///foo/bar.js don't have a host.
|
||||
if (hostPort) {
|
||||
// If the hostname is a dot-separated identifier, show the first 2 parts.
|
||||
group.push(hostPort.split(".").slice(0, 2).join("."));
|
||||
}
|
||||
// Append the last directory if the path leads to an actual file.
|
||||
// e.g. http://foo.org/bar/ should only show "foo.org", not "foo.org bar"
|
||||
if (fileName) {
|
||||
group.push(lastDir);
|
||||
}
|
||||
|
||||
let groupLabel = group.join(" ");
|
||||
let groupLabel = uri.prePath;
|
||||
let unicodeLabel = NetworkHelper.convertToUnicode(unescape(groupLabel));
|
||||
this._groupsCache.set(aUrl, unicodeLabel)
|
||||
return unicodeLabel;
|
||||
|
|
|
@ -399,14 +399,6 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
initialize: function() {
|
||||
dumpn("Initializing the StackFramesView");
|
||||
|
||||
let commandset = this._commandset = document.createElement("commandset");
|
||||
let menupopup = this._menupopup = document.createElement("menupopup");
|
||||
commandset.id = "stackframesCommandset";
|
||||
menupopup.id = "stackframesMenupopup";
|
||||
|
||||
document.getElementById("debuggerPopupset").appendChild(menupopup);
|
||||
document.getElementById("debuggerCommands").appendChild(commandset);
|
||||
|
||||
this.widget = new BreadcrumbsWidget(document.getElementById("stackframes"));
|
||||
this.widget.addEventListener("select", this._onSelect, false);
|
||||
this.widget.addEventListener("scroll", this._onScroll, true);
|
||||
|
@ -414,6 +406,9 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
|
||||
this.autoFocusOnFirstItem = false;
|
||||
this.autoFocusOnSelection = false;
|
||||
|
||||
// This view's contents are also mirrored in a different container.
|
||||
this._mirror = DebuggerView.StackFramesClassicList;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -453,24 +448,22 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
this._prevBlackBoxedUrl = null;
|
||||
}
|
||||
|
||||
// Create the element node and menu entry for the stack frame item.
|
||||
// Create the element node for the stack frame item.
|
||||
let frameView = this._createFrameView.apply(this, arguments);
|
||||
let menuEntry = this._createMenuEntry.apply(this, arguments);
|
||||
|
||||
// Append a stack frame item to this container.
|
||||
this.push([frameView, aTitle, aUrl], {
|
||||
index: 0, /* specifies on which position should the item be appended */
|
||||
attachment: {
|
||||
popup: menuEntry,
|
||||
depth: aDepth
|
||||
},
|
||||
attributes: [
|
||||
["contextmenu", "stackframesMenupopup"]
|
||||
],
|
||||
// Make sure that when the stack frame item is removed, the corresponding
|
||||
// menuitem and command are also destroyed.
|
||||
// mirrored item in the classic list is also removed.
|
||||
finalize: this._onStackframeRemoved
|
||||
});
|
||||
|
||||
// Mirror this newly inserted item inside the "Call Stack" tab.
|
||||
this._mirror.addFrame(aTitle, aUrl, aLine, aDepth);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -542,65 +535,6 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
return container;
|
||||
},
|
||||
|
||||
/**
|
||||
* Customization function for populating an item's context menu.
|
||||
*
|
||||
* @param string aTitle
|
||||
* The frame title to be displayed in the list.
|
||||
* @param string aUrl
|
||||
* The frame source url.
|
||||
* @param string aLine
|
||||
* The frame line number.
|
||||
* @param number aDepth
|
||||
* The frame depth in the stack.
|
||||
* @param boolean aIsBlackBoxed
|
||||
* Whether or not the frame is black boxed.
|
||||
* @return object
|
||||
* An object containing the stack frame command and menu item.
|
||||
*/
|
||||
_createMenuEntry: function(aTitle, aUrl, aLine, aDepth, aIsBlackBoxed) {
|
||||
let frameDescription = SourceUtils.trimUrlLength(
|
||||
SourceUtils.getSourceLabel(aUrl),
|
||||
STACK_FRAMES_POPUP_SOURCE_URL_MAX_LENGTH,
|
||||
STACK_FRAMES_POPUP_SOURCE_URL_TRIM_SECTION) +
|
||||
SEARCH_LINE_FLAG + aLine;
|
||||
|
||||
let prefix = "sf-cMenu-"; // "stackframes context menu"
|
||||
let commandId = prefix + aDepth + "-" + "-command";
|
||||
let menuitemId = prefix + aDepth + "-" + "-menuitem";
|
||||
|
||||
let command = document.createElement("command");
|
||||
command.id = commandId;
|
||||
command.addEventListener("command", () => this.selectedDepth = aDepth, false);
|
||||
|
||||
let menuitem = document.createElement("menuitem");
|
||||
menuitem.id = menuitemId;
|
||||
menuitem.className = "dbg-stackframe-menuitem";
|
||||
menuitem.setAttribute("type", "checkbox");
|
||||
menuitem.setAttribute("command", commandId);
|
||||
menuitem.setAttribute("tooltiptext", aUrl);
|
||||
|
||||
let labelNode = document.createElement("label");
|
||||
labelNode.className = "plain dbg-stackframe-menuitem-title";
|
||||
labelNode.setAttribute("value", aTitle);
|
||||
labelNode.setAttribute("flex", "1");
|
||||
|
||||
let descriptionNode = document.createElement("label");
|
||||
descriptionNode.className = "plain dbg-stackframe-menuitem-details";
|
||||
descriptionNode.setAttribute("value", frameDescription);
|
||||
|
||||
menuitem.appendChild(labelNode);
|
||||
menuitem.appendChild(descriptionNode);
|
||||
|
||||
this._commandset.appendChild(command);
|
||||
this._menupopup.appendChild(menuitem);
|
||||
|
||||
return {
|
||||
command: command,
|
||||
menuitem: menuitem
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Function called each time a stack frame item is removed.
|
||||
*
|
||||
|
@ -610,10 +544,9 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
_onStackframeRemoved: function(aItem) {
|
||||
dumpn("Finalizing stackframe item: " + aItem);
|
||||
|
||||
// Destroy the context menu item for the stack frame.
|
||||
let contextItem = aItem.attachment.popup;
|
||||
contextItem.command.remove();
|
||||
contextItem.menuitem.remove();
|
||||
// Remove the mirrored item in the classic list.
|
||||
let depth = aItem.attachment.depth;
|
||||
this._mirror.remove(this._mirror.getItemForAttachment(e => e.depth == depth));
|
||||
|
||||
// Forget the previously blackboxed stack frame url.
|
||||
this._prevBlackBoxedUrl = null;
|
||||
|
@ -626,17 +559,13 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
let stackframeItem = this.selectedItem;
|
||||
if (stackframeItem) {
|
||||
// The container is not empty and an actual item was selected.
|
||||
DebuggerController.StackFrames.selectFrame(stackframeItem.attachment.depth);
|
||||
let depth = stackframeItem.attachment.depth;
|
||||
DebuggerController.StackFrames.selectFrame(depth);
|
||||
|
||||
// Update the context menu to show the currently selected stackframe item
|
||||
// as a checked entry.
|
||||
for (let otherItem of this) {
|
||||
if (otherItem != stackframeItem) {
|
||||
otherItem.attachment.popup.menuitem.removeAttribute("checked");
|
||||
} else {
|
||||
otherItem.attachment.popup.menuitem.setAttribute("checked", "");
|
||||
}
|
||||
}
|
||||
// Mirror the selected item in the classic list.
|
||||
this.suppressSelectionEvents = true;
|
||||
this._mirror.selectedItem = e => e.attachment.depth == depth;
|
||||
this.suppressSelectionEvents = false;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -673,11 +602,142 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
}
|
||||
},
|
||||
|
||||
_commandset: null,
|
||||
_menupopup: null,
|
||||
_mirror: null,
|
||||
_prevBlackBoxedUrl: null
|
||||
});
|
||||
|
||||
/*
|
||||
* Functions handling the stackframes classic list UI.
|
||||
* Controlled by the DebuggerView.StackFrames isntance.
|
||||
*/
|
||||
function StackFramesClassicListView() {
|
||||
dumpn("StackFramesClassicListView was instantiated");
|
||||
|
||||
this._onSelect = this._onSelect.bind(this);
|
||||
}
|
||||
|
||||
StackFramesClassicListView.prototype = Heritage.extend(WidgetMethods, {
|
||||
/**
|
||||
* Initialization function, called when the debugger is started.
|
||||
*/
|
||||
initialize: function() {
|
||||
dumpn("Initializing the StackFramesClassicListView");
|
||||
|
||||
this.widget = new SideMenuWidget(document.getElementById("callstack-list"), {
|
||||
theme: "light"
|
||||
});
|
||||
this.widget.addEventListener("select", this._onSelect, false);
|
||||
|
||||
this.emptyText = L10N.getStr("noStackFramesText");
|
||||
this.autoFocusOnFirstItem = false;
|
||||
this.autoFocusOnSelection = false;
|
||||
|
||||
// This view's contents are also mirrored in a different container.
|
||||
this._mirror = DebuggerView.StackFrames;
|
||||
|
||||
// Show an empty label by default.
|
||||
this.empty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Destruction function, called when the debugger is closed.
|
||||
*/
|
||||
destroy: function() {
|
||||
dumpn("Destroying the StackFramesClassicListView");
|
||||
|
||||
this.widget.removeEventListener("select", this._onSelect, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a frame in this stackframes container.
|
||||
*
|
||||
* @param string aTitle
|
||||
* The frame title (function name).
|
||||
* @param string aUrl
|
||||
* The frame source url.
|
||||
* @param string aLine
|
||||
* The frame line number.
|
||||
* @param number aDepth
|
||||
* The frame depth in the stack.
|
||||
*/
|
||||
addFrame: function(aTitle, aUrl, aLine, aDepth) {
|
||||
// Create the element node for the stack frame item.
|
||||
let frameView = this._createFrameView.apply(this, arguments);
|
||||
|
||||
// Append a stack frame item to this container.
|
||||
this.push([frameView, aUrl], {
|
||||
attachment: {
|
||||
depth: aDepth
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Customization function for creating an item's UI.
|
||||
*
|
||||
* @param string aTitle
|
||||
* The frame title to be displayed in the list.
|
||||
* @param string aUrl
|
||||
* The frame source url.
|
||||
* @param string aLine
|
||||
* The frame line number.
|
||||
* @param number aDepth
|
||||
* The frame depth in the stack.
|
||||
* @return nsIDOMNode
|
||||
* The stack frame view.
|
||||
*/
|
||||
_createFrameView: function(aTitle, aUrl, aLine, aDepth) {
|
||||
let container = document.createElement("hbox");
|
||||
container.id = "classic-stackframe-" + aDepth;
|
||||
container.className = "dbg-classic-stackframe";
|
||||
container.setAttribute("flex", "1");
|
||||
|
||||
let frameTitleNode = document.createElement("label");
|
||||
frameTitleNode.className = "plain dbg-classic-stackframe-title";
|
||||
frameTitleNode.setAttribute("value", aTitle);
|
||||
frameTitleNode.setAttribute("crop", "center");
|
||||
|
||||
let frameDetailsNode = document.createElement("hbox");
|
||||
frameDetailsNode.className = "plain dbg-classic-stackframe-details";
|
||||
|
||||
let frameUrlNode = document.createElement("label");
|
||||
frameUrlNode.className = "plain dbg-classic-stackframe-details-url";
|
||||
frameUrlNode.setAttribute("value", SourceUtils.getSourceLabel(aUrl));
|
||||
frameUrlNode.setAttribute("crop", "center");
|
||||
frameDetailsNode.appendChild(frameUrlNode);
|
||||
|
||||
let frameDetailsSeparator = document.createElement("label");
|
||||
frameDetailsSeparator.className = "plain dbg-classic-stackframe-details-sep";
|
||||
frameDetailsSeparator.setAttribute("value", SEARCH_LINE_FLAG);
|
||||
frameDetailsNode.appendChild(frameDetailsSeparator);
|
||||
|
||||
let frameLineNode = document.createElement("label");
|
||||
frameLineNode.className = "plain dbg-classic-stackframe-details-line";
|
||||
frameLineNode.setAttribute("value", aLine);
|
||||
frameDetailsNode.appendChild(frameLineNode);
|
||||
|
||||
container.appendChild(frameTitleNode);
|
||||
container.appendChild(frameDetailsNode);
|
||||
|
||||
return container;
|
||||
},
|
||||
|
||||
/**
|
||||
* The select listener for the stackframes container.
|
||||
*/
|
||||
_onSelect: function(e) {
|
||||
let stackframeItem = this.selectedItem;
|
||||
if (stackframeItem) {
|
||||
// The container is not empty and an actual item was selected.
|
||||
// Mirror the selected item in the breadcrumbs list.
|
||||
let depth = stackframeItem.attachment.depth;
|
||||
this._mirror.selectedItem = e => e.attachment.depth == depth;
|
||||
}
|
||||
},
|
||||
|
||||
_mirror: null
|
||||
});
|
||||
|
||||
/**
|
||||
* Functions handling the filtering UI.
|
||||
*/
|
||||
|
@ -1533,3 +1593,4 @@ DebuggerView.FilteredSources = new FilteredSourcesView();
|
|||
DebuggerView.FilteredFunctions = new FilteredFunctionsView();
|
||||
DebuggerView.ChromeGlobals = new ChromeGlobalsView();
|
||||
DebuggerView.StackFrames = new StackFramesView();
|
||||
DebuggerView.StackFramesClassicList = new StackFramesClassicListView();
|
||||
|
|
|
@ -9,8 +9,6 @@ const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 1048576; // 1 MB in bytes
|
|||
const SOURCE_URL_DEFAULT_MAX_LENGTH = 64; // chars
|
||||
const STACK_FRAMES_SOURCE_URL_MAX_LENGTH = 15; // chars
|
||||
const STACK_FRAMES_SOURCE_URL_TRIM_SECTION = "center";
|
||||
const STACK_FRAMES_POPUP_SOURCE_URL_MAX_LENGTH = 32; // chars
|
||||
const STACK_FRAMES_POPUP_SOURCE_URL_TRIM_SECTION = "center";
|
||||
const STACK_FRAMES_SCROLL_DELAY = 100; // ms
|
||||
const BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH = 1000; // chars
|
||||
const BREAKPOINT_CONDITIONAL_POPUP_POSITION = "before_start";
|
||||
|
@ -59,6 +57,7 @@ let DebuggerView = {
|
|||
this.FilteredFunctions.initialize();
|
||||
this.ChromeGlobals.initialize();
|
||||
this.StackFrames.initialize();
|
||||
this.StackFramesClassicList.initialize();
|
||||
this.Sources.initialize();
|
||||
this.VariableBubble.initialize();
|
||||
this.WatchExpressions.initialize();
|
||||
|
@ -93,6 +92,7 @@ let DebuggerView = {
|
|||
this.FilteredFunctions.destroy();
|
||||
this.ChromeGlobals.destroy();
|
||||
this.StackFrames.destroy();
|
||||
this.StackFramesClassicList.destroy();
|
||||
this.Sources.destroy();
|
||||
this.VariableBubble.destroy();
|
||||
this.WatchExpressions.destroy();
|
||||
|
@ -531,7 +531,7 @@ let DebuggerView = {
|
|||
animated: true,
|
||||
delayed: true,
|
||||
callback: aCallback
|
||||
}, 0);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,14 +4,9 @@
|
|||
* 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/. */
|
||||
|
||||
/* Sources search view */
|
||||
|
||||
#globalsearch {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Instruments pane view (watch expressions, variables, events...) */
|
||||
/* Side pane views */
|
||||
|
||||
#sources-pane > tabpanels > tabpanel,
|
||||
#instruments-pane > tabpanels > tabpanel {
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
@ -21,6 +16,10 @@
|
|||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#globalsearch {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Toolbar controls */
|
||||
|
||||
.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
|
||||
|
|
|
@ -321,23 +321,35 @@
|
|||
<scrollbox id="globalsearch" orient="vertical" hidden="true"/>
|
||||
<splitter class="devtools-horizontal-splitter" hidden="true"/>
|
||||
<hbox id="debugger-widgets" flex="1">
|
||||
<vbox id="sources-pane">
|
||||
<vbox id="sources" flex="1"/>
|
||||
<toolbar id="sources-toolbar" class="devtools-toolbar">
|
||||
<hbox id="sources-controls">
|
||||
<toolbarbutton id="black-box"
|
||||
tooltiptext="&debuggerUI.sources.blackBoxTooltip;"
|
||||
command="blackBoxCommand"
|
||||
class="devtools-toolbarbutton"/>
|
||||
<toolbarbutton id="pretty-print"
|
||||
label="{}"
|
||||
tooltiptext="&debuggerUI.sources.prettyPrint;"
|
||||
class="devtools-toolbarbutton devtools-monospace"
|
||||
command="prettyPrintCommand"
|
||||
hidden="true"/>
|
||||
</hbox>
|
||||
</toolbar>
|
||||
</vbox>
|
||||
<tabbox id="sources-pane"
|
||||
class="devtools-sidebar-tabs">
|
||||
<tabs>
|
||||
<tab id="sources-tab" label="&debuggerUI.tabs.sources;"/>
|
||||
<tab id="callstack-tab" label="&debuggerUI.tabs.callstack;"/>
|
||||
</tabs>
|
||||
<tabpanels flex="1">
|
||||
<tabpanel id="sources-tabpanel">
|
||||
<vbox id="sources" flex="1"/>
|
||||
<toolbar id="sources-toolbar" class="devtools-toolbar">
|
||||
<hbox id="sources-controls">
|
||||
<toolbarbutton id="black-box"
|
||||
tooltiptext="&debuggerUI.sources.blackBoxTooltip;"
|
||||
command="blackBoxCommand"
|
||||
class="devtools-toolbarbutton"/>
|
||||
<toolbarbutton id="pretty-print"
|
||||
label="{}"
|
||||
tooltiptext="&debuggerUI.sources.prettyPrint;"
|
||||
class="devtools-toolbarbutton devtools-monospace"
|
||||
command="prettyPrintCommand"
|
||||
hidden="true"/>
|
||||
</hbox>
|
||||
</toolbar>
|
||||
</tabpanel>
|
||||
<tabpanel id="callstack-tabpanel">
|
||||
<vbox id="callstack-list" flex="1"/>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
<splitter id="sources-and-editor-splitter"
|
||||
class="devtools-side-splitter"/>
|
||||
<deck id="editor-deck" flex="4">
|
||||
|
@ -367,12 +379,12 @@
|
|||
<tab id="events-tab" label="&debuggerUI.tabs.events;"/>
|
||||
</tabs>
|
||||
<tabpanels flex="1">
|
||||
<tabpanel id="variables-tabpanel">
|
||||
<tabpanel id="variables-tabpanel" class="theme-body">
|
||||
<vbox id="expressions"/>
|
||||
<splitter class="devtools-horizontal-splitter"/>
|
||||
<vbox id="variables" flex="1"/>
|
||||
</tabpanel>
|
||||
<tabpanel id="events-tabpanel">
|
||||
<tabpanel id="events-tabpanel" class="theme-body">
|
||||
<vbox id="event-listeners" flex="1"/>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
|
|
|
@ -48,34 +48,40 @@ function test() {
|
|||
}
|
||||
|
||||
function checkNavigationWhileFocused() {
|
||||
let deferred = promise.defer();
|
||||
return Task.spawn(function() {
|
||||
yield promise.all([
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
|
||||
EventUtils.sendKey("UP", gDebugger)
|
||||
]);
|
||||
checkState({ frame: 2, source: 1, line: 6 });
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
checkState({ frame: 2, source: 1, line: 6 });
|
||||
|
||||
waitForSourceAndCaret(gPanel, "-01.js", 5).then(() => {
|
||||
yield promise.all([
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
|
||||
waitForSourceAndCaret(gPanel, "-01.js", 5),
|
||||
EventUtils.sendKey("UP", gDebugger)
|
||||
]);
|
||||
checkState({ frame: 1, source: 0, line: 5 });
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger);
|
||||
yield promise.all([
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
|
||||
EventUtils.sendKey("UP", gDebugger)
|
||||
]);
|
||||
checkState({ frame: 0, source: 0, line: 5 });
|
||||
|
||||
waitForSourceAndCaret(gPanel, "-02.js", 6).then(() => {
|
||||
checkState({ frame: 3, source: 1, line: 6 });
|
||||
|
||||
waitForSourceAndCaret(gPanel, "-01.js", 5).then(() => {
|
||||
checkState({ frame: 0, source: 0, line: 5 });
|
||||
deferred.resolve();
|
||||
});
|
||||
yield promise.all([
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
|
||||
waitForSourceAndCaret(gPanel, "-02.js", 6),
|
||||
EventUtils.sendKey("END", gDebugger)
|
||||
]);
|
||||
checkState({ frame: 3, source: 1, line: 6 });
|
||||
|
||||
yield promise.all([
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
|
||||
waitForSourceAndCaret(gPanel, "-01.js", 5),
|
||||
EventUtils.sendKey("HOME", gDebugger)
|
||||
});
|
||||
|
||||
EventUtils.sendKey("END", gDebugger)
|
||||
]);
|
||||
checkState({ frame: 0, source: 0, line: 5 });
|
||||
});
|
||||
|
||||
EventUtils.sendKey("UP", gDebugger)
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function checkState({ frame, source, line }) {
|
||||
|
|
|
@ -46,7 +46,7 @@ function performTest() {
|
|||
|
||||
is(gSources.widget.getAttribute("label"), "doc_recursion-stack.html",
|
||||
"The sources widget should have a correct label attribute.");
|
||||
is(gSources.widget.getAttribute("tooltiptext"), "example.com test",
|
||||
is(gSources.widget.getAttribute("tooltiptext"), "http://example.com",
|
||||
"The sources widget should have a correct tooltip text attribute.");
|
||||
|
||||
is(gDebugger.document.querySelectorAll("#sources .side-menu-widget-empty-notice-container").length, 0,
|
||||
|
|
|
@ -48,7 +48,7 @@ function testLocationChange() {
|
|||
|
||||
is(gSources.widget.getAttribute("label"), "doc_inline-debugger-statement.html",
|
||||
"The sources widget should have a correct label attribute.");
|
||||
is(gSources.widget.getAttribute("tooltiptext"), "example.com test",
|
||||
is(gSources.widget.getAttribute("tooltiptext"), "http://example.com",
|
||||
"The sources widget should have a correct tooltip text attribute.");
|
||||
|
||||
is(gDebugger.document.querySelectorAll("#sources .side-menu-widget-empty-notice-container").length, 0,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gFrames;
|
||||
let gFrames, gClassicFrames;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
|
@ -17,6 +17,7 @@ function test() {
|
|||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 14).then(performTest);
|
||||
gDebuggee.simpleCall();
|
||||
|
@ -28,6 +29,8 @@ function performTest() {
|
|||
"Should only be getting stack frames while paused.");
|
||||
is(gFrames.itemCount, 1,
|
||||
"Should have only one frame.");
|
||||
is(gClassicFrames.itemCount, 1,
|
||||
"Should also have only one frame in the mirrored view.");
|
||||
|
||||
resumeDebuggerThenCloseAndFinish(gPanel);
|
||||
}
|
||||
|
@ -38,4 +41,5 @@ registerCleanupFunction(function() {
|
|||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gFrames = null;
|
||||
gClassicFrames = null;
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gFrames;
|
||||
let gFrames, gClassicFrames;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
|
@ -17,6 +17,7 @@ function test() {
|
|||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 18).then(performTest);
|
||||
gDebuggee.evalCall();
|
||||
|
@ -28,21 +29,32 @@ function performTest() {
|
|||
"Should only be getting stack frames while paused.");
|
||||
is(gFrames.itemCount, 2,
|
||||
"Should have two frames.");
|
||||
is(gClassicFrames.itemCount, 2,
|
||||
"Should also have only two in the mirrored view.");
|
||||
|
||||
is(gFrames.getItemAtIndex(0).value,
|
||||
"evalCall", "Oldest frame name should be correct.");
|
||||
is(gFrames.getItemAtIndex(0).description,
|
||||
TAB_URL, "Oldest frame url should be correct.");
|
||||
is(gClassicFrames.getItemAtIndex(0).value,
|
||||
TAB_URL, "Oldest frame name is mirrored correctly.");
|
||||
|
||||
is(gFrames.getItemAtIndex(1).value,
|
||||
"(eval)", "Newest frame name should be correct.");
|
||||
is(gFrames.getItemAtIndex(1).description,
|
||||
TAB_URL, "Newest frame url should be correct.");
|
||||
is(gClassicFrames.getItemAtIndex(1).value,
|
||||
TAB_URL, "Newest frame name is mirrored correctly.");
|
||||
|
||||
is(gFrames.selectedIndex, 1,
|
||||
"Newest frame should be selected by default.");
|
||||
is(gClassicFrames.selectedIndex, 0,
|
||||
"Newest frame should be selected by default in the mirrored view.");
|
||||
|
||||
isnot(gFrames.selectedIndex, 0,
|
||||
"Oldest frame should not be selected.");
|
||||
isnot(gClassicFrames.selectedIndex, 1,
|
||||
"Oldest frame should not be selected in the mirrored view.");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gFrames.getItemAtIndex(0).target,
|
||||
|
@ -50,8 +62,13 @@ function performTest() {
|
|||
|
||||
isnot(gFrames.selectedIndex, 1,
|
||||
"Newest frame should not be selected after click.");
|
||||
isnot(gClassicFrames.selectedIndex, 0,
|
||||
"Newest frame in the mirrored view should not be selected.");
|
||||
|
||||
is(gFrames.selectedIndex, 0,
|
||||
"Oldest frame should be selected after click.");
|
||||
is(gClassicFrames.selectedIndex, 1,
|
||||
"Oldest frame in the mirrored view should be selected.");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gFrames.getItemAtIndex(1).target.querySelector(".dbg-stackframe-title"),
|
||||
|
@ -59,8 +76,13 @@ function performTest() {
|
|||
|
||||
is(gFrames.selectedIndex, 1,
|
||||
"Newest frame should be selected after click inside the newest frame.");
|
||||
is(gClassicFrames.selectedIndex, 0,
|
||||
"Newest frame in the mirrored view should be selected.");
|
||||
|
||||
isnot(gFrames.selectedIndex, 0,
|
||||
"Oldest frame should not be selected after click inside the newest frame.");
|
||||
isnot(gClassicFrames.selectedIndex, 1,
|
||||
"Oldest frame in the mirrored view should not be selected.");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gFrames.getItemAtIndex(0).target.querySelector(".dbg-stackframe-details"),
|
||||
|
@ -68,8 +90,13 @@ function performTest() {
|
|||
|
||||
isnot(gFrames.selectedIndex, 1,
|
||||
"Newest frame should not be selected after click inside the oldest frame.");
|
||||
isnot(gClassicFrames.selectedIndex, 0,
|
||||
"Newest frame in the mirrored view should not be selected.");
|
||||
|
||||
is(gFrames.selectedIndex, 0,
|
||||
"Oldest frame should be selected after click inside the oldest frame.");
|
||||
is(gClassicFrames.selectedIndex, 1,
|
||||
"Oldest frame in the mirrored view should be selected.");
|
||||
|
||||
resumeDebuggerThenCloseAndFinish(gPanel);
|
||||
}
|
||||
|
@ -80,4 +107,5 @@ registerCleanupFunction(function() {
|
|||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gFrames = null;
|
||||
gClassicFrames = null;
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gFrames, gFramesScrollingInterval;
|
||||
let gFrames, gClassicFrames, gFramesScrollingInterval;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
|
@ -17,6 +17,7 @@ function test() {
|
|||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 26).then(performTest);
|
||||
|
||||
|
@ -30,14 +31,20 @@ function performTest() {
|
|||
"Should only be getting stack frames while paused.");
|
||||
is(gFrames.itemCount, gDebugger.gCallStackPageSize,
|
||||
"Should have only the max limit of frames.");
|
||||
is(gClassicFrames.itemCount, gDebugger.gCallStackPageSize,
|
||||
"Should have only the max limit of frames in the mirrored view as well.")
|
||||
|
||||
gDebugger.gThreadClient.addOneTimeListener("framesadded", () => {
|
||||
is(gFrames.itemCount, gDebugger.gCallStackPageSize * 2,
|
||||
"Should now have twice the max limit of frames.");
|
||||
is(gClassicFrames.itemCount, gDebugger.gCallStackPageSize * 2,
|
||||
"Should now have twice the max limit of frames in the mirrored view as well.");
|
||||
|
||||
gDebugger.gThreadClient.addOneTimeListener("framesadded", () => {
|
||||
is(gFrames.itemCount, gDebuggee.gRecurseLimit,
|
||||
"Should have reached the recurse limit.");
|
||||
is(gClassicFrames.itemCount, gDebuggee.gRecurseLimit,
|
||||
"Should have reached the recurse limit in the mirrored view as well.");
|
||||
|
||||
gDebugger.gThreadClient.resume(() => {
|
||||
window.clearInterval(gFramesScrollingInterval);
|
||||
|
@ -60,4 +67,5 @@ registerCleanupFunction(function() {
|
|||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gFrames = null;
|
||||
gClassicFrames = null;
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gFrames;
|
||||
let gFrames, gClassicFrames;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
|
@ -17,6 +17,7 @@ function test() {
|
|||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 18).then(performTest);
|
||||
gDebuggee.evalCall();
|
||||
|
@ -28,10 +29,14 @@ function performTest() {
|
|||
"Should only be getting stack frames while paused.");
|
||||
is(gFrames.itemCount, 2,
|
||||
"Should have two frames.");
|
||||
is(gClassicFrames.itemCount, 2,
|
||||
"Should also have two frames in the mirrored view.");
|
||||
|
||||
gDebugger.once(gDebugger.EVENTS.AFTER_FRAMES_CLEARED, () => {
|
||||
is(gFrames.itemCount, 0,
|
||||
"Should have no frames after resume.");
|
||||
is(gClassicFrames.itemCount, 0,
|
||||
"Should also have no frames in the mirrored view after resume.");
|
||||
|
||||
closeDebuggerAndFinish(gPanel);
|
||||
}, true);
|
||||
|
@ -45,4 +50,5 @@ registerCleanupFunction(function() {
|
|||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gFrames = null;
|
||||
gClassicFrames = null;
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gEditor, gSources, gFrames;
|
||||
let gEditor, gSources, gFrames, gClassicFrames;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
|
@ -20,6 +20,7 @@ function test() {
|
|||
gEditor = gDebugger.DebuggerView.editor;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
|
||||
.then(initialChecks)
|
||||
|
@ -40,6 +41,8 @@ function initialChecks() {
|
|||
"Should only be getting stack frames while paused.");
|
||||
is(gFrames.itemCount, 4,
|
||||
"Should have four frames.");
|
||||
is(gClassicFrames.itemCount, 4,
|
||||
"Should also have four frames in the mirrored view.");
|
||||
}
|
||||
|
||||
function testNewestTwoFrames() {
|
||||
|
@ -47,6 +50,8 @@ function testNewestTwoFrames() {
|
|||
|
||||
is(gFrames.selectedIndex, 3,
|
||||
"Newest frame should be selected by default.");
|
||||
is(gClassicFrames.selectedIndex, 0,
|
||||
"Newest frame should be selected in the mirrored view as well.");
|
||||
is(gSources.selectedIndex, 1,
|
||||
"The second source is selected in the widget.");
|
||||
ok(isCaretPos(gPanel, 6),
|
||||
|
@ -63,6 +68,8 @@ function testNewestTwoFrames() {
|
|||
|
||||
is(gFrames.selectedIndex, 2,
|
||||
"Third frame should be selected after click.");
|
||||
is(gClassicFrames.selectedIndex, 1,
|
||||
"Third frame should be selected in the mirrored view as well.");
|
||||
is(gSources.selectedIndex, 1,
|
||||
"The second source is still selected in the widget.");
|
||||
ok(isCaretPos(gPanel, 6),
|
||||
|
@ -83,9 +90,11 @@ function testNewestTwoFrames() {
|
|||
function testOldestTwoFrames() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
waitForSourceAndCaret(gPanel, "-01.js", 5).then(() => {
|
||||
waitForSourceAndCaret(gPanel, "-01.js", 5).then(waitForTick).then(() => {
|
||||
is(gFrames.selectedIndex, 1,
|
||||
"Second frame should be selected after click.");
|
||||
is(gClassicFrames.selectedIndex, 2,
|
||||
"Second frame should be selected in the mirrored view as well.");
|
||||
is(gSources.selectedIndex, 0,
|
||||
"The first source is now selected in the widget.");
|
||||
ok(isCaretPos(gPanel, 5),
|
||||
|
@ -102,6 +111,8 @@ function testOldestTwoFrames() {
|
|||
|
||||
is(gFrames.selectedIndex, 0,
|
||||
"Oldest frame should be selected after click.");
|
||||
is(gClassicFrames.selectedIndex, 3,
|
||||
"Oldest frame should be selected in the mirrored view as well.");
|
||||
is(gSources.selectedIndex, 0,
|
||||
"The first source is still selected in the widget.");
|
||||
ok(isCaretPos(gPanel, 5),
|
||||
|
@ -118,7 +129,7 @@ function testOldestTwoFrames() {
|
|||
});
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gFrames.getItemAtIndex(1).target,
|
||||
gDebugger.document.querySelector("#stackframe-2"),
|
||||
gDebugger);
|
||||
|
||||
return deferred.promise;
|
||||
|
@ -130,6 +141,8 @@ function testAfterResume() {
|
|||
gDebugger.once(gDebugger.EVENTS.AFTER_FRAMES_CLEARED, () => {
|
||||
is(gFrames.itemCount, 0,
|
||||
"Should have no frames after resume.");
|
||||
is(gClassicFrames.itemCount, 0,
|
||||
"Should have no frames in the mirrored view as well.");
|
||||
ok(isCaretPos(gPanel, 5),
|
||||
"Editor caret location is correct after resume.");
|
||||
is(gEditor.getDebugLocation(), null,
|
||||
|
@ -150,5 +163,6 @@ registerCleanupFunction(function() {
|
|||
gDebugger = null;
|
||||
gEditor = null;
|
||||
gFrames = null;
|
||||
gClassicFrames = null;
|
||||
});
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gEditor, gSources, gFrames;
|
||||
let gEditor, gSources, gFrames, gClassicFrames;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
|
@ -20,6 +20,7 @@ function test() {
|
|||
gEditor = gDebugger.DebuggerView.editor;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest);
|
||||
gDebuggee.firstCall();
|
||||
|
@ -29,16 +30,20 @@ function test() {
|
|||
function performTest() {
|
||||
is(gFrames.selectedIndex, 3,
|
||||
"Newest frame should be selected by default.");
|
||||
is(gClassicFrames.selectedIndex, 0,
|
||||
"Newest frame should also be selected in the mirrored view.");
|
||||
is(gSources.selectedIndex, 1,
|
||||
"The second source is selected in the widget.");
|
||||
is(gEditor.getText().search(/firstCall/), -1,
|
||||
"The first source is not displayed.");
|
||||
is(gEditor.getText().search(/debugger/), 172,
|
||||
"The second source is displayed.")
|
||||
"The second source is displayed.");
|
||||
|
||||
waitForSourceAndCaret(gPanel, "-01.js", 6).then(() => {
|
||||
waitForSourceAndCaret(gPanel, "-01.js", 6).then(waitForTick).then(() => {
|
||||
is(gFrames.selectedIndex, 0,
|
||||
"Oldest frame should be selected after click.");
|
||||
is(gClassicFrames.selectedIndex, 3,
|
||||
"Oldest frame should also be selected in the mirrored view.");
|
||||
is(gSources.selectedIndex, 0,
|
||||
"The first source is now selected in the widget.");
|
||||
is(gEditor.getText().search(/firstCall/), 118,
|
||||
|
@ -46,7 +51,24 @@ function performTest() {
|
|||
is(gEditor.getText().search(/debugger/), -1,
|
||||
"The second source is not displayed.");
|
||||
|
||||
resumeDebuggerThenCloseAndFinish(gPanel);
|
||||
waitForSourceAndCaret(gPanel, "-02.js", 6).then(waitForTick).then(() => {
|
||||
is(gFrames.selectedIndex, 3,
|
||||
"Newest frame should be selected again after click.");
|
||||
is(gClassicFrames.selectedIndex, 0,
|
||||
"Newest frame should also be selected again in the mirrored view.");
|
||||
is(gSources.selectedIndex, 1,
|
||||
"The second source is selected in the widget.");
|
||||
is(gEditor.getText().search(/firstCall/), -1,
|
||||
"The first source is not displayed.");
|
||||
is(gEditor.getText().search(/debugger/), 172,
|
||||
"The second source is displayed.");
|
||||
|
||||
resumeDebuggerThenCloseAndFinish(gPanel);
|
||||
});
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gDebugger.document.querySelector("#classic-stackframe-0"),
|
||||
gDebugger);
|
||||
});
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
|
@ -62,4 +84,5 @@ registerCleanupFunction(function() {
|
|||
gEditor = null;
|
||||
gSources = null;
|
||||
gFrames = null;
|
||||
gClassicFrames = null;
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gEditor, gSources, gFrames, gToolbar;
|
||||
let gEditor, gSources, gFrames, gClassicFrames, gToolbar;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
|
@ -21,6 +21,7 @@ function test() {
|
|||
gEditor = gDebugger.DebuggerView.editor;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
|
||||
gToolbar = gDebugger.DebuggerView.Toolbar;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest);
|
||||
|
@ -53,13 +54,15 @@ function performTest() {
|
|||
|
||||
function selectBottomFrame() {
|
||||
let updated = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES);
|
||||
gFrames.selectedIndex = 0;
|
||||
gClassicFrames.selectedIndex = gClassicFrames.itemCount - 1;
|
||||
return updated.then(waitForTick);
|
||||
}
|
||||
|
||||
function testBottomFrame(debugLocation) {
|
||||
is(gFrames.selectedIndex, 0,
|
||||
"Oldest frame should be selected after click.");
|
||||
is(gClassicFrames.selectedIndex, gFrames.itemCount - 1,
|
||||
"Oldest frame should also be selected in the mirrored view.");
|
||||
is(gSources.selectedIndex, 0,
|
||||
"The first source is now selected in the widget.");
|
||||
is(gEditor.getText().search(/firstCall/), 118,
|
||||
|
@ -82,6 +85,8 @@ function performTest() {
|
|||
function testTopFrame(frameIndex) {
|
||||
is(gFrames.selectedIndex, frameIndex,
|
||||
"Topmost frame should be selected after click.");
|
||||
is(gClassicFrames.selectedIndex, gFrames.itemCount - frameIndex - 1,
|
||||
"Topmost frame should also be selected in the mirrored view.");
|
||||
is(gSources.selectedIndex, 1,
|
||||
"The second source is now selected in the widget.");
|
||||
is(gEditor.getText().search(/firstCall/), -1,
|
||||
|
@ -99,5 +104,6 @@ registerCleanupFunction(function() {
|
|||
gEditor = null;
|
||||
gSources = null;
|
||||
gFrames = null;
|
||||
gClassicFrames = null;
|
||||
gToolbar = null;
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"use strict";
|
||||
|
||||
const { Cc, Ci, Cu, Cr } = require("chrome");
|
||||
const promise = require("sdk/core/promise");
|
||||
const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { WebGLFront } = require("devtools/server/actors/webgl");
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
|
|||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
|
||||
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
|
||||
const promise = require("sdk/core/promise");
|
||||
const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
|
||||
const Editor = require("devtools/sourceeditor/editor");
|
||||
|
@ -28,7 +28,13 @@ const EVENTS = {
|
|||
SOURCES_SHOWN: "ShaderEditor:SourcesShown",
|
||||
|
||||
// When a shader's source was edited and compiled via the editor.
|
||||
SHADER_COMPILED: "ShaderEditor:ShaderCompiled"
|
||||
SHADER_COMPILED: "ShaderEditor:ShaderCompiled",
|
||||
|
||||
// When the UI is reset from tab navigation
|
||||
UI_RESET: "ShaderEditor:UIReset",
|
||||
|
||||
// When the editor's error markers are all removed
|
||||
EDITOR_ERROR_MARKERS_REMOVED: "ShaderEditor:EditorCleaned"
|
||||
};
|
||||
|
||||
const STRINGS_URI = "chrome://browser/locale/devtools/shadereditor.properties"
|
||||
|
@ -114,15 +120,17 @@ let EventsHandler = {
|
|||
_onTabNavigated: function(event) {
|
||||
switch (event) {
|
||||
case "will-navigate": {
|
||||
// Make sure the backend is prepared to handle WebGL contexts.
|
||||
gFront.setup({ reload: false });
|
||||
Task.spawn(function() {
|
||||
// Make sure the backend is prepared to handle WebGL contexts.
|
||||
gFront.setup({ reload: false });
|
||||
|
||||
// Reset UI.
|
||||
ShadersListView.empty();
|
||||
ShadersEditorsView.setText({ vs: "", fs: "" });
|
||||
$("#reload-notice").hidden = true;
|
||||
$("#waiting-notice").hidden = false;
|
||||
$("#content").hidden = true;
|
||||
// Reset UI.
|
||||
ShadersListView.empty();
|
||||
$("#reload-notice").hidden = true;
|
||||
$("#waiting-notice").hidden = false;
|
||||
yield ShadersEditorsView.setText({ vs: "", fs: "" });
|
||||
$("#content").hidden = true;
|
||||
}).then(() => window.emit(EVENTS.UI_RESET));
|
||||
break;
|
||||
}
|
||||
case "navigate": {
|
||||
|
@ -272,13 +280,16 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
|
|||
]);
|
||||
}
|
||||
function showSources([vertexShaderText, fragmentShaderText]) {
|
||||
ShadersEditorsView.setText({
|
||||
return ShadersEditorsView.setText({
|
||||
vs: vertexShaderText,
|
||||
fs: fragmentShaderText
|
||||
});
|
||||
}
|
||||
|
||||
getShaders().then(getSources).then(showSources).then(null, Cu.reportError);
|
||||
getShaders()
|
||||
.then(getSources)
|
||||
.then(showSources)
|
||||
.then(null, Cu.reportError);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -351,19 +362,24 @@ let ShadersEditorsView = {
|
|||
* An object containing the following properties
|
||||
* - vs: the vertex shader source code
|
||||
* - fs: the fragment shader source code
|
||||
* @return object
|
||||
* A promise resolving upon completion of text setting.
|
||||
*/
|
||||
setText: function(sources) {
|
||||
let view = this;
|
||||
function setTextAndClearHistory(editor, text) {
|
||||
editor.setText(text);
|
||||
editor.clearHistory();
|
||||
}
|
||||
|
||||
this._toggleListeners("off");
|
||||
this._getEditor("vs").then(e => setTextAndClearHistory(e, sources.vs));
|
||||
this._getEditor("fs").then(e => setTextAndClearHistory(e, sources.fs));
|
||||
this._toggleListeners("on");
|
||||
|
||||
window.emit(EVENTS.SOURCES_SHOWN, sources);
|
||||
return Task.spawn(function() {
|
||||
yield view._toggleListeners("off");
|
||||
yield promise.all([
|
||||
view._getEditor("vs").then(e => setTextAndClearHistory(e, sources.vs)),
|
||||
view._getEditor("fs").then(e => setTextAndClearHistory(e, sources.fs))
|
||||
]);
|
||||
yield view._toggleListeners("on");
|
||||
}).then(() => window.emit(EVENTS.SOURCES_SHOWN, sources));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -372,10 +388,12 @@ let ShadersEditorsView = {
|
|||
* @param string type
|
||||
* Specifies for which shader type should an editor be retrieved,
|
||||
* either are "vs" for a vertex, or "fs" for a fragment shader.
|
||||
* @return object
|
||||
* Returns a promise that resolves to an editor instance
|
||||
*/
|
||||
_getEditor: function(type) {
|
||||
if ($("#content").hidden) {
|
||||
return promise.reject(null);
|
||||
return promise.reject(new Error("Shader Editor is still waiting for a WebGL context to be created."));
|
||||
}
|
||||
if (this._editorPromises.has(type)) {
|
||||
return this._editorPromises.get(type);
|
||||
|
@ -399,14 +417,16 @@ let ShadersEditorsView = {
|
|||
*
|
||||
* @param string flag
|
||||
* Either "on" to enable the event listeners, "off" to disable them.
|
||||
* @return object
|
||||
* A promise resolving upon completion of toggling the listeners.
|
||||
*/
|
||||
_toggleListeners: function(flag) {
|
||||
["vs", "fs"].forEach(type => {
|
||||
this._getEditor(type).then(editor => {
|
||||
return promise.all(["vs", "fs"].map(type => {
|
||||
return this._getEditor(type).then(editor => {
|
||||
editor[flag]("focus", this["_" + type + "Focused"]);
|
||||
editor[flag]("change", this["_" + type + "Changed"]);
|
||||
});
|
||||
});
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -486,7 +506,7 @@ let ShadersEditorsView = {
|
|||
}
|
||||
function sanitizeValidMatches(e) {
|
||||
return {
|
||||
// Drivers might yield retarded line numbers under some obscure
|
||||
// Drivers might yield confusing line numbers under some obscure
|
||||
// circumstances. Don't throw the errors away in those cases,
|
||||
// just display them on the currently edited line.
|
||||
line: e.lineMatch[0] > lineCount ? currentLine : e.lineMatch[0] - 1,
|
||||
|
@ -554,6 +574,7 @@ let ShadersEditorsView = {
|
|||
editor.removeAllMarkers("errors");
|
||||
this._errors[type].forEach(e => editor.removeLineClass(e.line));
|
||||
this._errors[type].length = 0;
|
||||
window.emit(EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@ function ifWebGLSupported() {
|
|||
yield reloaded;
|
||||
|
||||
let navigated = navigate(target, MULTIPLE_CONTEXTS_URL);
|
||||
let secondProgram = yield once(gFront, "program-linked");
|
||||
let thirdProgram = yield once(gFront, "program-linked");
|
||||
let [secondProgram, thirdProgram] = yield getPrograms(gFront, 2);
|
||||
yield navigated;
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
|
@ -56,9 +55,3 @@ function ifWebGLSupported() {
|
|||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -8,14 +8,18 @@
|
|||
|
||||
function ifWebGLSupported() {
|
||||
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
|
||||
let { gFront, ShadersEditorsView } = panel.panelWin;
|
||||
let { gFront, ShadersEditorsView, EVENTS } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
yield promise.all([
|
||||
once(gFront, "program-linked"),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]);
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
||||
|
||||
is(vsEditor.getText().indexOf("gl_Position"), 170,
|
||||
"The vertex shader editor contains the correct text.");
|
||||
is(fsEditor.getText().indexOf("gl_FragColor"), 97,
|
||||
|
|
|
@ -11,55 +11,61 @@ function ifWebGLSupported() {
|
|||
let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
yield promise.all([
|
||||
once(gFront, "program-linked"),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]);
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
||||
vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
|
||||
let vertError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
let [, vertError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
info("Error marks added in the vertex shader editor.");
|
||||
|
||||
vsEditor.insertText(" ", { line: 1, ch: 0 });
|
||||
yield once(panel.panelWin, EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
|
||||
is(vsEditor.getText(1), " precision lowp float;", "Typed space.");
|
||||
checkHasVertFirstError(false, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
info("Error marks removed while typing in the vertex shader editor.");
|
||||
|
||||
let vertError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
[, vertError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
info("Error marks were re-added after recompiling the vertex shader.");
|
||||
|
||||
fsEditor.replaceText("vec4", { line: 2, ch: 14 }, { line: 2, ch: 18 });
|
||||
let fragError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
let [, fragError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks added in the fragment shader editor.");
|
||||
|
||||
fsEditor.insertText(" ", { line: 1, ch: 0 });
|
||||
yield once(panel.panelWin, EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
|
||||
is(fsEditor.getText(1), " precision lowp float;", "Typed space.");
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(false, fragError);
|
||||
info("Error marks removed while typing in the fragment shader editor.");
|
||||
|
||||
let fragError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
[, fragError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks were re-added after recompiling the fragment shader.");
|
||||
|
||||
vsEditor.replaceText("2", { line: 3, ch: 19 }, { line: 3, ch: 20 });
|
||||
yield once(panel.panelWin, EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
|
||||
checkHasVertFirstError(false, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks removed while typing in the vertex shader editor again.");
|
||||
|
||||
let vertError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
[, vertError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(true, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
|
@ -148,9 +154,3 @@ function ifWebGLSupported() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, (aName, aData) => deferred.resolve(aData));
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,10 @@ function ifWebGLSupported() {
|
|||
let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
yield promise.all([
|
||||
once(gFront, "program-linked"),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]);
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
@ -51,9 +54,3 @@ function ifWebGLSupported() {
|
|||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, (aName, aData) => deferred.resolve(aData));
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,13 @@
|
|||
|
||||
function ifWebGLSupported() {
|
||||
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
|
||||
let { gFront, $, ShadersListView, ShadersEditorsView } = panel.panelWin;
|
||||
let { gFront, $, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
yield promise.all([
|
||||
once(gFront, "program-linked"),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]);
|
||||
|
||||
is($("#reload-notice").hidden, true,
|
||||
"The 'reload this page' notice should be hidden after linking.");
|
||||
|
@ -39,6 +42,7 @@ function ifWebGLSupported() {
|
|||
navigate(target, "about:blank");
|
||||
|
||||
yield navigating;
|
||||
yield once(panel.panelWin, EVENTS.UI_RESET);
|
||||
|
||||
is($("#reload-notice").hidden, true,
|
||||
"The 'reload this page' notice should be hidden while navigating.");
|
||||
|
@ -54,19 +58,17 @@ function ifWebGLSupported() {
|
|||
is(ShadersListView.selectedIndex, -1,
|
||||
"The shaders list has a negative index.");
|
||||
|
||||
try {
|
||||
yield ShadersEditorsView._getEditor("vs");
|
||||
yield ShadersEditorsView._getEditor("vs").then(() => {
|
||||
ok(false, "The promise for a vertex shader editor should be rejected.");
|
||||
} catch (e) {
|
||||
}, () => {
|
||||
ok(true, "The vertex shader editors wasn't initialized.");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
yield ShadersEditorsView._getEditor("fs");
|
||||
yield ShadersEditorsView._getEditor("fs").then(() => {
|
||||
ok(false, "The promise for a fragment shader editor should be rejected.");
|
||||
} catch (e) {
|
||||
}, () => {
|
||||
ok(true, "The fragment shader editors wasn't initialized.");
|
||||
}
|
||||
});
|
||||
|
||||
yield navigated;
|
||||
|
||||
|
|
|
@ -14,8 +14,10 @@ function ifWebGLSupported() {
|
|||
});
|
||||
|
||||
reload(target);
|
||||
let firstProgramActor = yield once(gFront, "program-linked");
|
||||
let secondProgramActor = yield once(gFront, "program-linked");
|
||||
let [firstProgramActor, secondProgramActor] = yield promise.all([
|
||||
getPrograms(gFront, 2),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]).then(([programs]) => programs);
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
|
|
@ -11,8 +11,10 @@ function ifWebGLSupported() {
|
|||
let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
let firstProgramActor = yield once(gFront, "program-linked");
|
||||
let secondProgramActor = yield once(gFront, "program-linked");
|
||||
let [firstProgramActor, secondProgramActor] = yield promise.all([
|
||||
getPrograms(gFront, 2),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]).then(([programs]) => programs);
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
|
||||
|
@ -59,9 +61,3 @@ function getBlackBoxCheckbox(aPanel, aIndex) {
|
|||
return aPanel.panelWin.document.querySelectorAll(
|
||||
".side-menu-widget-item-checkbox")[aIndex];
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,14 @@
|
|||
|
||||
function ifWebGLSupported() {
|
||||
let [target, debuggee, panel] = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
|
||||
let { gFront, ShadersListView, ShadersEditorsView } = panel.panelWin;
|
||||
let { EVENTS, gFront, ShadersListView, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
let programActor = yield once(gFront, "program-linked");
|
||||
let [programActor] = yield promise.all([
|
||||
getPrograms(gFront, 1),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]).then(([programs]) => programs);
|
||||
|
||||
let programItem = ShadersListView.selectedItem;
|
||||
|
||||
is(programItem.attachment.programActor, programActor,
|
||||
|
|
|
@ -14,8 +14,10 @@ function ifWebGLSupported() {
|
|||
});
|
||||
|
||||
reload(target);
|
||||
let firstProgramActor = yield once(gFront, "program-linked");
|
||||
let secondProgramActor = yield once(gFront, "program-linked");
|
||||
let [firstProgramActor, secondProgramActor] = yield promise.all([
|
||||
getPrograms(gFront, 2),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]).then(([programs]) => programs);
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
@ -89,9 +91,3 @@ function getBlackBoxCheckbox(aPanel, aIndex) {
|
|||
return aPanel.panelWin.document.querySelectorAll(
|
||||
".side-menu-widget-item-checkbox")[aIndex];
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,10 @@ function ifWebGLSupported() {
|
|||
let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
let firstProgramActor = yield once(gFront, "program-linked");
|
||||
let secondProgramActor = yield once(gFront, "program-linked");
|
||||
let [firstProgramActor, secondProgramActor] = yield promise.all([
|
||||
getPrograms(gFront, 2),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]).then(([programs]) => programs);
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
|
||||
|
@ -45,9 +47,3 @@ function getItemLabel(aPanel, aIndex) {
|
|||
return aPanel.panelWin.document.querySelectorAll(
|
||||
".side-menu-widget-item-label")[aIndex];
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -19,23 +19,17 @@ function ifWebGLSupported() {
|
|||
|
||||
reload(target);
|
||||
|
||||
let firstProgramActor = yield once(gFront, "program-linked");
|
||||
|
||||
is(ShadersListView.itemCount, 1,
|
||||
"The shaders list contains one entry.");
|
||||
is(ShadersListView.selectedItem, ShadersListView.items[0],
|
||||
"The shaders list has a correct item selected.");
|
||||
is(ShadersListView.selectedIndex, 0,
|
||||
"The shaders list has a correct index selected.");
|
||||
|
||||
let secondProgramActor = yield once(gFront, "program-linked");
|
||||
|
||||
is(ShadersListView.itemCount, 2,
|
||||
"The shaders list contains two entries.");
|
||||
is(ShadersListView.selectedItem, ShadersListView.items[0],
|
||||
"The shaders list has a correct item selected.");
|
||||
is(ShadersListView.selectedIndex, 0,
|
||||
"The shaders list has a correct index selected.");
|
||||
let [firstProgramActor, secondProgramActor] = yield promise.all([
|
||||
getPrograms(gFront, 2, (actors) => {
|
||||
// Fired upon each actor addition, we want to check only
|
||||
// after the first actor has been added so we can test state
|
||||
if (actors.length === 1)
|
||||
checkFirstProgram();
|
||||
if (actors.length === 2)
|
||||
checkSecondProgram();
|
||||
}),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]).then(([programs, ]) => programs);
|
||||
|
||||
is(ShadersListView.labels[0], L10N.getFormatStr("shadersList.programLabel", 0),
|
||||
"The correct first label is shown in the shaders list.");
|
||||
|
@ -73,10 +67,21 @@ function ifWebGLSupported() {
|
|||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, deferred.resolve);
|
||||
return deferred.promise;
|
||||
function checkFirstProgram () {
|
||||
is(ShadersListView.itemCount, 1,
|
||||
"The shaders list contains one entry.");
|
||||
is(ShadersListView.selectedItem, ShadersListView.items[0],
|
||||
"The shaders list has a correct item selected.");
|
||||
is(ShadersListView.selectedIndex, 0,
|
||||
"The shaders list has a correct index selected.");
|
||||
}
|
||||
function checkSecondProgram () {
|
||||
is(ShadersListView.itemCount, 2,
|
||||
"The shaders list contains two entries.");
|
||||
is(ShadersListView.selectedItem, ShadersListView.items[0],
|
||||
"The shaders list has a correct item selected.");
|
||||
is(ShadersListView.selectedIndex, 0,
|
||||
"The shaders list has a correct index selected.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,10 @@ function ifWebGLSupported() {
|
|||
let { gFront, $, EVENTS, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
yield promise.all([
|
||||
once(gFront, "program-linked"),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]);
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
@ -68,9 +71,3 @@ function ifWebGLSupported() {
|
|||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -11,13 +11,16 @@ function ifWebGLSupported() {
|
|||
let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
yield promise.all([
|
||||
once(gFront, "program-linked"),
|
||||
once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
]);
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
||||
vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
|
||||
let error = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
let [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
|
||||
ok(error,
|
||||
"The new vertex shader source was compiled with errors.");
|
||||
|
@ -33,7 +36,7 @@ function ifWebGLSupported() {
|
|||
"An assignment error is contained in the linkage status.");
|
||||
|
||||
fsEditor.replaceText("vec4", { line: 2, ch: 14 }, { line: 2, ch: 18 });
|
||||
let error = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
let [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
|
||||
ok(error,
|
||||
"The new fragment shader source was compiled with errors.");
|
||||
|
@ -50,11 +53,11 @@ function ifWebGLSupported() {
|
|||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
|
||||
|
||||
vsEditor.replaceText("vec4", { line: 7, ch: 22 }, { line: 7, ch: 26 });
|
||||
let error = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
let [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
ok(!error, "The new vertex shader source was compiled successfully.");
|
||||
|
||||
fsEditor.replaceText("vec3", { line: 2, ch: 14 }, { line: 2, ch: 18 });
|
||||
let error = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
let [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
ok(!error, "The new fragment shader source was compiled successfully.");
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
|
||||
|
@ -63,9 +66,3 @@ function ifWebGLSupported() {
|
|||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, (aName, aData) => deferred.resolve(aData));
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,13 @@ function ifWebGLSupported() {
|
|||
let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
let firstProgramActor = yield once(gFront, "program-linked");
|
||||
let secondProgramActor = yield once(gFront, "program-linked");
|
||||
|
||||
yield promise.all([
|
||||
once(gFront, "program-linked"),
|
||||
once(gFront, "program-linked")
|
||||
]);
|
||||
|
||||
yield once(panel.panelWin, EVENTS.SOURCES_SHOWN)
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
@ -78,9 +83,3 @@ function ifWebGLSupported() {
|
|||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@ function ifWebGLSupported() {
|
|||
let [target, debuggee, front] = yield initBackend(MULTIPLE_CONTEXTS_URL);
|
||||
front.setup({ reload: true });
|
||||
|
||||
let firstProgramActor = yield once(front, "program-linked");
|
||||
let secondProgramActor = yield once(front, "program-linked");
|
||||
let [firstProgramActor, secondProgramActor] = yield getPrograms(front, 2);
|
||||
|
||||
isnot(firstProgramActor, secondProgramActor,
|
||||
"Two distinct program actors were recevide from two separate contexts.");
|
||||
|
|
|
@ -10,8 +10,8 @@ function ifWebGLSupported() {
|
|||
let [target, debuggee, front] = yield initBackend(MULTIPLE_CONTEXTS_URL);
|
||||
front.setup({ reload: true });
|
||||
|
||||
let firstProgramActor = yield once(front, "program-linked");
|
||||
let secondProgramActor = yield once(front, "program-linked");
|
||||
let [firstProgramActor, secondProgramActor] = yield getPrograms(front, 2);
|
||||
|
||||
let firstFragmentShader = yield firstProgramActor.getFragmentShader();
|
||||
let secondFragmentShader = yield secondProgramActor.getFragmentShader();
|
||||
|
||||
|
|
|
@ -16,8 +16,7 @@ function ifWebGLSupported() {
|
|||
ok(true, "The cached programs behave correctly before the navigation.");
|
||||
|
||||
navigate(target, MULTIPLE_CONTEXTS_URL);
|
||||
let secondProgram = yield once(front, "program-linked");
|
||||
let thirdProgram = yield once(front, "program-linked");
|
||||
let [secondProgram, thirdProgram] = yield getPrograms(front, 2);
|
||||
yield checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
|
||||
yield checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
|
||||
ok(true, "The cached programs behave correctly after the navigation.");
|
||||
|
|
|
@ -23,8 +23,7 @@ function ifWebGLSupported() {
|
|||
// 1. Perform a simple navigation.
|
||||
|
||||
navigate(target, MULTIPLE_CONTEXTS_URL);
|
||||
let secondProgram = yield once(front, "program-linked");
|
||||
let thirdProgram = yield once(front, "program-linked");
|
||||
let [secondProgram, thirdProgram] = yield getPrograms(front, 2);
|
||||
let programs = yield front.getPrograms();
|
||||
is(programs.length, 2,
|
||||
"The second and third programs should be returned by a call to getPrograms().");
|
||||
|
@ -65,8 +64,7 @@ function ifWebGLSupported() {
|
|||
is(programs.length, 0,
|
||||
"There should be no cached program actors yet.");
|
||||
|
||||
yield once(front, "program-linked");
|
||||
yield once(front, "program-linked");
|
||||
yield getPrograms(front, 2);
|
||||
yield globalCreated;
|
||||
let programs = yield front.getPrograms();
|
||||
is(programs.length, 2,
|
||||
|
|
|
@ -10,8 +10,7 @@ function ifWebGLSupported() {
|
|||
let [target, debuggee, front] = yield initBackend(OVERLAPPING_GEOMETRY_CANVAS_URL);
|
||||
front.setup({ reload: true });
|
||||
|
||||
let firstProgramActor = yield once(front, "program-linked");
|
||||
let secondProgramActor = yield once(front, "program-linked");
|
||||
let [firstProgramActor, secondProgramActor] = yield getPrograms(front, 2);
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
window.onload = function() {
|
||||
canvas = document.querySelector("canvas");
|
||||
gl = canvas.getContext("webgl");
|
||||
gl = canvas.getContext("webgl", { preserveDrawingBuffer: true });
|
||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
initProgram(0);
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
window.onload = function() {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
canvas[i] = document.querySelector("#canvas" + (i + 1));
|
||||
gl[i] = canvas[i].getContext("webgl");
|
||||
gl[i] = canvas[i].getContext("webgl", { preserveDrawingBuffer: true });
|
||||
gl[i].clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
initProgram(i);
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
window.onload = function() {
|
||||
canvas = document.querySelector("canvas");
|
||||
gl = canvas.getContext("webgl");
|
||||
gl = canvas.getContext("webgl", { preserveDrawingBuffer: true });
|
||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
initProgram(0);
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
window.onload = function() {
|
||||
canvas = document.querySelector("canvas");
|
||||
gl = canvas.getContext("webgl");
|
||||
gl = canvas.getContext("webgl", { preserveDrawingBuffer: true });
|
||||
|
||||
let shaderProgram = gl.createProgram();
|
||||
let vertexShader, fragmentShader;
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
window.onload = function() {
|
||||
canvas = document.querySelector("canvas");
|
||||
gl = canvas.getContext("webgl");
|
||||
gl = canvas.getContext("webgl", { preserveDrawingBuffer: true });
|
||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
initProgram();
|
||||
|
|
|
@ -12,7 +12,7 @@ let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|
|||
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
|
||||
let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
let { Promise: promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
|
||||
let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||
|
@ -124,14 +124,14 @@ function once(aTarget, aEventName, aUseCapture = false) {
|
|||
let deferred = promise.defer();
|
||||
|
||||
for (let [add, remove] of [
|
||||
["on", "off"], // Use event emitter before DOM events for consistency
|
||||
["addEventListener", "removeEventListener"],
|
||||
["addListener", "removeListener"],
|
||||
["on", "off"]
|
||||
["addListener", "removeListener"]
|
||||
]) {
|
||||
if ((add in aTarget) && (remove in aTarget)) {
|
||||
aTarget[add](aEventName, function onEvent(...aArgs) {
|
||||
aTarget[remove](aEventName, onEvent, aUseCapture);
|
||||
deferred.resolve.apply(deferred, aArgs);
|
||||
deferred.resolve(...aArgs);
|
||||
}, aUseCapture);
|
||||
break;
|
||||
}
|
||||
|
@ -140,6 +140,16 @@ function once(aTarget, aEventName, aUseCapture = false) {
|
|||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Hack around `once`, as that only resolves to a single (first) argument
|
||||
// and discards the rest. `onceSpread` is similar, except resolves to an
|
||||
// array of all of the arguments in the handler. These should be consolidated
|
||||
// into the same function, but many tests will need to be changed.
|
||||
function onceSpread(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, (...args) => deferred.resolve(args));
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function observe(aNotificationName, aOwnsWeak = false) {
|
||||
info("Waiting for observer notification: '" + aNotificationName + ".");
|
||||
|
||||
|
@ -273,3 +283,27 @@ function teardown(aPanel) {
|
|||
removeTab(aPanel.target.tab)
|
||||
]);
|
||||
}
|
||||
|
||||
// Due to `program-linked` events firing synchronously, we cannot
|
||||
// just yield/chain them together, as then we miss all actors after the
|
||||
// first event since they're fired consecutively. This allows us to capture
|
||||
// all actors and returns an array containing them.
|
||||
//
|
||||
// Takes a `front` object that is an event emitter, the number of
|
||||
// programs that should be listened to and waited on, and an optional
|
||||
// `onAdd` function that calls with the entire actors array on program link
|
||||
function getPrograms(front, count, onAdd) {
|
||||
let actors = [];
|
||||
let deferred = promise.defer();
|
||||
front.on("program-linked", function onLink (actor) {
|
||||
if (actors.length !== count) {
|
||||
actors.push(actor);
|
||||
if (typeof onAdd === 'function') onAdd(actors)
|
||||
}
|
||||
if (actors.length === count) {
|
||||
front.off("program-linked", onLink);
|
||||
deferred.resolve(actors);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ this.VariablesView = function VariablesView(aParentNode, aFlags = {}) {
|
|||
|
||||
this._parent = aParentNode;
|
||||
this._parent.classList.add("variables-view-container");
|
||||
this._parent.classList.add("theme-body");
|
||||
this._appendEmptyNotice();
|
||||
|
||||
this._onSearchboxInput = this._onSearchboxInput.bind(this);
|
||||
|
|
|
@ -654,7 +654,7 @@ this.WidgetMethods = {
|
|||
* - relaxed: true if this container should allow dupes & degenerates
|
||||
* - attachment: some attached primitive/object for the item
|
||||
* - attributes: a batch of attributes set to the displayed element
|
||||
* - finalize: function invokde when the item is removed
|
||||
* - finalize: function invoked when the item is removed
|
||||
* @return Item
|
||||
* The item associated with the displayed element if an unstaged push,
|
||||
* undefined if the item was staged for a later commit.
|
||||
|
@ -733,6 +733,7 @@ this.WidgetMethods = {
|
|||
}
|
||||
this._widget.removeChild(aItem._target);
|
||||
this._untangleItem(aItem);
|
||||
if (!this.itemCount) this.empty();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1009,7 +1010,9 @@ this.WidgetMethods = {
|
|||
// a redundant selection event, so return early.
|
||||
if (targetElement != prevElement) {
|
||||
this._widget.selectedItem = targetElement;
|
||||
ViewHelpers.dispatchEvent(targetElement || prevElement, "select", aItem);
|
||||
let dispTarget = targetElement || prevElement;
|
||||
let dispName = this.suppressSelectionEvents ? "suppressed-select" : "select";
|
||||
ViewHelpers.dispatchEvent(dispTarget, dispName, aItem);
|
||||
}
|
||||
|
||||
// Updates this container to reflect the information provided by the
|
||||
|
@ -1044,6 +1047,15 @@ this.WidgetMethods = {
|
|||
set selectedValue(aValue)
|
||||
this.selectedItem = this._itemsByValue.get(aValue),
|
||||
|
||||
/**
|
||||
* Specifies if "select" events dispatched from the elements in this container
|
||||
* when their respective items are selected should be suppressed or not.
|
||||
*
|
||||
* If this flag is set to true, then consumers of this container won't
|
||||
* be normally notified when items are selected.
|
||||
*/
|
||||
suppressSelectionEvents: false,
|
||||
|
||||
/**
|
||||
* Focus this container the first time an element is inserted?
|
||||
*
|
||||
|
@ -1305,6 +1317,14 @@ this.WidgetMethods = {
|
|||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Shortcut function for getItemForPredicate which works on item attachments.
|
||||
* @see getItemForPredicate
|
||||
*/
|
||||
getItemForAttachment: function(aPredicate, aOwner = this) {
|
||||
return this.getItemForPredicate(e => aPredicate(e.attachment));
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the index of an item in the container.
|
||||
*
|
||||
|
|
|
@ -236,7 +236,13 @@ Editor.prototype = {
|
|||
|
||||
cm.on("focus", () => this.emit("focus"));
|
||||
cm.on("scroll", () => this.emit("scroll"));
|
||||
cm.on("change", () => this.emit("change"));
|
||||
cm.on("change", () => {
|
||||
this.emit("change");
|
||||
if (!this._lastDirty) {
|
||||
this._lastDirty = true;
|
||||
this.emit("dirty-change");
|
||||
}
|
||||
});
|
||||
cm.on("cursorActivity", (cm) => this.emit("cursorActivity"));
|
||||
|
||||
cm.on("gutterClick", (cm, line, gutter, ev) => {
|
||||
|
@ -614,6 +620,8 @@ Editor.prototype = {
|
|||
setClean: function () {
|
||||
let cm = editors.get(this);
|
||||
this.version = cm.changeGeneration();
|
||||
this._lastDirty = false;
|
||||
this.emit("dirty-change");
|
||||
return this.version;
|
||||
},
|
||||
|
||||
|
|
|
@ -234,7 +234,7 @@ StyleSheetEditor.prototype = {
|
|||
this.emit("source-editor-load");
|
||||
});
|
||||
|
||||
sourceEditor.on("change", this._onPropertyChange);
|
||||
sourceEditor.on("dirty-change", this._onPropertyChange);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,7 +14,6 @@ Components.utils.import("resource://gre/modules/NetUtil.jsm", tempScope);
|
|||
let FileUtils = tempScope.FileUtils;
|
||||
let NetUtil = tempScope.NetUtil;
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
@ -41,9 +40,22 @@ function test()
|
|||
|
||||
function runTests(editor)
|
||||
{
|
||||
editor.sourceEditor.once("dirty-change", () => {
|
||||
is(editor.sourceEditor.isClean(), false, "Editor is dirty.");
|
||||
ok(editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is present in the corresponding summary.");
|
||||
});
|
||||
let beginCursor = {line: 0, ch: 0};
|
||||
editor.sourceEditor.replaceText("DIRTY TEXT", beginCursor, beginCursor);
|
||||
|
||||
editor.sourceEditor.once("dirty-change", () => {
|
||||
is(editor.sourceEditor.isClean(), true, "Editor is clean.");
|
||||
ok(!editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is not present in the corresponding summary.");
|
||||
finish();
|
||||
});
|
||||
editor.saveToFile(null, function (file) {
|
||||
ok(file, "file should get saved directly when using a file:// URI");
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3322,10 +3322,12 @@ JSTerm.prototype = {
|
|||
|
||||
iframe.addEventListener("load", function onIframeLoad(aEvent) {
|
||||
iframe.removeEventListener("load", onIframeLoad, true);
|
||||
iframe.style.visibility = "visible";
|
||||
deferred.resolve(iframe.contentWindow);
|
||||
}, true);
|
||||
|
||||
iframe.flex = 1;
|
||||
iframe.style.visibility = "hidden";
|
||||
iframe.setAttribute("src", VARIABLES_VIEW_URL);
|
||||
aOptions.targetElement.appendChild(iframe);
|
||||
}
|
||||
|
|
|
@ -129,8 +129,10 @@
|
|||
<!ENTITY debuggerUI.seMenuCondBreak "Add conditional breakpoint">
|
||||
<!ENTITY debuggerUI.seMenuCondBreak.key "B">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.instruments.*): This is the text that
|
||||
- appears in the debugger's instruments pane tabs. -->
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.tabs.*): This is the text that
|
||||
- appears in the debugger's side pane tabs. -->
|
||||
<!ENTITY debuggerUI.tabs.sources "Sources">
|
||||
<!ENTITY debuggerUI.tabs.callstack "Call Stack">
|
||||
<!ENTITY debuggerUI.tabs.variables "Variables">
|
||||
<!ENTITY debuggerUI.tabs.events "Events">
|
||||
|
||||
|
|
|
@ -69,10 +69,14 @@ noGlobalsText=No globals
|
|||
# when there are no scripts.
|
||||
noSourcesText=This page has no sources.
|
||||
|
||||
# LOCALIZATION NOTE (noEventsTExt): The text to display in the events tab
|
||||
# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab
|
||||
# when there are no events.
|
||||
noEventListenersText=No event listeners to display
|
||||
|
||||
# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab
|
||||
# when there are no stack frames.
|
||||
noStackFramesText=No stack frames to display
|
||||
|
||||
# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when
|
||||
# the user hovers over the checkbox used to toggle an event breakpoint.
|
||||
eventCheckboxTooltip=Toggle breaking on this event
|
||||
|
|
|
@ -247,7 +247,7 @@ Desktop browser's sync prefs.
|
|||
completeselectedindex="true"
|
||||
placeholder="&urlbar.emptytext;"
|
||||
tabscrolling="true"
|
||||
onclick="SelectionHelperUI.urlbarClick();"/>
|
||||
onclick="SelectionHelperUI.urlbarTextboxClick(this);"/>
|
||||
|
||||
<toolbarbutton id="go-button" class="urlbar-button"
|
||||
command="cmd_go"/>
|
||||
|
|
|
@ -34,10 +34,9 @@ var ChromeSelectionHandler = {
|
|||
this._domWinUtils = Util.getWindowUtils(window);
|
||||
this._contentWindow = window;
|
||||
this._targetElement = this._domWinUtils.elementFromPoint(aJson.xPos, aJson.yPos, true, false);
|
||||
|
||||
this._targetIsEditable = this._targetElement instanceof Components.interfaces.nsIDOMXULTextBoxElement;
|
||||
if (!this._targetIsEditable) {
|
||||
this._onFail("not an editable?");
|
||||
this._onFail("not an editable?", this._targetElement);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -344,9 +344,9 @@ var SelectionHelperUI = {
|
|||
observe: function (aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "attach_edit_session_to_content":
|
||||
let event = aSubject;
|
||||
this.attachEditSession(Browser.selectedTab.browser,
|
||||
event.clientX, event.clientY);
|
||||
// We receive this from text input bindings when this module
|
||||
// isn't accessible.
|
||||
this.chromeTextboxClick(aSubject);
|
||||
break;
|
||||
|
||||
case "apzc-transform-begin":
|
||||
|
@ -514,6 +514,40 @@ var SelectionHelperUI = {
|
|||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Event handler on the navbar text input. Called from navbar bindings
|
||||
* when focus is applied to the edit.
|
||||
*/
|
||||
urlbarTextboxClick: function(aEdit) {
|
||||
// workaround for bug 925457: taping browser chrome resets last tap
|
||||
// co-ordinates to 'undefined' so that we know not to shift the browser
|
||||
// when the keyboard is up in SelectionHandler's _calcNewContentPosition().
|
||||
Browser.selectedTab.browser.messageManager.sendAsyncMessage("Browser:ResetLastPos", {
|
||||
xPos: null,
|
||||
yPos: null
|
||||
});
|
||||
|
||||
if (InputSourceHelper.isPrecise || !aEdit.textLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable selection when there's text in the control
|
||||
let innerRect = aEdit.inputField.getBoundingClientRect();
|
||||
this.attachEditSession(ChromeSelectionHandler,
|
||||
innerRect.left,
|
||||
innerRect.top);
|
||||
},
|
||||
|
||||
/*
|
||||
* Click handler for chrome pages loaded into the browser (about:config).
|
||||
* Called from the text input bindings via the attach_edit_session_to_content
|
||||
* observer.
|
||||
*/
|
||||
chromeTextboxClick: function (aEvent) {
|
||||
this.attachEditSession(Browser.selectedTab.browser,
|
||||
aEvent.clientX, aEvent.clientY);
|
||||
},
|
||||
|
||||
/*
|
||||
* Handy debug routines that work independent of selection. They
|
||||
* make use of the selection overlay for drawing points.
|
||||
|
@ -835,20 +869,10 @@ var SelectionHelperUI = {
|
|||
* Event handlers for document events
|
||||
*/
|
||||
|
||||
urlbarClick: function() {
|
||||
// Workaround for bug 925457: taping browser chrome resets last tap
|
||||
// co-ordinates to 'undefined' so that we know not to shift the browser
|
||||
// when the keyboard is up (in SelectionHandler._calcNewContentPosition())
|
||||
Browser.selectedTab.browser.messageManager.sendAsyncMessage("Browser:ResetLastPos", {
|
||||
xPos: null,
|
||||
yPos: null
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Handles taps that move the current caret around in text edits,
|
||||
* clear active selection and focus when neccessary, or change
|
||||
* modes.
|
||||
* modes. Only active afer SelectionHandlerUI is initialized.
|
||||
*/
|
||||
_onClick: function(aEvent) {
|
||||
if (this.layerMode == kChromeLayer && this._targetIsEditable) {
|
||||
|
|
|
@ -44,7 +44,6 @@ pref("layers.componentalpha.enabled", false);
|
|||
pref("apz.touch_start_tolerance", "0.1"); // dpi * tolerance = pixel threshold
|
||||
pref("apz.pan_repaint_interval", 50); // prefer 20 fps
|
||||
pref("apz.fling_repaint_interval", 50); // prefer 20 fps
|
||||
pref("apz.fling_friction", "0.002");
|
||||
pref("apz.fling_stopped_threshold", "0.2");
|
||||
|
||||
// 0 = free, 1 = standard, 2 = sticky
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
min-width: 50px;
|
||||
}
|
||||
|
||||
#sources-pane > tabs {
|
||||
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
|
||||
}
|
||||
|
||||
#sources-and-editor-splitter {
|
||||
-moz-border-start-color: transparent;
|
||||
}
|
||||
|
@ -20,7 +24,7 @@
|
|||
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
|
||||
}
|
||||
|
||||
#sources-toolbar .devtools-toolbarbutton {
|
||||
#sources-toolbar > #sources-controls > .devtools-toolbarbutton {
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
|
@ -87,15 +91,23 @@
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
.list-widget-item:not(.selected):not(.empty):hover {
|
||||
.theme-light .list-widget-item:not(.selected):not(.empty):hover {
|
||||
background: linear-gradient(rgba(255,255,255,0.9), rgba(255,255,255,0.85)), Highlight;
|
||||
}
|
||||
|
||||
.list-widget-item.selected.light {
|
||||
.theme-light .list-widget-item.selected.light {
|
||||
background: linear-gradient(rgba(255,255,255,0.85), rgba(255,255,255,0.8)), Highlight;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.theme-dark .list-widget-item:not(.selected):not(.empty):hover {
|
||||
background: linear-gradient(rgba(255,255,255,0.1), rgba(255,255,255,0.05));
|
||||
}
|
||||
|
||||
.theme-dark .list-widget-item.selected.light {
|
||||
background: linear-gradient(rgba(255,255,255,0.05), rgba(255,255,255,0.025));
|
||||
}
|
||||
|
||||
.list-widget-item.selected {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
|
@ -106,21 +118,52 @@
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
/* Stack frames view */
|
||||
/* Breadcrumbs stack frames view */
|
||||
|
||||
.breadcrumbs-widget-item {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.dbg-stackframe-details {
|
||||
-moz-padding-start: 4px;
|
||||
}
|
||||
|
||||
.dbg-stackframe-menuitem[checked] {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
outline: 1px solid #eee;
|
||||
font-weight: 600;
|
||||
/* Classic stack frames view */
|
||||
|
||||
.dbg-classic-stackframe {
|
||||
display: block;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.dbg-stackframe-menuitem-details {
|
||||
-moz-padding-start: 16px;
|
||||
.dbg-classic-stackframe-title {
|
||||
font-weight: 600;
|
||||
color: #046;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details:-moz-locale-dir(ltr) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details:-moz-locale-dir(rtl) {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details-url {
|
||||
max-width: 90%;
|
||||
text-align: end;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details-sep {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details-line {
|
||||
color: #58b;
|
||||
}
|
||||
|
||||
#callstack-list .side-menu-widget-item.selected label {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Sources and breakpoints view */
|
||||
|
@ -180,8 +223,9 @@
|
|||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#instruments-pane > tabpanels > tabpanel {
|
||||
background: #fff;
|
||||
#instruments-pane .side-menu-widget-container,
|
||||
#instruments-pane .side-menu-widget-empty-notice-container {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Watch expressions view */
|
||||
|
@ -203,6 +247,7 @@
|
|||
|
||||
.dbg-expression-input {
|
||||
-moz-padding-start: 2px !important;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Event listeners view */
|
||||
|
@ -227,6 +272,10 @@
|
|||
color: #666;
|
||||
}
|
||||
|
||||
#event-listeners .side-menu-widget-item.selected {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* Searchbox and the search operations help panel */
|
||||
|
||||
#searchbox {
|
||||
|
@ -380,6 +429,11 @@
|
|||
|
||||
/* Toolbar controls */
|
||||
|
||||
.devtools-sidebar-tabs > tabs > tab {
|
||||
min-height: 25px !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#resumption-panel-desc {
|
||||
width: 200px;
|
||||
}
|
||||
|
@ -468,12 +522,17 @@
|
|||
max-height: 80vh;
|
||||
}
|
||||
|
||||
#body[layout=vertical] #sources-pane > tabs {
|
||||
-moz-border-end: none;
|
||||
}
|
||||
|
||||
#body[layout=vertical] #instruments-pane {
|
||||
margin: 0 !important;
|
||||
/* To prevent all the margin hacks to hide the sidebar. */
|
||||
}
|
||||
|
||||
#body[layout=vertical] .side-menu-widget-container {
|
||||
#body[layout=vertical] .side-menu-widget-container,
|
||||
#body[layout=vertical] .side-menu-widget-empty-notice-container {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -312,6 +312,10 @@ box.requests-menu-status[code^="5"] {
|
|||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.side-menu-widget-item[odd] {
|
||||
background: rgba(255,255,255,0.05);
|
||||
}
|
||||
|
@ -363,10 +367,6 @@ box.requests-menu-status[code^="5"] {
|
|||
-moz-padding-start: 3px;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-string {
|
||||
color: #10c !important;
|
||||
}
|
||||
|
||||
/* Headers tabpanel */
|
||||
|
||||
#headers-summary-status,
|
||||
|
|
|
@ -281,15 +281,17 @@
|
|||
|
||||
/* SideMenuWidget container */
|
||||
|
||||
.side-menu-widget-container[with-arrows=true]:-moz-locale-dir(ltr) {
|
||||
.side-menu-widget-container:-moz-locale-dir(ltr),
|
||||
.side-menu-widget-empty-notice-container:-moz-locale-dir(ltr) {
|
||||
box-shadow: inset -1px 0 0 #222426;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true]:-moz-locale-dir(rtl) {
|
||||
.side-menu-widget-container:-moz-locale-dir(rtl),
|
||||
.side-menu-widget-empty-notice-container:-moz-locale-dir(rtl) {
|
||||
box-shadow: inset 1px 0 0 #222426;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true] .side-menu-widget-group {
|
||||
.side-menu-widget-group {
|
||||
/* To allow visibility of the dark margin shadow. */
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
@ -328,7 +330,9 @@
|
|||
|
||||
.side-menu-widget-item[theme="light"] {
|
||||
border-top: 1px solid hsla(210,8%,75%,.25);
|
||||
border-bottom: 1px solid hsla(210,16%,76%,.1);
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.side-menu-widget-item[theme="dark"]:last-of-type {
|
||||
|
@ -339,15 +343,11 @@
|
|||
box-shadow: inset 0 -1px 0 hsla(210,8%,75%,.25);
|
||||
}
|
||||
|
||||
.side-menu-widget-item[theme="dark"].selected {
|
||||
.side-menu-widget-item.selected {
|
||||
background: linear-gradient(hsl(206,61%,40%), hsl(206,61%,31%)) repeat-x top left !important;
|
||||
box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15);
|
||||
}
|
||||
|
||||
.side-menu-widget-item[theme="light"].selected {
|
||||
/* Nothing here yet */
|
||||
}
|
||||
|
||||
.side-menu-widget-item.selected > .side-menu-widget-item-arrow {
|
||||
background-size: auto, 1px 100%;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -432,10 +432,6 @@
|
|||
|
||||
/* VariablesView */
|
||||
|
||||
.variables-view-container {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.variables-view-empty-notice {
|
||||
color: GrayText;
|
||||
padding: 2px;
|
||||
|
@ -445,11 +441,6 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.variables-view-scope:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
.variables-view-scope > .variables-view-element-details:not(:empty) {
|
||||
-moz-margin-start: 2px;
|
||||
-moz-margin-end: 1px;
|
||||
|
@ -458,13 +449,12 @@
|
|||
/* Generic traits applied to both variables and properties */
|
||||
|
||||
.variable-or-property {
|
||||
transition: background 1s ease-in-out;
|
||||
color: #000;
|
||||
transition: background 1s ease-in-out, color 1s ease-in-out;
|
||||
}
|
||||
|
||||
.variable-or-property[changed] {
|
||||
background: rgba(255,255,0,0.65);
|
||||
transition-duration: 0.4s;
|
||||
color: black;
|
||||
transition-duration: .4s;
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
|
@ -473,12 +463,6 @@
|
|||
-moz-padding-end: 4px;
|
||||
}
|
||||
|
||||
.variable-or-property:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.variable-or-property[editable] > .title > .value {
|
||||
cursor: text;
|
||||
}
|
||||
|
@ -495,52 +479,22 @@
|
|||
}
|
||||
|
||||
.variables-view-variable:not(:last-child) {
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid rgba(128, 128, 128, .15);
|
||||
}
|
||||
|
||||
.variables-view-variable > .title > .name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.variables-view-variable:not(:focus) > .title > .name {
|
||||
color: #048;
|
||||
}
|
||||
|
||||
.variables-view-property:not(:focus) > .title > .name {
|
||||
color: #881090;
|
||||
}
|
||||
|
||||
/* Token value colors */
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-undefined {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-null {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-boolean {
|
||||
color: #10c;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-number {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-string {
|
||||
color: #282;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-other {
|
||||
color: #333;
|
||||
.variable-or-property:focus > .title > label {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* Custom configurable/enumerable/writable or frozen/sealed/extensible
|
||||
* variables and properties */
|
||||
|
||||
.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]):not([scope]) > .title > .name {
|
||||
opacity: 0.5;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.variable-or-property[non-configurable] > .title > .name {
|
||||
|
@ -551,6 +505,10 @@
|
|||
border-bottom: 1px dashed #f99;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > .title > .name {
|
||||
border-bottom: 1px dashed #8b0;
|
||||
}
|
||||
|
||||
.variable-or-property-non-writable-icon {
|
||||
background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
|
||||
width: 16px;
|
||||
|
@ -577,27 +535,6 @@
|
|||
color: #666;
|
||||
}
|
||||
|
||||
/* Special variables and properties */
|
||||
|
||||
.variable-or-property[safe-getter] > .title > .name {
|
||||
border-bottom: 1px dashed #8b0;
|
||||
}
|
||||
|
||||
.variable-or-property[exception]:not(:focus) > .title > .name {
|
||||
color: #a00;
|
||||
text-shadow: 0 0 8px #fcc;
|
||||
}
|
||||
|
||||
.variable-or-property[return]:not(:focus) > .title > .name {
|
||||
color: #0a0;
|
||||
text-shadow: 0 0 8px #cfc;
|
||||
}
|
||||
|
||||
.variable-or-property[scope]:not(:focus) > .title > .name {
|
||||
color: #00a;
|
||||
text-shadow: 0 0 8px #ccf;
|
||||
}
|
||||
|
||||
/* Aligned values */
|
||||
|
||||
.variables-view-container[aligned-values] .title > .separator {
|
||||
|
@ -634,7 +571,7 @@
|
|||
.variable-or-property[non-configurable] > tooltip > label[value=configurable],
|
||||
.variable-or-property[non-writable] > tooltip > label[value=writable],
|
||||
.variable-or-property[non-extensible] > tooltip > label[value=extensible] {
|
||||
color: #f44;
|
||||
color: #800;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
|
@ -680,14 +617,13 @@
|
|||
.element-name-input {
|
||||
-moz-margin-start: -2px !important;
|
||||
-moz-margin-end: 2px !important;
|
||||
color: #048;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.element-value-input,
|
||||
.element-name-input {
|
||||
border: 1px solid #999 !important;
|
||||
box-shadow: 1px 2px 4px #aaa;
|
||||
border: 1px solid rgba(128, 128, 128, .5) !important;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Variables and properties searching */
|
||||
|
|
|
@ -3594,11 +3594,16 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
|||
#navigator-toolbox[inFullscreen]:not(:-moz-lwtheme)::before {
|
||||
height: calc(@tabHeight@ + 11px) !important;
|
||||
}
|
||||
#main-window[inFullscreen][privatebrowsingmode=temporary],
|
||||
#main-window[inFullscreen]:-moz-lwtheme {
|
||||
/* This additional padding matches the change in height in the pseudo-element
|
||||
* above. The rules combined force the top 22px of the background image to
|
||||
* be hidden, so there image doesn't jump around with the loss of the titlebar */
|
||||
* above. */
|
||||
padding-top: 11px;
|
||||
}
|
||||
#main-window[inFullscreen]:not([privatebrowsingmode=temporary]):-moz-lwtheme {
|
||||
/* In combination with the previous rule, forces the top 22px of the
|
||||
* background image to be hidden, so the image doesn't jump around with
|
||||
* the loss of the titlebar. */
|
||||
background-position: right -11px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
min-width: 50px;
|
||||
}
|
||||
|
||||
#sources-pane > tabs {
|
||||
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
|
||||
}
|
||||
|
||||
#sources-and-editor-splitter {
|
||||
-moz-border-start-color: transparent;
|
||||
}
|
||||
|
@ -22,7 +26,7 @@
|
|||
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
|
||||
}
|
||||
|
||||
#sources-toolbar .devtools-toolbarbutton {
|
||||
#sources-toolbar > #sources-controls > .devtools-toolbarbutton {
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
|
@ -89,15 +93,23 @@
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
.list-widget-item:not(.selected):not(.empty):hover {
|
||||
.theme-light .list-widget-item:not(.selected):not(.empty):hover {
|
||||
background: linear-gradient(rgba(255,255,255,0.9), rgba(255,255,255,0.85)), Highlight;
|
||||
}
|
||||
|
||||
.list-widget-item.selected.light {
|
||||
.theme-light .list-widget-item.selected.light {
|
||||
background: linear-gradient(rgba(255,255,255,0.85), rgba(255,255,255,0.8)), Highlight;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.theme-dark .list-widget-item:not(.selected):not(.empty):hover {
|
||||
background: linear-gradient(rgba(255,255,255,0.1), rgba(255,255,255,0.05));
|
||||
}
|
||||
|
||||
.theme-dark .list-widget-item.selected.light {
|
||||
background: linear-gradient(rgba(255,255,255,0.05), rgba(255,255,255,0.025));
|
||||
}
|
||||
|
||||
.list-widget-item.selected {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
|
@ -108,21 +120,52 @@
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
/* Stack frames view */
|
||||
/* Breadcrumbs stack frames view */
|
||||
|
||||
.breadcrumbs-widget-item {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.dbg-stackframe-details {
|
||||
-moz-padding-start: 4px;
|
||||
}
|
||||
|
||||
.dbg-stackframe-menuitem[checked] {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
outline: 1px solid #eee;
|
||||
font-weight: 600;
|
||||
/* Classic stack frames view */
|
||||
|
||||
.dbg-classic-stackframe {
|
||||
display: block;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.dbg-stackframe-menuitem-details {
|
||||
-moz-padding-start: 16px;
|
||||
.dbg-classic-stackframe-title {
|
||||
font-weight: 600;
|
||||
color: #046;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details:-moz-locale-dir(ltr) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details:-moz-locale-dir(rtl) {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details-url {
|
||||
max-width: 90%;
|
||||
text-align: end;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details-sep {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details-line {
|
||||
color: #58b;
|
||||
}
|
||||
|
||||
#callstack-list .side-menu-widget-item.selected label {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Sources and breakpoints view */
|
||||
|
@ -182,8 +225,9 @@
|
|||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#instruments-pane > tabpanels > tabpanel {
|
||||
background: #fff;
|
||||
#instruments-pane .side-menu-widget-container,
|
||||
#instruments-pane .side-menu-widget-empty-notice-container {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Watch expressions view */
|
||||
|
@ -205,6 +249,7 @@
|
|||
|
||||
.dbg-expression-input {
|
||||
-moz-padding-start: 2px !important;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Event listeners view */
|
||||
|
@ -229,6 +274,10 @@
|
|||
color: #666;
|
||||
}
|
||||
|
||||
#event-listeners .side-menu-widget-item.selected {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* Searchbox and the search operations help panel */
|
||||
|
||||
#searchbox {
|
||||
|
@ -382,6 +431,11 @@
|
|||
|
||||
/* Toolbar controls */
|
||||
|
||||
.devtools-sidebar-tabs > tabs > tab {
|
||||
min-height: 1em !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#resumption-panel-desc {
|
||||
width: 200px;
|
||||
}
|
||||
|
@ -470,12 +524,17 @@
|
|||
max-height: 80vh;
|
||||
}
|
||||
|
||||
#body[layout=vertical] #sources-pane > tabs {
|
||||
-moz-border-end: none;
|
||||
}
|
||||
|
||||
#body[layout=vertical] #instruments-pane {
|
||||
margin: 0 !important;
|
||||
/* To prevent all the margin hacks to hide the sidebar. */
|
||||
}
|
||||
|
||||
#body[layout=vertical] .side-menu-widget-container {
|
||||
#body[layout=vertical] .side-menu-widget-container,
|
||||
#body[layout=vertical] .side-menu-widget-empty-notice-container {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -312,6 +312,10 @@ box.requests-menu-status[code^="5"] {
|
|||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.side-menu-widget-item[odd] {
|
||||
background: rgba(255,255,255,0.05);
|
||||
}
|
||||
|
@ -363,10 +367,6 @@ box.requests-menu-status[code^="5"] {
|
|||
-moz-padding-start: 3px;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-string {
|
||||
color: #10c !important;
|
||||
}
|
||||
|
||||
/* Headers tabpanel */
|
||||
|
||||
#headers-summary-status,
|
||||
|
|
|
@ -281,15 +281,17 @@
|
|||
|
||||
/* SideMenuWidget container */
|
||||
|
||||
.side-menu-widget-container[with-arrows=true]:-moz-locale-dir(ltr) {
|
||||
.side-menu-widget-container:-moz-locale-dir(ltr),
|
||||
.side-menu-widget-empty-notice-container:-moz-locale-dir(ltr) {
|
||||
box-shadow: inset -1px 0 0 #222426;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true]:-moz-locale-dir(rtl) {
|
||||
.side-menu-widget-container:-moz-locale-dir(rtl),
|
||||
.side-menu-widget-empty-notice-container:-moz-locale-dir(rtl) {
|
||||
box-shadow: inset 1px 0 0 #222426;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true] .side-menu-widget-group {
|
||||
.side-menu-widget-group {
|
||||
/* To allow visibility of the dark margin shadow. */
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
@ -328,7 +330,9 @@
|
|||
|
||||
.side-menu-widget-item[theme="light"] {
|
||||
border-top: 1px solid hsla(210,8%,75%,.25);
|
||||
border-bottom: 1px solid hsla(210,16%,76%,.1);
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.side-menu-widget-item[theme="dark"]:last-of-type {
|
||||
|
@ -339,15 +343,11 @@
|
|||
box-shadow: inset 0 -1px 0 hsla(210,8%,75%,.25);
|
||||
}
|
||||
|
||||
.side-menu-widget-item[theme="dark"].selected {
|
||||
.side-menu-widget-item.selected {
|
||||
background: linear-gradient(hsl(206,61%,40%), hsl(206,61%,31%)) repeat-x top left !important;
|
||||
box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15);
|
||||
}
|
||||
|
||||
.side-menu-widget-item[theme="light"].selected {
|
||||
/* Nothing here yet */
|
||||
}
|
||||
|
||||
.side-menu-widget-item.selected > .side-menu-widget-item-arrow {
|
||||
background-size: auto, 1px 100%;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -426,10 +426,6 @@
|
|||
|
||||
/* VariablesView */
|
||||
|
||||
.variables-view-container {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.variables-view-empty-notice {
|
||||
color: GrayText;
|
||||
padding: 2px;
|
||||
|
@ -439,11 +435,6 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.variables-view-scope:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
.variables-view-scope > .variables-view-element-details:not(:empty) {
|
||||
-moz-margin-start: 2px;
|
||||
-moz-margin-end: 1px;
|
||||
|
@ -452,13 +443,12 @@
|
|||
/* Generic traits applied to both variables and properties */
|
||||
|
||||
.variable-or-property {
|
||||
transition: background 1s ease-in-out;
|
||||
color: #000;
|
||||
transition: background 1s ease-in-out, color 1s ease-in-out;
|
||||
}
|
||||
|
||||
.variable-or-property[changed] {
|
||||
background: rgba(255,255,0,0.65);
|
||||
transition-duration: 0.4s;
|
||||
color: black;
|
||||
transition-duration: .4s;
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
|
@ -467,12 +457,6 @@
|
|||
-moz-padding-end: 4px;
|
||||
}
|
||||
|
||||
.variable-or-property:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.variable-or-property[editable] > .title > .value {
|
||||
cursor: text;
|
||||
}
|
||||
|
@ -489,52 +473,22 @@
|
|||
}
|
||||
|
||||
.variables-view-variable:not(:last-child) {
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid rgba(128, 128, 128, .15);
|
||||
}
|
||||
|
||||
.variables-view-variable > .title > .name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.variables-view-variable:not(:focus) > .title > .name {
|
||||
color: #048;
|
||||
}
|
||||
|
||||
.variables-view-property:not(:focus) > .title > .name {
|
||||
color: #881090;
|
||||
}
|
||||
|
||||
/* Token value colors */
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-undefined {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-null {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-boolean {
|
||||
color: #10c;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-number {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-string {
|
||||
color: #282;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-other {
|
||||
color: #333;
|
||||
.variable-or-property:focus > .title > label {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* Custom configurable/enumerable/writable or frozen/sealed/extensible
|
||||
* variables and properties */
|
||||
|
||||
.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]):not([scope]) > .title > .name {
|
||||
opacity: 0.5;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.variable-or-property[non-configurable] > .title > .name {
|
||||
|
@ -545,6 +499,10 @@
|
|||
border-bottom: 1px dashed #f99;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > .title > .name {
|
||||
border-bottom: 1px dashed #8b0;
|
||||
}
|
||||
|
||||
.variable-or-property-non-writable-icon {
|
||||
background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
|
||||
width: 16px;
|
||||
|
@ -571,27 +529,6 @@
|
|||
color: #666;
|
||||
}
|
||||
|
||||
/* Special variables and properties */
|
||||
|
||||
.variable-or-property[safe-getter] > .title > .name {
|
||||
border-bottom: 1px dashed #8b0;
|
||||
}
|
||||
|
||||
.variable-or-property[exception]:not(:focus) > .title > .name {
|
||||
color: #a00;
|
||||
text-shadow: 0 0 8px #fcc;
|
||||
}
|
||||
|
||||
.variable-or-property[return]:not(:focus) > .title > .name {
|
||||
color: #0a0;
|
||||
text-shadow: 0 0 8px #cfc;
|
||||
}
|
||||
|
||||
.variable-or-property[scope]:not(:focus) > .title > .name {
|
||||
color: #00a;
|
||||
text-shadow: 0 0 8px #ccf;
|
||||
}
|
||||
|
||||
/* Aligned values */
|
||||
|
||||
.variables-view-container[aligned-values] .title > .separator {
|
||||
|
@ -674,14 +611,13 @@
|
|||
.element-name-input {
|
||||
-moz-margin-start: -2px !important;
|
||||
-moz-margin-end: 2px !important;
|
||||
color: #048;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.element-value-input,
|
||||
.element-name-input {
|
||||
border: 1px solid #999 !important;
|
||||
box-shadow: 1px 2px 4px #aaa;
|
||||
border: 1px solid rgba(128, 128, 128, .5) !important;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Variables and properties searching */
|
||||
|
|
|
@ -51,7 +51,8 @@
|
|||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.theme-bg-contrast { /* contrast bg color to attract attention on a container */
|
||||
.theme-bg-contrast,
|
||||
.variable-or-property[changed] { /* contrast bg color to attract attention on a container */
|
||||
background: #a18650;
|
||||
}
|
||||
|
||||
|
@ -73,7 +74,9 @@
|
|||
.theme-comment,
|
||||
.cm-s-mozilla .cm-meta,
|
||||
.cm-s-mozilla .cm-hr,
|
||||
.cm-s-mozilla .cm-comment { /* grey */
|
||||
.cm-s-mozilla .cm-comment,
|
||||
.variable-or-property .token-undefined,
|
||||
.variable-or-property .token-null { /* grey */
|
||||
color: #5c6773;
|
||||
}
|
||||
|
||||
|
@ -88,7 +91,9 @@
|
|||
}
|
||||
|
||||
.theme-fg-color1,
|
||||
.cm-s-mozilla .cm-number { /* green */
|
||||
.cm-s-mozilla .cm-number,
|
||||
.variable-or-property .token-number,
|
||||
.variable-or-property[return] > .title > .name { /* green */
|
||||
color: #5c9966;
|
||||
}
|
||||
|
||||
|
@ -97,14 +102,18 @@
|
|||
.cm-s-mozilla .cm-variable,
|
||||
.cm-s-mozilla .cm-def,
|
||||
.cm-s-mozilla .cm-property,
|
||||
.cm-s-mozilla .cm-qualifier { /* blue */
|
||||
.cm-s-mozilla .cm-qualifier,
|
||||
.variables-view-variable > .title > .name,
|
||||
.variable-or-property[scope] > .title > .name { /* blue */
|
||||
color: #3689b2;
|
||||
}
|
||||
|
||||
.theme-fg-color3,
|
||||
.cm-s-mozilla .cm-builtin,
|
||||
.cm-s-mozilla .cm-tag,
|
||||
.cm-s-mozilla .cm-header { /* pink/lavender */
|
||||
.cm-s-mozilla .cm-header,
|
||||
.variables-view-property > .title > .name,
|
||||
.variable-or-property[safe-getter] > .title > .name { /* pink/lavender */
|
||||
color: #a673bf;
|
||||
}
|
||||
|
||||
|
@ -120,14 +129,17 @@
|
|||
|
||||
.theme-fg-color6,
|
||||
.cm-s-mozilla .cm-string,
|
||||
.cm-s-mozilla .cm-string-2 { /* Orange */
|
||||
.cm-s-mozilla .cm-string-2,
|
||||
.variable-or-property .token-string { /* Orange */
|
||||
color: #b26b47;
|
||||
}
|
||||
|
||||
.theme-fg-color7,
|
||||
.cm-s-mozilla .cm-atom,
|
||||
.cm-s-mozilla .cm-quote,
|
||||
.cm-s-mozilla .cm-error { /* Red */
|
||||
.cm-s-mozilla .cm-error,
|
||||
.variable-or-property .token-boolean,
|
||||
.variable-or-property[exception] > .title > .name { /* Red */
|
||||
color: #bf5656;
|
||||
}
|
||||
|
||||
|
@ -148,6 +160,12 @@
|
|||
box-shadow: 0 0 0 1px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.variables-view-scope:focus > .title,
|
||||
.variable-or-property:focus > .title {
|
||||
background: #3689b2; /* fg-color2 */
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* CodeMirror specific styles.
|
||||
* Best effort to match the existing theme, some of the colors
|
||||
* are duplicated here to prevent weirdness in the main theme. */
|
||||
|
|
|
@ -51,7 +51,8 @@
|
|||
background: #EFEFEF;
|
||||
}
|
||||
|
||||
.theme-bg-contrast { /* contrast bg color to attract attention on a container */
|
||||
.theme-bg-contrast,
|
||||
.variable-or-property[changed] { /* contrast bg color to attract attention on a container */
|
||||
background: #a18650;
|
||||
}
|
||||
|
||||
|
@ -72,7 +73,9 @@
|
|||
.theme-comment,
|
||||
.cm-s-mozilla .cm-meta,
|
||||
.cm-s-mozilla .cm-hr,
|
||||
.cm-s-mozilla .cm-comment { /* grey */
|
||||
.cm-s-mozilla .cm-comment,
|
||||
.variable-or-property .token-undefined,
|
||||
.variable-or-property .token-null { /* grey */
|
||||
color: hsl(90,2%,46%);
|
||||
}
|
||||
|
||||
|
@ -87,7 +90,9 @@
|
|||
}
|
||||
|
||||
.theme-fg-color1,
|
||||
.cm-s-mozilla .cm-number { /* green */
|
||||
.cm-s-mozilla .cm-number,
|
||||
.variable-or-property .token-number,
|
||||
.variable-or-property[return] > .title > .name { /* green */
|
||||
color: hsl(72,100%,27%);
|
||||
}
|
||||
|
||||
|
@ -96,14 +101,18 @@
|
|||
.cm-s-mozilla .cm-builtin,
|
||||
.cm-s-mozilla .cm-def,
|
||||
.cm-s-mozilla .cm-property,
|
||||
.cm-s-mozilla .cm-qualifier { /* blue */
|
||||
.cm-s-mozilla .cm-qualifier,
|
||||
.variables-view-variable > .title > .name,
|
||||
.variable-or-property[scope] > .title > .name { /* blue */
|
||||
color: hsl(208,56%,40%);
|
||||
}
|
||||
|
||||
.theme-fg-color3,
|
||||
.cm-s-mozilla .cm-variable,
|
||||
.cm-s-mozilla .cm-tag,
|
||||
.cm-s-mozilla .cm-header { /* dark blue */
|
||||
.cm-s-mozilla .cm-header,
|
||||
.variables-view-property > .title > .name,
|
||||
.variable-or-property[safe-getter] > .title > .name { /* dark blue */
|
||||
color: hsl(208,81%,21%)
|
||||
}
|
||||
|
||||
|
@ -119,14 +128,17 @@
|
|||
|
||||
.theme-fg-color6,
|
||||
.cm-s-mozilla .cm-string,
|
||||
.cm-s-mozilla .cm-string-2 { /* Orange */
|
||||
.cm-s-mozilla .cm-string-2,
|
||||
.variable-or-property .token-string { /* Orange */
|
||||
color: hsl(24,85%,39%);
|
||||
}
|
||||
|
||||
.theme-fg-color7,
|
||||
.cm-s-mozilla .cm-atom,
|
||||
.cm-s-mozilla .cm-quote,
|
||||
.cm-s-mozilla .cm-error { /* Red */
|
||||
.cm-s-mozilla .cm-error,
|
||||
.variable-or-property .token-boolean,
|
||||
.variable-or-property[exception] > .title > .name { /* Red */
|
||||
color: #bf5656;
|
||||
}
|
||||
|
||||
|
@ -147,6 +159,12 @@
|
|||
box-shadow: 0 0 0 1px #EFEFEF;
|
||||
}
|
||||
|
||||
.variables-view-scope:focus > .title,
|
||||
.variable-or-property:focus > .title {
|
||||
background: hsl(208,56%,40%); /* fg-color2 */
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* CodeMirror specific styles.
|
||||
* Best effort to match the existing theme, some of the colors
|
||||
* are duplicated here to prevent weirdness in the main theme. */
|
||||
|
|
|
@ -260,11 +260,15 @@
|
|||
}
|
||||
|
||||
.devtools-sidebar-tabs > tabs > tab {
|
||||
background-size: calc(100% - 2px) 100%, 1px 100%;
|
||||
background-size: calc(100% - 1px) 100%, 1px 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 1px, 0;
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs > tabs > tab:not(:last-of-type) {
|
||||
background-size: calc(100% - 2px) 100%, 1px 100%;
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs:-moz-locale-dir(rtl) > tabs > tab {
|
||||
background-position: calc(100% - 1px), 100%;
|
||||
}
|
||||
|
|
|
@ -291,11 +291,11 @@ a {
|
|||
.inlined-variables-view iframe {
|
||||
display: block;
|
||||
flex: 1;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
-moz-margin-end: 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 12px #dfdfdf;
|
||||
border: 1px solid rgba(128, 128, 128, .5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#webconsole-sidebar > tabs {
|
||||
|
@ -354,6 +354,10 @@ a {
|
|||
background: #131c26; /* mainBackgroundColor */
|
||||
}
|
||||
|
||||
.theme-dark .inlined-variables-view iframe {
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
.theme-light .jsterm-input-container {
|
||||
background-color: #fff; /* mainBackgroundColor */
|
||||
border-color: ThreeDShadow;
|
||||
|
@ -370,3 +374,8 @@ a {
|
|||
.theme-light .navigation-marker .url {
|
||||
background: #fff; /* mainBackgroundColor */
|
||||
}
|
||||
|
||||
.theme-light .inlined-variables-view iframe {
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
min-width: 50px;
|
||||
}
|
||||
|
||||
#sources-pane > tabs {
|
||||
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
|
||||
}
|
||||
|
||||
#sources-and-editor-splitter {
|
||||
-moz-border-start-color: transparent;
|
||||
}
|
||||
|
@ -20,7 +24,7 @@
|
|||
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
|
||||
}
|
||||
|
||||
#sources-toolbar .devtools-toolbarbutton {
|
||||
#sources-toolbar > #sources-controls > .devtools-toolbarbutton {
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
|
@ -87,15 +91,23 @@
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
.list-widget-item:not(.selected):not(.empty):hover {
|
||||
.theme-light .list-widget-item:not(.selected):not(.empty):hover {
|
||||
background: linear-gradient(rgba(255,255,255,0.9), rgba(255,255,255,0.85)), Highlight;
|
||||
}
|
||||
|
||||
.list-widget-item.selected.light {
|
||||
.theme-light .list-widget-item.selected.light {
|
||||
background: linear-gradient(rgba(255,255,255,0.85), rgba(255,255,255,0.8)), Highlight;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.theme-dark .list-widget-item:not(.selected):not(.empty):hover {
|
||||
background: linear-gradient(rgba(255,255,255,0.1), rgba(255,255,255,0.05));
|
||||
}
|
||||
|
||||
.theme-dark .list-widget-item.selected.light {
|
||||
background: linear-gradient(rgba(255,255,255,0.05), rgba(255,255,255,0.025));
|
||||
}
|
||||
|
||||
.list-widget-item.selected {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
|
@ -106,21 +118,52 @@
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
/* Stack frames view */
|
||||
/* Breadcrumbs stack frames view */
|
||||
|
||||
.breadcrumbs-widget-item {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.dbg-stackframe-details {
|
||||
-moz-padding-start: 4px;
|
||||
}
|
||||
|
||||
.dbg-stackframe-menuitem[checked] {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
outline: 1px solid #eee;
|
||||
font-weight: 600;
|
||||
/* Classic stack frames view */
|
||||
|
||||
.dbg-classic-stackframe {
|
||||
display: block;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.dbg-stackframe-menuitem-details {
|
||||
-moz-padding-start: 16px;
|
||||
.dbg-classic-stackframe-title {
|
||||
font-weight: 600;
|
||||
color: #046;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details:-moz-locale-dir(ltr) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details:-moz-locale-dir(rtl) {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details-url {
|
||||
max-width: 90%;
|
||||
text-align: end;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details-sep {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.dbg-classic-stackframe-details-line {
|
||||
color: #58b;
|
||||
}
|
||||
|
||||
#callstack-list .side-menu-widget-item.selected label {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Sources and breakpoints view */
|
||||
|
@ -180,8 +223,9 @@
|
|||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#instruments-pane > tabpanels > tabpanel {
|
||||
background: #fff;
|
||||
#instruments-pane .side-menu-widget-container,
|
||||
#instruments-pane .side-menu-widget-empty-notice-container {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Watch expressions view */
|
||||
|
@ -203,6 +247,7 @@
|
|||
|
||||
.dbg-expression-input {
|
||||
-moz-padding-start: 2px !important;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Event listeners view */
|
||||
|
@ -227,6 +272,10 @@
|
|||
color: #666;
|
||||
}
|
||||
|
||||
#event-listeners .side-menu-widget-item.selected {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* Searchbox and the search operations help panel */
|
||||
|
||||
#searchbox {
|
||||
|
@ -380,6 +429,11 @@
|
|||
|
||||
/* Toolbar controls */
|
||||
|
||||
.devtools-sidebar-tabs > tabs > tab {
|
||||
min-height: 25px !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#resumption-panel-desc {
|
||||
width: 200px;
|
||||
}
|
||||
|
@ -473,12 +527,17 @@
|
|||
max-height: 80vh;
|
||||
}
|
||||
|
||||
#body[layout=vertical] #sources-pane > tabs {
|
||||
-moz-border-end: none;
|
||||
}
|
||||
|
||||
#body[layout=vertical] #instruments-pane {
|
||||
margin: 0 !important;
|
||||
/* To prevent all the margin hacks to hide the sidebar. */
|
||||
}
|
||||
|
||||
#body[layout=vertical] .side-menu-widget-container {
|
||||
#body[layout=vertical] .side-menu-widget-container,
|
||||
#body[layout=vertical] .side-menu-widget-empty-notice-container {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -312,6 +312,10 @@ box.requests-menu-status[code^="5"] {
|
|||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.side-menu-widget-item[odd] {
|
||||
background: rgba(255,255,255,0.05);
|
||||
}
|
||||
|
@ -363,10 +367,6 @@ box.requests-menu-status[code^="5"] {
|
|||
-moz-padding-start: 3px;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-string {
|
||||
color: #10c !important;
|
||||
}
|
||||
|
||||
/* Headers tabpanel */
|
||||
|
||||
#headers-summary-status,
|
||||
|
|
|
@ -285,15 +285,17 @@
|
|||
|
||||
/* SideMenuWidget container */
|
||||
|
||||
.side-menu-widget-container[with-arrows=true]:-moz-locale-dir(ltr) {
|
||||
.side-menu-widget-container:-moz-locale-dir(ltr),
|
||||
.side-menu-widget-empty-notice-container:-moz-locale-dir(ltr) {
|
||||
box-shadow: inset -1px 0 0 #222426;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true]:-moz-locale-dir(rtl) {
|
||||
.side-menu-widget-container:-moz-locale-dir(rtl),
|
||||
.side-menu-widget-empty-notice-container:-moz-locale-dir(rtl) {
|
||||
box-shadow: inset 1px 0 0 #222426;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true] .side-menu-widget-group {
|
||||
.side-menu-widget-group {
|
||||
/* To allow visibility of the dark margin shadow. */
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
@ -332,7 +334,9 @@
|
|||
|
||||
.side-menu-widget-item[theme="light"] {
|
||||
border-top: 1px solid hsla(210,8%,75%,.25);
|
||||
border-bottom: 1px solid hsla(210,16%,76%,.1);
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.side-menu-widget-item[theme="dark"]:last-of-type {
|
||||
|
@ -343,15 +347,11 @@
|
|||
box-shadow: inset 0 -1px 0 hsla(210,8%,75%,.25);
|
||||
}
|
||||
|
||||
.side-menu-widget-item[theme="dark"].selected {
|
||||
.side-menu-widget-item.selected {
|
||||
background: linear-gradient(hsl(206,61%,40%), hsl(206,61%,31%)) repeat-x top left !important;
|
||||
box-shadow: inset 0 1px 0 hsla(210,40%,83%,.15);
|
||||
}
|
||||
|
||||
.side-menu-widget-item[theme="light"].selected {
|
||||
/* Nothing here yet */
|
||||
}
|
||||
|
||||
.side-menu-widget-item.selected > .side-menu-widget-item-arrow {
|
||||
background-size: auto, 1px 100%;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -429,10 +429,6 @@
|
|||
|
||||
/* VariablesView */
|
||||
|
||||
.variables-view-container {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.variables-view-empty-notice {
|
||||
color: GrayText;
|
||||
padding: 2px;
|
||||
|
@ -442,11 +438,6 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.variables-view-scope:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
.variables-view-scope > .variables-view-element-details:not(:empty) {
|
||||
-moz-margin-start: 2px;
|
||||
-moz-margin-end: 1px;
|
||||
|
@ -455,13 +446,12 @@
|
|||
/* Generic traits applied to both variables and properties */
|
||||
|
||||
.variable-or-property {
|
||||
transition: background 1s ease-in-out;
|
||||
color: #000;
|
||||
transition: background 1s ease-in-out, color 1s ease-in-out;
|
||||
}
|
||||
|
||||
.variable-or-property[changed] {
|
||||
background: rgba(255,255,0,0.65);
|
||||
transition-duration: 0.4s;
|
||||
color: black;
|
||||
transition-duration: .4s;
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
|
@ -470,12 +460,6 @@
|
|||
-moz-padding-end: 4px;
|
||||
}
|
||||
|
||||
.variable-or-property:focus > .title {
|
||||
background: Highlight;
|
||||
color: HighlightText;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.variable-or-property[editable] > .title > .value {
|
||||
cursor: text;
|
||||
}
|
||||
|
@ -492,52 +476,22 @@
|
|||
}
|
||||
|
||||
.variables-view-variable:not(:last-child) {
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid rgba(128, 128, 128, .15);
|
||||
}
|
||||
|
||||
.variables-view-variable > .title > .name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.variables-view-variable:not(:focus) > .title > .name {
|
||||
color: #048;
|
||||
}
|
||||
|
||||
.variables-view-property:not(:focus) > .title > .name {
|
||||
color: #881090;
|
||||
}
|
||||
|
||||
/* Token value colors */
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-undefined {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-null {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-boolean {
|
||||
color: #10c;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-number {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-string {
|
||||
color: #282;
|
||||
}
|
||||
|
||||
.variable-or-property:not(:focus) > .title > .token-other {
|
||||
color: #333;
|
||||
.variable-or-property:focus > .title > label {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* Custom configurable/enumerable/writable or frozen/sealed/extensible
|
||||
* variables and properties */
|
||||
|
||||
.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]):not([scope]) > .title > .name {
|
||||
opacity: 0.5;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.variable-or-property[non-configurable] > .title > .name {
|
||||
|
@ -548,6 +502,10 @@
|
|||
border-bottom: 1px dashed #f99;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > .title > .name {
|
||||
border-bottom: 1px dashed #8b0;
|
||||
}
|
||||
|
||||
.variable-or-property-non-writable-icon {
|
||||
background: url("chrome://browser/skin/identity-icons-https.png") no-repeat;
|
||||
width: 16px;
|
||||
|
@ -574,27 +532,6 @@
|
|||
color: #666;
|
||||
}
|
||||
|
||||
/* Special variables and properties */
|
||||
|
||||
.variable-or-property[safe-getter] > .title > .name {
|
||||
border-bottom: 1px dashed #8b0;
|
||||
}
|
||||
|
||||
.variable-or-property[exception]:not(:focus) > .title > .name {
|
||||
color: #a00;
|
||||
text-shadow: 0 0 8px #fcc;
|
||||
}
|
||||
|
||||
.variable-or-property[return]:not(:focus) > .title > .name {
|
||||
color: #0a0;
|
||||
text-shadow: 0 0 8px #cfc;
|
||||
}
|
||||
|
||||
.variable-or-property[scope]:not(:focus) > .title > .name {
|
||||
color: #00a;
|
||||
text-shadow: 0 0 8px #ccf;
|
||||
}
|
||||
|
||||
/* Aligned values */
|
||||
|
||||
.variables-view-container[aligned-values] .title > .separator {
|
||||
|
@ -677,14 +614,13 @@
|
|||
.element-name-input {
|
||||
-moz-margin-start: -2px !important;
|
||||
-moz-margin-end: 2px !important;
|
||||
color: #048;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.element-value-input,
|
||||
.element-name-input {
|
||||
border: 1px solid #999 !important;
|
||||
box-shadow: 1px 2px 4px #aaa;
|
||||
border: 1px solid rgba(128, 128, 128, .5) !important;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Variables and properties searching */
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.ArrayList;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.gfx.GeckoLayerClient;
|
||||
import org.mozilla.gecko.gfx.GeckoLayerClient.DrawListener;
|
||||
import org.mozilla.gecko.mozglue.GeckoLoader;
|
||||
import org.mozilla.gecko.sqlite.SQLiteBridge;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
|
@ -24,112 +22,65 @@ import android.view.KeyEvent;
|
|||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.ArrayList;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.jayway.android.robotium.solo.Solo;
|
||||
|
||||
import static org.mozilla.gecko.FennecNativeDriver.LogLevel;
|
||||
|
||||
public class FennecNativeActions implements Actions {
|
||||
private static final String LOGTAG = "FennecNativeActions";
|
||||
|
||||
private Solo mSolo;
|
||||
private Instrumentation mInstr;
|
||||
private Activity mGeckoApp;
|
||||
private Assert mAsserter;
|
||||
|
||||
// Objects for reflexive access of fennec classes.
|
||||
private ClassLoader mClassLoader;
|
||||
private Class mApiClass;
|
||||
private Class mEventListenerClass;
|
||||
private Class mDrawListenerClass;
|
||||
private Method mRegisterEventListener;
|
||||
private Method mUnregisterEventListener;
|
||||
private Method mBroadcastEvent;
|
||||
private Method mPreferencesGetEvent;
|
||||
private Method mPreferencesObserveEvent;
|
||||
private Method mPreferencesRemoveObserversEvent;
|
||||
private Method mSetDrawListener;
|
||||
private Method mQuerySql;
|
||||
private Object mRobocopApi;
|
||||
|
||||
private static final String LOGTAG = "FennecNativeActions";
|
||||
|
||||
public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation, Assert asserter) {
|
||||
mSolo = robocop;
|
||||
mInstr = instrumentation;
|
||||
mGeckoApp = activity;
|
||||
mAsserter = asserter;
|
||||
// Set up reflexive access of java classes and methods.
|
||||
try {
|
||||
mClassLoader = activity.getClassLoader();
|
||||
|
||||
mApiClass = mClassLoader.loadClass("org.mozilla.gecko.RobocopAPI");
|
||||
mEventListenerClass = mClassLoader.loadClass("org.mozilla.gecko.util.GeckoEventListener");
|
||||
mDrawListenerClass = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoLayerClient$DrawListener");
|
||||
|
||||
mRegisterEventListener = mApiClass.getMethod("registerEventListener", String.class, mEventListenerClass);
|
||||
mUnregisterEventListener = mApiClass.getMethod("unregisterEventListener", String.class, mEventListenerClass);
|
||||
mBroadcastEvent = mApiClass.getMethod("broadcastEvent", String.class, String.class);
|
||||
mPreferencesGetEvent = mApiClass.getMethod("preferencesGetEvent", Integer.TYPE, String[].class);
|
||||
mPreferencesObserveEvent = mApiClass.getMethod("preferencesObserveEvent", Integer.TYPE, String[].class);
|
||||
mPreferencesRemoveObserversEvent = mApiClass.getMethod("preferencesRemoveObserversEvent", Integer.TYPE);
|
||||
mSetDrawListener = mApiClass.getMethod("setDrawListener", mDrawListenerClass);
|
||||
mQuerySql = mApiClass.getMethod("querySql", String.class, String.class);
|
||||
|
||||
mRobocopApi = mApiClass.getConstructor(Activity.class).newInstance(activity);
|
||||
} catch (Exception e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
class wakeInvocationHandler implements InvocationHandler {
|
||||
private final GeckoEventExpecter mEventExpecter;
|
||||
|
||||
public wakeInvocationHandler(GeckoEventExpecter expecter) {
|
||||
mEventExpecter = expecter;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
String methodName = method.getName();
|
||||
//Depending on the method, return a completely different type.
|
||||
if(methodName.equals("toString")) {
|
||||
return this.toString();
|
||||
}
|
||||
if(methodName.equals("equals")) {
|
||||
return
|
||||
args[0] == null ? false :
|
||||
this.toString().equals(args[0].toString());
|
||||
}
|
||||
if(methodName.equals("clone")) {
|
||||
return this;
|
||||
}
|
||||
if(methodName.equals("hashCode")) {
|
||||
return 314;
|
||||
}
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
||||
"Waking up on "+methodName);
|
||||
mEventExpecter.notifyOfEvent(args);
|
||||
return null;
|
||||
}
|
||||
GeckoLoader.loadSQLiteLibs(activity, activity.getApplication().getPackageResourcePath());
|
||||
}
|
||||
|
||||
class GeckoEventExpecter implements RepeatedEventExpecter {
|
||||
private final String mGeckoEvent;
|
||||
private Object[] mRegistrationParams;
|
||||
private boolean mEventEverReceived;
|
||||
private String mEventData;
|
||||
private BlockingQueue<String> mEventDataQueue;
|
||||
private static final int MAX_WAIT_MS = 90000;
|
||||
|
||||
GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
|
||||
private volatile boolean mIsRegistered;
|
||||
|
||||
private final String mGeckoEvent;
|
||||
private final GeckoEventListener mListener;
|
||||
|
||||
private volatile boolean mEventEverReceived;
|
||||
private String mEventData;
|
||||
private BlockingQueue<String> mEventDataQueue;
|
||||
|
||||
GeckoEventExpecter(final String geckoEvent) {
|
||||
if (TextUtils.isEmpty(geckoEvent)) {
|
||||
throw new IllegalArgumentException("geckoEvent must not be empty");
|
||||
}
|
||||
if (registrationParams == null || registrationParams.length == 0) {
|
||||
throw new IllegalArgumentException("registrationParams must not be empty");
|
||||
}
|
||||
|
||||
mGeckoEvent = geckoEvent;
|
||||
mRegistrationParams = registrationParams;
|
||||
mEventDataQueue = new LinkedBlockingQueue<String>();
|
||||
|
||||
final GeckoEventExpecter expecter = this;
|
||||
mListener = new GeckoEventListener() {
|
||||
@Override
|
||||
public void handleMessage(final String event, final JSONObject message) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
||||
"handleMessage called for: " + event + "; expecting: " + mGeckoEvent);
|
||||
mAsserter.is(event, mGeckoEvent, "Given message occurred for registered event");
|
||||
|
||||
expecter.notifyOfEvent(message);
|
||||
}
|
||||
};
|
||||
|
||||
GeckoAppShell.registerEventListener(mGeckoEvent, mListener);
|
||||
mIsRegistered = true;
|
||||
}
|
||||
|
||||
public void blockForEvent() {
|
||||
|
@ -137,9 +88,10 @@ public class FennecNativeActions implements Actions {
|
|||
}
|
||||
|
||||
private void blockForEvent(long millis, boolean failOnTimeout) {
|
||||
if (mRegistrationParams == null) {
|
||||
if (!mIsRegistered) {
|
||||
throw new IllegalStateException("listener not registered");
|
||||
}
|
||||
|
||||
try {
|
||||
mEventData = mEventDataQueue.poll(millis, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException ie) {
|
||||
|
@ -161,12 +113,13 @@ public class FennecNativeActions implements Actions {
|
|||
}
|
||||
|
||||
public void blockUntilClear(long millis) {
|
||||
if (mRegistrationParams == null) {
|
||||
if (!mIsRegistered) {
|
||||
throw new IllegalStateException("listener not registered");
|
||||
}
|
||||
if (millis <= 0) {
|
||||
throw new IllegalArgumentException("millis must be > 0");
|
||||
}
|
||||
|
||||
// wait for at least one event
|
||||
try {
|
||||
mEventData = mEventDataQueue.poll(MAX_WAIT_MS, TimeUnit.MILLISECONDS);
|
||||
|
@ -205,139 +158,82 @@ public class FennecNativeActions implements Actions {
|
|||
}
|
||||
|
||||
public void unregisterListener() {
|
||||
if (mRegistrationParams == null) {
|
||||
if (!mIsRegistered) {
|
||||
throw new IllegalStateException("listener not registered");
|
||||
}
|
||||
try {
|
||||
FennecNativeDriver.log(LogLevel.INFO, "EventExpecter: no longer listening for "+mGeckoEvent);
|
||||
mUnregisterEventListener.invoke(mRobocopApi, mRegistrationParams);
|
||||
mRegistrationParams = null;
|
||||
} catch (IllegalAccessException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
}
|
||||
|
||||
FennecNativeDriver.log(LogLevel.INFO,
|
||||
"EventExpecter: no longer listening for " + mGeckoEvent);
|
||||
|
||||
GeckoAppShell.unregisterEventListener(mGeckoEvent, mListener);
|
||||
mIsRegistered = false;
|
||||
}
|
||||
|
||||
public synchronized boolean eventReceived() {
|
||||
public boolean eventReceived() {
|
||||
return mEventEverReceived;
|
||||
}
|
||||
|
||||
void notifyOfEvent(Object[] args) {
|
||||
void notifyOfEvent(final JSONObject message) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
||||
"received event " + mGeckoEvent);
|
||||
synchronized (this) {
|
||||
mEventEverReceived = true;
|
||||
}
|
||||
"received event " + mGeckoEvent);
|
||||
|
||||
mEventEverReceived = true;
|
||||
|
||||
try {
|
||||
mEventDataQueue.put(args[1].toString());
|
||||
mEventDataQueue.put(message.toString());
|
||||
} catch (InterruptedException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR,
|
||||
"EventExpecter dropped event: "+args[1].toString());
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
"EventExpecter dropped event: " + message.toString(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RepeatedEventExpecter expectGeckoEvent(String geckoEvent) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
||||
"waiting for "+geckoEvent);
|
||||
try {
|
||||
Object[] finalParams = new Object[2];
|
||||
finalParams[0] = geckoEvent;
|
||||
GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
|
||||
wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
|
||||
Object proxy = Proxy.newProxyInstance(mClassLoader, new Class[] { mEventListenerClass }, wIH);
|
||||
finalParams[1] = proxy;
|
||||
|
||||
mRegisterEventListener.invoke(mRobocopApi, finalParams);
|
||||
return expecter;
|
||||
} catch (IllegalAccessException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
}
|
||||
return null;
|
||||
public RepeatedEventExpecter expectGeckoEvent(final String geckoEvent) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, "waiting for " + geckoEvent);
|
||||
return new GeckoEventExpecter(geckoEvent);
|
||||
}
|
||||
|
||||
public void sendGeckoEvent(String geckoEvent, String data) {
|
||||
try {
|
||||
mBroadcastEvent.invoke(mRobocopApi, geckoEvent, data);
|
||||
} catch (IllegalAccessException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPreferencesEvent(Method method, int requestId, String[] prefNames) {
|
||||
try {
|
||||
method.invoke(mRobocopApi, requestId, prefNames);
|
||||
} catch (IllegalAccessException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
}
|
||||
public void sendGeckoEvent(final String geckoEvent, final String data) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(geckoEvent, data));
|
||||
}
|
||||
|
||||
public void sendPreferencesGetEvent(int requestId, String[] prefNames) {
|
||||
sendPreferencesEvent(mPreferencesGetEvent, requestId, prefNames);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesGetEvent(requestId, prefNames));
|
||||
}
|
||||
|
||||
public void sendPreferencesObserveEvent(int requestId, String[] prefNames) {
|
||||
sendPreferencesEvent(mPreferencesObserveEvent, requestId, prefNames);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesObserveEvent(requestId, prefNames));
|
||||
}
|
||||
|
||||
public void sendPreferencesRemoveObserversEvent(int requestId) {
|
||||
try {
|
||||
mPreferencesRemoveObserversEvent.invoke(mRobocopApi, requestId);
|
||||
} catch (IllegalAccessException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
class DrawListenerProxy implements InvocationHandler {
|
||||
private final PaintExpecter mPaintExpecter;
|
||||
|
||||
DrawListenerProxy(PaintExpecter paintExpecter) {
|
||||
mPaintExpecter = paintExpecter;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
String methodName = method.getName();
|
||||
if ("drawFinished".equals(methodName)) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
||||
"Received drawFinished notification");
|
||||
mPaintExpecter.notifyOfEvent(args);
|
||||
} else if ("toString".equals(methodName)) {
|
||||
return "DrawListenerProxy";
|
||||
} else if ("equals".equals(methodName)) {
|
||||
return false;
|
||||
} else if ("hashCode".equals(methodName)) {
|
||||
return 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesRemoveObserversEvent(requestId));
|
||||
}
|
||||
|
||||
class PaintExpecter implements RepeatedEventExpecter {
|
||||
private boolean mPaintDone;
|
||||
private boolean mListening;
|
||||
private static final int MAX_WAIT_MS = 90000;
|
||||
|
||||
PaintExpecter() throws IllegalAccessException, InvocationTargetException {
|
||||
Object proxy = Proxy.newProxyInstance(mClassLoader, new Class[] { mDrawListenerClass }, new DrawListenerProxy(this));
|
||||
mSetDrawListener.invoke(mRobocopApi, proxy);
|
||||
private boolean mPaintDone;
|
||||
private boolean mListening;
|
||||
|
||||
private final GeckoLayerClient mLayerClient;
|
||||
|
||||
PaintExpecter() {
|
||||
final PaintExpecter expecter = this;
|
||||
mLayerClient = GeckoAppShell.getLayerView().getLayerClient();
|
||||
mLayerClient.setDrawListener(new DrawListener() {
|
||||
@Override
|
||||
public void drawFinished() {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
||||
"Received drawFinished notification");
|
||||
expecter.notifyOfEvent();
|
||||
}
|
||||
});
|
||||
mListening = true;
|
||||
}
|
||||
|
||||
void notifyOfEvent(Object[] args) {
|
||||
synchronized (this) {
|
||||
mPaintDone = true;
|
||||
this.notifyAll();
|
||||
}
|
||||
private synchronized void notifyOfEvent() {
|
||||
mPaintDone = true;
|
||||
this.notifyAll();
|
||||
}
|
||||
|
||||
private synchronized void blockForEvent(long millis, boolean failOnTimeout) {
|
||||
|
@ -431,23 +327,16 @@ public class FennecNativeActions implements Actions {
|
|||
if (!mListening) {
|
||||
throw new IllegalStateException("listener not registered");
|
||||
}
|
||||
try {
|
||||
FennecNativeDriver.log(LogLevel.INFO, "PaintExpecter: no longer listening for events");
|
||||
mListening = false;
|
||||
mSetDrawListener.invoke(mRobocopApi, (Object)null);
|
||||
} catch (Exception e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
}
|
||||
|
||||
FennecNativeDriver.log(LogLevel.INFO,
|
||||
"PaintExpecter: no longer listening for events");
|
||||
mLayerClient.setDrawListener(null);
|
||||
mListening = false;
|
||||
}
|
||||
}
|
||||
|
||||
public RepeatedEventExpecter expectPaint() {
|
||||
try {
|
||||
return new PaintExpecter();
|
||||
} catch (Exception e) {
|
||||
FennecNativeDriver.log(LogLevel.ERROR, e);
|
||||
return null;
|
||||
}
|
||||
return new PaintExpecter();
|
||||
}
|
||||
|
||||
public void sendSpecialKey(SpecialKey button) {
|
||||
|
@ -495,14 +384,7 @@ public class FennecNativeActions implements Actions {
|
|||
mSolo.drag(startingX, endingX, startingY, endingY, 10);
|
||||
}
|
||||
|
||||
public Cursor querySql(String dbPath, String sql) {
|
||||
try {
|
||||
return (Cursor)mQuerySql.invoke(mRobocopApi, dbPath, sql);
|
||||
} catch(InvocationTargetException ex) {
|
||||
Log.e(LOGTAG, "Error invoking method", ex);
|
||||
} catch(IllegalAccessException ex) {
|
||||
Log.e(LOGTAG, "Error using field", ex);
|
||||
}
|
||||
return null;
|
||||
public Cursor querySql(final String dbPath, final String sql) {
|
||||
return new SQLiteBridge(dbPath).rawQuery(sql, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.gfx.PanningPerfAPI;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
|
@ -20,11 +24,6 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.view.View;
|
||||
|
@ -46,18 +45,6 @@ public class FennecNativeDriver implements Driver {
|
|||
private static String mLogFile = null;
|
||||
private static LogLevel mLogLevel = LogLevel.INFO;
|
||||
|
||||
// Objects for reflexive access of fennec classes.
|
||||
private ClassLoader mClassLoader;
|
||||
private Class mApiClass;
|
||||
private Class mEventListenerClass;
|
||||
private Method mRegisterEventListener;
|
||||
private Method mGetPixels;
|
||||
private Method mStartFrameRecording;
|
||||
private Method mStopFrameRecording;
|
||||
private Method mStartCheckerboardRecording;
|
||||
private Method mStopCheckerboardRecording;
|
||||
private Object mRobocopApi;
|
||||
|
||||
public enum LogLevel {
|
||||
DEBUG(1),
|
||||
INFO(2),
|
||||
|
@ -83,25 +70,6 @@ public class FennecNativeDriver implements Driver {
|
|||
|
||||
// Set up table of fennec_ids.
|
||||
mLocators = convertTextToTable(getFile(mRootPath + "/fennec_ids.txt"));
|
||||
|
||||
// Set up reflexive access of java classes and methods.
|
||||
try {
|
||||
mClassLoader = activity.getClassLoader();
|
||||
|
||||
mApiClass = mClassLoader.loadClass("org.mozilla.gecko.RobocopAPI");
|
||||
mEventListenerClass = mClassLoader.loadClass("org.mozilla.gecko.util.GeckoEventListener");
|
||||
|
||||
mRegisterEventListener = mApiClass.getMethod("registerEventListener", String.class, mEventListenerClass);
|
||||
mGetPixels = mApiClass.getMethod("getViewPixels", View.class);
|
||||
mStartFrameRecording = mApiClass.getDeclaredMethod("startFrameTimeRecording");
|
||||
mStopFrameRecording = mApiClass.getDeclaredMethod("stopFrameTimeRecording");
|
||||
mStartCheckerboardRecording = mApiClass.getDeclaredMethod("startCheckerboardRecording");
|
||||
mStopCheckerboardRecording = mApiClass.getDeclaredMethod("stopCheckerboardRecording");
|
||||
|
||||
mRobocopApi = mApiClass.getConstructor(Activity.class).newInstance(activity);
|
||||
} catch (Exception e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
//Information on the location of the Gecko Frame.
|
||||
|
@ -172,99 +140,59 @@ public class FennecNativeDriver implements Driver {
|
|||
}
|
||||
|
||||
public void startFrameRecording() {
|
||||
try {
|
||||
mStartFrameRecording.invoke(null);
|
||||
} catch (IllegalAccessException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
}
|
||||
PanningPerfAPI.startFrameTimeRecording();
|
||||
}
|
||||
|
||||
public int stopFrameRecording() {
|
||||
try {
|
||||
List<Long> frames = (List<Long>)mStopFrameRecording.invoke(null);
|
||||
int badness = 0;
|
||||
for (int i = 1; i < frames.size(); i++) {
|
||||
long frameTime = frames.get(i) - frames.get(i - 1);
|
||||
int delay = (int)(frameTime - FRAME_TIME_THRESHOLD);
|
||||
// for each frame we miss, add the square of the delay. This
|
||||
// makes large delays much worse than small delays.
|
||||
if (delay > 0) {
|
||||
badness += delay * delay;
|
||||
}
|
||||
final List<Long> frames = PanningPerfAPI.stopFrameTimeRecording();
|
||||
int badness = 0;
|
||||
for (int i = 1; i < frames.size(); i++) {
|
||||
long frameTime = frames.get(i) - frames.get(i - 1);
|
||||
int delay = (int)(frameTime - FRAME_TIME_THRESHOLD);
|
||||
// for each frame we miss, add the square of the delay. This
|
||||
// makes large delays much worse than small delays.
|
||||
if (delay > 0) {
|
||||
badness += delay * delay;
|
||||
}
|
||||
// Don't do any averaging of the numbers because really we want to
|
||||
// know how bad the jank was at its worst
|
||||
return badness;
|
||||
} catch (IllegalAccessException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
}
|
||||
|
||||
// higher values are worse, and the test failing is the worst!
|
||||
return Integer.MAX_VALUE;
|
||||
// Don't do any averaging of the numbers because really we want to
|
||||
// know how bad the jank was at its worst
|
||||
return badness;
|
||||
}
|
||||
|
||||
public void startCheckerboardRecording() {
|
||||
try {
|
||||
mStartCheckerboardRecording.invoke(null);
|
||||
} catch (IllegalAccessException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
}
|
||||
PanningPerfAPI.startCheckerboardRecording();
|
||||
}
|
||||
|
||||
public float stopCheckerboardRecording() {
|
||||
try {
|
||||
List<Float> checkerboard = (List<Float>)mStopCheckerboardRecording.invoke(null);
|
||||
float total = 0;
|
||||
for (float val : checkerboard) {
|
||||
total += val;
|
||||
}
|
||||
return total * 100.0f;
|
||||
} catch (IllegalAccessException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
final List<Float> checkerboard = PanningPerfAPI.stopCheckerboardRecording();
|
||||
float total = 0;
|
||||
for (float val : checkerboard) {
|
||||
total += val;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
return total * 100.0f;
|
||||
}
|
||||
|
||||
private View getSurfaceView() {
|
||||
ArrayList<View> views = mSolo.getCurrentViews();
|
||||
try {
|
||||
Class c = Class.forName("org.mozilla.gecko.gfx.LayerView");
|
||||
for (View v : views) {
|
||||
if (c.isInstance(v)) {
|
||||
return v;
|
||||
}
|
||||
private LayerView getSurfaceView() {
|
||||
final LayerView layerView = mSolo.getView(LayerView.class, 0);
|
||||
|
||||
if (layerView == null) {
|
||||
log(LogLevel.WARN, "getSurfaceView could not find LayerView");
|
||||
for (final View v : mSolo.getViews()) {
|
||||
log(LogLevel.WARN, " View: " + v);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
}
|
||||
log(LogLevel.WARN, "getSurfaceView could not find LayerView");
|
||||
for (View v : views) {
|
||||
log(LogLevel.WARN, v.toString());
|
||||
}
|
||||
return null;
|
||||
return layerView;
|
||||
}
|
||||
|
||||
public PaintedSurface getPaintedSurface() {
|
||||
View view = getSurfaceView();
|
||||
final LayerView view = getSurfaceView();
|
||||
if (view == null) {
|
||||
return null;
|
||||
}
|
||||
IntBuffer pixelBuffer;
|
||||
try {
|
||||
pixelBuffer = (IntBuffer)mGetPixels.invoke(mRobocopApi, view);
|
||||
} catch (Exception e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
final IntBuffer pixelBuffer = view.getPixels();
|
||||
|
||||
// now we need to (1) flip the image, because GL likes to do things up-side-down,
|
||||
// and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
|
||||
|
@ -312,27 +240,6 @@ public class FennecNativeDriver implements Driver {
|
|||
public int mScrollHeight=0;
|
||||
public int mPageHeight=10;
|
||||
|
||||
class scrollHandler implements InvocationHandler {
|
||||
public scrollHandler(){};
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
try {
|
||||
// Disect the JSON object into the appropriate variables
|
||||
JSONObject jo = ((JSONObject)args[1]);
|
||||
mScrollHeight = jo.getInt("y");
|
||||
mHeight = jo.getInt("cheight");
|
||||
// We don't want a height of 0. That means it's a bad response.
|
||||
if (mHeight > 0) {
|
||||
mPageHeight = jo.getInt("height");
|
||||
}
|
||||
|
||||
} catch( Throwable e) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.WARN,
|
||||
"WARNING: ScrollReceived, but read wrong!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getScrollHeight() {
|
||||
return mScrollHeight;
|
||||
}
|
||||
|
@ -344,20 +251,23 @@ public class FennecNativeDriver implements Driver {
|
|||
}
|
||||
|
||||
public void setupScrollHandling() {
|
||||
//Setup scrollHandler to catch "robocop:scroll" events.
|
||||
try {
|
||||
Class [] interfaces = new Class[1];
|
||||
interfaces[0] = mEventListenerClass;
|
||||
Object[] finalParams = new Object[2];
|
||||
finalParams[0] = "robocop:scroll";
|
||||
finalParams[1] = Proxy.newProxyInstance(mClassLoader, interfaces, new scrollHandler());
|
||||
mRegisterEventListener.invoke(mRobocopApi, finalParams);
|
||||
} catch (IllegalAccessException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
log(LogLevel.ERROR, e);
|
||||
}
|
||||
|
||||
GeckoAppShell.registerEventListener("robocop:scroll", new GeckoEventListener() {
|
||||
@Override
|
||||
public void handleMessage(final String event, final JSONObject message) {
|
||||
try {
|
||||
mScrollHeight = message.getInt("y");
|
||||
mHeight = message.getInt("cheight");
|
||||
// We don't want a height of 0. That means it's a bad response.
|
||||
if (mHeight > 0) {
|
||||
mPageHeight = message.getInt("height");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
FennecNativeDriver.log(FennecNativeDriver.LogLevel.WARN,
|
||||
"WARNING: ScrollReceived, but message does not contain " +
|
||||
"expected fields: " + e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,7 +78,7 @@ SetObexPacketInfo(uint8_t* aRetBuf, uint8_t aOpcode, int aPacketLength)
|
|||
aRetBuf[2] = aPacketLength & 0x00FF;
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
ParseHeaders(const uint8_t* aHeaderStart,
|
||||
int aTotalLength,
|
||||
ObexHeaderSet* aRetHandlerSet)
|
||||
|
@ -88,7 +88,7 @@ ParseHeaders(const uint8_t* aHeaderStart,
|
|||
while (ptr - aHeaderStart < aTotalLength) {
|
||||
ObexHeaderId headerId = (ObexHeaderId)*ptr++;
|
||||
|
||||
int contentLength = 0;
|
||||
uint16_t contentLength = 0;
|
||||
uint8_t highByte, lowByte;
|
||||
|
||||
// Defined in 2.1 OBEX Headers, IrOBEX 1.2
|
||||
|
@ -101,7 +101,7 @@ ParseHeaders(const uint8_t* aHeaderStart,
|
|||
// byte sequence, length prefixed with 2 byte unsigned integer.
|
||||
highByte = *ptr++;
|
||||
lowByte = *ptr++;
|
||||
contentLength = (((int)highByte << 8) | lowByte) - 3;
|
||||
contentLength = (((uint16_t)highByte << 8) | lowByte) - 3;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
|
@ -115,10 +115,20 @@ ParseHeaders(const uint8_t* aHeaderStart,
|
|||
break;
|
||||
}
|
||||
|
||||
aRetHandlerSet->AddHeader(new ObexHeader(headerId, contentLength, ptr));
|
||||
// Length check to prevent from memory pollusion.
|
||||
if (ptr + contentLength > aHeaderStart + aTotalLength) {
|
||||
// Severe error occurred. We can't even believe the received data, so
|
||||
// clear all headers.
|
||||
MOZ_ASSERT(false);
|
||||
aRetHandlerSet->ClearHeaders();
|
||||
return false;
|
||||
}
|
||||
|
||||
aRetHandlerSet->AddHeader(new ObexHeader(headerId, contentLength, ptr));
|
||||
ptr += contentLength;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
|
|
@ -102,7 +102,8 @@ enum ObexResponseCode {
|
|||
DatabaseLocked = 0xE1,
|
||||
};
|
||||
|
||||
class ObexHeader {
|
||||
class ObexHeader
|
||||
{
|
||||
public:
|
||||
ObexHeader(ObexHeaderId aId, int aDataLength, const uint8_t* aData)
|
||||
: mId(aId)
|
||||
|
@ -122,11 +123,9 @@ public:
|
|||
nsAutoArrayPtr<uint8_t> mData;
|
||||
};
|
||||
|
||||
class ObexHeaderSet {
|
||||
class ObexHeaderSet
|
||||
{
|
||||
public:
|
||||
uint8_t mOpcode;
|
||||
nsTArray<nsAutoPtr<ObexHeader> > mHeaders;
|
||||
|
||||
ObexHeaderSet(uint8_t aOpcode) : mOpcode(aOpcode)
|
||||
{
|
||||
}
|
||||
|
@ -241,6 +240,15 @@ public:
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClearHeaders()
|
||||
{
|
||||
mHeaders.Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t mOpcode;
|
||||
nsTArray<nsAutoPtr<ObexHeader> > mHeaders;
|
||||
};
|
||||
|
||||
int AppendHeaderName(uint8_t* aRetBuf, const char* aName, int aLength);
|
||||
|
@ -249,7 +257,11 @@ int AppendHeaderEndOfBody(uint8_t* aRetBuf);
|
|||
int AppendHeaderLength(uint8_t* aRetBuf, int aObjectLength);
|
||||
int AppendHeaderConnectionId(uint8_t* aRetBuf, int aConnectionId);
|
||||
void SetObexPacketInfo(uint8_t* aRetBuf, uint8_t aOpcode, int aPacketLength);
|
||||
void ParseHeaders(const uint8_t* aHeaderStart,
|
||||
|
||||
/**
|
||||
* @return true when the message was parsed without any error, false otherwise.
|
||||
*/
|
||||
bool ParseHeaders(const uint8_t* aHeaderStart,
|
||||
int aTotalLength,
|
||||
ObexHeaderSet* aRetHanderSet);
|
||||
|
||||
|
|
|
@ -803,25 +803,31 @@ BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage)
|
|||
// Section 3.3.1 "Connect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
||||
// [Headers:var]
|
||||
ParseHeaders(&aMessage->mData[7],
|
||||
receivedLength - 7,
|
||||
&pktHeaders);
|
||||
if (!ParseHeaders(&aMessage->mData[7], receivedLength - 7, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
ReplyToConnect();
|
||||
AfterOppConnected();
|
||||
} else if (opCode == ObexRequestCode::Abort) {
|
||||
// Section 3.3.5 "Abort", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
ParseHeaders(&aMessage->mData[3],
|
||||
receivedLength - 3,
|
||||
&pktHeaders);
|
||||
if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
ReplyToDisconnectOrAbort();
|
||||
DeleteReceivedFile();
|
||||
} else if (opCode == ObexRequestCode::Disconnect) {
|
||||
// Section 3.3.2 "Disconnect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
ParseHeaders(&aMessage->mData[3],
|
||||
receivedLength - 3,
|
||||
&pktHeaders);
|
||||
if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
ReplyToDisconnectOrAbort();
|
||||
AfterOppDisconnected();
|
||||
FileTransferComplete();
|
||||
|
@ -1197,7 +1203,7 @@ BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
|
|||
void
|
||||
BluetoothOppManager::ReplyError(uint8_t aError)
|
||||
{
|
||||
if (!mConnected) return;
|
||||
BT_LOGR("error: %d", aError);
|
||||
|
||||
// Section 3.2 "Response Format", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
|
|
|
@ -341,9 +341,12 @@ Call::IsActive()
|
|||
/**
|
||||
* BluetoothHfpManager
|
||||
*/
|
||||
BluetoothHfpManager::BluetoothHfpManager() : mPhoneType(PhoneType::NONE)
|
||||
, mController(nullptr)
|
||||
BluetoothHfpManager::BluetoothHfpManager() : mController(nullptr)
|
||||
{
|
||||
#ifdef MOZ_B2G_RIL
|
||||
mPhoneType = PhoneType::NONE;
|
||||
#endif // MOZ_B2G_RIL
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -819,25 +819,31 @@ BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage)
|
|||
// Section 3.3.1 "Connect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
||||
// [Headers:var]
|
||||
ParseHeaders(&aMessage->mData[7],
|
||||
receivedLength - 7,
|
||||
&pktHeaders);
|
||||
if (!ParseHeaders(&aMessage->mData[7], receivedLength - 7, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
ReplyToConnect();
|
||||
AfterOppConnected();
|
||||
} else if (opCode == ObexRequestCode::Abort) {
|
||||
// Section 3.3.5 "Abort", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
ParseHeaders(&aMessage->mData[3],
|
||||
receivedLength - 3,
|
||||
&pktHeaders);
|
||||
if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
ReplyToDisconnectOrAbort();
|
||||
DeleteReceivedFile();
|
||||
} else if (opCode == ObexRequestCode::Disconnect) {
|
||||
// Section 3.3.2 "Disconnect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
ParseHeaders(&aMessage->mData[3],
|
||||
receivedLength - 3,
|
||||
&pktHeaders);
|
||||
if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
ReplyToDisconnectOrAbort();
|
||||
AfterOppDisconnected();
|
||||
FileTransferComplete();
|
||||
|
@ -1213,7 +1219,7 @@ BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
|
|||
void
|
||||
BluetoothOppManager::ReplyError(uint8_t aError)
|
||||
{
|
||||
if (!mConnected) return;
|
||||
BT_LOGR("error: %d", aError);
|
||||
|
||||
// Section 3.2 "Response Format", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
|
|
|
@ -79,6 +79,7 @@ enum {
|
|||
CAMERA_PARAM_PICTURESIZE,
|
||||
CAMERA_PARAM_THUMBNAILSIZE,
|
||||
CAMERA_PARAM_THUMBNAILQUALITY,
|
||||
CAMERA_PARAM_SENSORANGLE,
|
||||
|
||||
CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
|
||||
CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
|
||||
|
|
|
@ -268,6 +268,14 @@ CameraControlImpl::Get(uint32_t aKey, idl::CameraSize& aSize)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::Get(uint32_t aKey, int32_t* aValue)
|
||||
{
|
||||
MOZ_ASSERT(aValue);
|
||||
*aValue = GetParameterInt32(aKey);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<RecorderProfileManager>
|
||||
CameraControlImpl::GetRecorderProfileManager()
|
||||
{
|
||||
|
|
|
@ -75,6 +75,7 @@ public:
|
|||
nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange);
|
||||
nsresult Set(uint32_t aKey, const idl::CameraSize& aSize);
|
||||
nsresult Get(uint32_t aKey, idl::CameraSize& aSize);
|
||||
nsresult Get(uint32_t aKey, int32_t* aValue);
|
||||
|
||||
nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue)
|
||||
{
|
||||
|
@ -92,6 +93,7 @@ public:
|
|||
virtual const char* GetParameter(const char* aKey) = 0;
|
||||
virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
|
||||
virtual double GetParameterDouble(uint32_t aKey) = 0;
|
||||
virtual int32_t GetParameterInt32(uint32_t aKey) = 0;
|
||||
virtual void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions) = 0;
|
||||
virtual void GetParameter(uint32_t aKey, idl::CameraSize& aSize) = 0;
|
||||
virtual void SetParameter(const char* aKey, const char* aValue) = 0;
|
||||
|
|
|
@ -285,6 +285,14 @@ nsDOMCameraControl::GetExposureCompensation(ErrorResult& aRv)
|
|||
return compensation;
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsDOMCameraControl::SensorAngle()
|
||||
{
|
||||
int32_t angle;
|
||||
mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, &angle);
|
||||
return angle;
|
||||
}
|
||||
|
||||
already_AddRefed<nsICameraShutterCallback>
|
||||
nsDOMCameraControl::GetOnShutter(ErrorResult& aRv)
|
||||
{
|
||||
|
|
|
@ -76,6 +76,7 @@ public:
|
|||
double GetFocusDistanceFar(ErrorResult& aRv);
|
||||
void SetExposureCompensation(const dom::Optional<double>& aCompensation, ErrorResult& aRv);
|
||||
double GetExposureCompensation(ErrorResult& aRv);
|
||||
int32_t SensorAngle();
|
||||
already_AddRefed<nsICameraShutterCallback> GetOnShutter(ErrorResult& aRv);
|
||||
void SetOnShutter(nsICameraShutterCallback* aCb, ErrorResult& aRv);
|
||||
already_AddRefed<nsICameraClosedCallback> GetOnClosed(ErrorResult& aRv);
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
const char* GetParameter(const char* aKey);
|
||||
const char* GetParameterConstChar(uint32_t aKey);
|
||||
double GetParameterDouble(uint32_t aKey);
|
||||
int32_t GetParameterInt32(uint32_t aKey);
|
||||
void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions);
|
||||
void GetParameter(uint32_t aKey, idl::CameraSize& aSize);
|
||||
void SetParameter(const char* aKey, const char* aValue);
|
||||
|
@ -101,6 +102,12 @@ nsFallbackCameraControl::GetParameterDouble(uint32_t aKey)
|
|||
return NAN;
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsFallbackCameraControl::GetParameterInt32(uint32_t aKey)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nsFallbackCameraControl::GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions)
|
||||
{
|
||||
|
|
|
@ -434,6 +434,25 @@ nsGonkCameraControl::GetParameterDouble(uint32_t aKey)
|
|||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsGonkCameraControl::GetParameterInt32(uint32_t aKey)
|
||||
{
|
||||
if (aKey == CAMERA_PARAM_SENSORANGLE) {
|
||||
if (!mCameraHw.get()) {
|
||||
return 0;
|
||||
}
|
||||
return mCameraHw->GetSensorOrientation();
|
||||
}
|
||||
|
||||
const char* key = getKeyText(aKey);
|
||||
if (!key) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
RwAutoLockRead lock(mRwLock);
|
||||
return mParams.getInt(key);
|
||||
}
|
||||
|
||||
void
|
||||
nsGonkCameraControl::GetParameter(uint32_t aKey,
|
||||
nsTArray<idl::CameraRegion>& aRegions)
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
const char* GetParameter(const char* aKey);
|
||||
const char* GetParameterConstChar(uint32_t aKey);
|
||||
double GetParameterDouble(uint32_t aKey);
|
||||
int32_t GetParameterInt32(uint32_t aKey);
|
||||
void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions);
|
||||
void GetParameter(uint32_t aKey, nsTArray<idl::CameraSize>& aSizes);
|
||||
void GetParameter(uint32_t aKey, idl::CameraSize& aSize);
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
virtual nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, const idl::CameraSize& aSize) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, idl::CameraSize& aSize) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, int32_t* aValue) = 0;
|
||||
virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0;
|
||||
virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0;
|
||||
virtual nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes) = 0;
|
||||
|
|
|
@ -62,6 +62,9 @@ this.SystemMessagePermissionsTable = {
|
|||
"settings": ["read", "write"]
|
||||
},
|
||||
"media-button": { },
|
||||
"networkstats-alarm": {
|
||||
"networkstats-manage": []
|
||||
},
|
||||
"notification": {
|
||||
"desktop-notification": []
|
||||
},
|
||||
|
|
|
@ -20,7 +20,22 @@ interface nsIDOMMozNetworkStatsInterface : nsISupports
|
|||
readonly attribute DOMString id;
|
||||
};
|
||||
|
||||
[scriptable, uuid(5f033d31-c9a2-4e2d-83aa-6a807f1e0c11)]
|
||||
dictionary NetworkStatsAlarmOptions
|
||||
{
|
||||
jsval startTime; // Date object
|
||||
jsval data;
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c)]
|
||||
interface nsIDOMMozNetworkStatsAlarm : nsISupports
|
||||
{
|
||||
readonly attribute unsigned long alarmId;
|
||||
readonly attribute nsIDOMMozNetworkStatsInterface network;
|
||||
readonly attribute long threshold;
|
||||
readonly attribute jsval data;
|
||||
};
|
||||
|
||||
[scriptable, uuid(50d109b8-0d7f-4208-81fe-5f07a759f159)]
|
||||
interface nsIDOMMozNetworkStatsManager : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -42,6 +57,41 @@ interface nsIDOMMozNetworkStatsManager : nsISupports
|
|||
in jsval end,
|
||||
[optional] in DOMString manifestURL);
|
||||
|
||||
/**
|
||||
* Install an alarm on a network. The network must be in the return of
|
||||
* getAvailableNetworks() otherwise an "InvalidNetwork" exception will
|
||||
* be raised.
|
||||
*
|
||||
* When total data usage reaches threshold bytes, a "networkstats-alarm"
|
||||
* system message is sent to the application, where the optional parameter
|
||||
* |data| must be a cloneable object.
|
||||
*
|
||||
* If success, the |result| field of the DOMRequest keeps the alarm Id.
|
||||
*/
|
||||
nsIDOMDOMRequest addAlarm(in nsIDOMMozNetworkStatsInterface network,
|
||||
in long threshold,
|
||||
[optional] in jsval options /* NetworkStatsAlarmOptions */);
|
||||
|
||||
/**
|
||||
* Obtain all alarms for those networks returned by getAvailableNetworks().
|
||||
* If a network is provided, only retrieves the alarms for that network.
|
||||
* The network must be one of those returned by getAvailebleNetworks() or an
|
||||
* "InvalidNetwork" exception will be raised.
|
||||
*
|
||||
* Each alarm object has the same fields as that in the system message:
|
||||
* - alarmId
|
||||
* - network
|
||||
* - threshold
|
||||
* - data
|
||||
*/
|
||||
nsIDOMDOMRequest getAllAlarms([optional] in nsIDOMMozNetworkStatsInterface network);
|
||||
|
||||
/**
|
||||
* Remove all network alarms. If an |alarmId| is provided, then only that
|
||||
* alarm is removed.
|
||||
*/
|
||||
nsIDOMDOMRequest removeAlarms([optional] in long alarmId);
|
||||
|
||||
/**
|
||||
* Remove all stats related with the provided network from DB.
|
||||
*/
|
||||
|
|
|
@ -16,8 +16,9 @@ Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
|
|||
Cu.importGlobalProperties(["indexedDB"]);
|
||||
|
||||
const DB_NAME = "net_stats";
|
||||
const DB_VERSION = 4;
|
||||
const STORE_NAME = "net_stats";
|
||||
const DB_VERSION = 5;
|
||||
const STATS_STORE_NAME = "net_stats";
|
||||
const ALARMS_STORE_NAME = "net_alarm";
|
||||
|
||||
// Constant defining the maximum values allowed per interface. If more, older
|
||||
// will be erased.
|
||||
|
@ -30,20 +31,20 @@ this.NetworkStatsDB = function NetworkStatsDB() {
|
|||
if (DEBUG) {
|
||||
debug("Constructor");
|
||||
}
|
||||
this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME]);
|
||||
this.initDBHelper(DB_NAME, DB_VERSION, [STATS_STORE_NAME, ALARMS_STORE_NAME]);
|
||||
}
|
||||
|
||||
NetworkStatsDB.prototype = {
|
||||
__proto__: IndexedDBHelper.prototype,
|
||||
|
||||
dbNewTxn: function dbNewTxn(txn_type, callback, txnCb) {
|
||||
dbNewTxn: function dbNewTxn(store_name, txn_type, callback, txnCb) {
|
||||
function successCb(result) {
|
||||
txnCb(null, result);
|
||||
}
|
||||
function errorCb(error) {
|
||||
txnCb(error, null);
|
||||
}
|
||||
return this.newTxn(txn_type, STORE_NAME, callback, successCb, errorCb);
|
||||
return this.newTxn(txn_type, store_name, callback, successCb, errorCb);
|
||||
},
|
||||
|
||||
upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
|
||||
|
@ -58,7 +59,7 @@ NetworkStatsDB.prototype = {
|
|||
* Create the initial database schema.
|
||||
*/
|
||||
|
||||
objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["connectionType", "timestamp"] });
|
||||
objectStore = db.createObjectStore(STATS_STORE_NAME, { keyPath: ["connectionType", "timestamp"] });
|
||||
objectStore.createIndex("connectionType", "connectionType", { unique: false });
|
||||
objectStore.createIndex("timestamp", "timestamp", { unique: false });
|
||||
objectStore.createIndex("rxBytes", "rxBytes", { unique: false });
|
||||
|
@ -77,9 +78,9 @@ NetworkStatsDB.prototype = {
|
|||
// to modify the keyPath is mandatory to delete the object store
|
||||
// and create it again. Old data is going to be deleted because the
|
||||
// networkId for each sample can not be set.
|
||||
db.deleteObjectStore(STORE_NAME);
|
||||
db.deleteObjectStore(STATS_STORE_NAME);
|
||||
|
||||
objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["appId", "network", "timestamp"] });
|
||||
objectStore = db.createObjectStore(STATS_STORE_NAME, { keyPath: ["appId", "network", "timestamp"] });
|
||||
objectStore.createIndex("appId", "appId", { unique: false });
|
||||
objectStore.createIndex("network", "network", { unique: false });
|
||||
objectStore.createIndex("networkType", "networkType", { unique: false });
|
||||
|
@ -94,7 +95,7 @@ NetworkStatsDB.prototype = {
|
|||
}
|
||||
} else if (currVersion == 3) {
|
||||
// Delete redundent indexes (leave "network" only).
|
||||
objectStore = aTransaction.objectStore(STORE_NAME);
|
||||
objectStore = aTransaction.objectStore(STATS_STORE_NAME);
|
||||
if (objectStore.indexNames.contains("appId")) {
|
||||
objectStore.deleteIndex("appId");
|
||||
}
|
||||
|
@ -120,18 +121,82 @@ NetworkStatsDB.prototype = {
|
|||
if (DEBUG) {
|
||||
debug("Deleted redundent indexes for version 4");
|
||||
}
|
||||
} else if (currVersion == 4) {
|
||||
// In order to manage alarms, it is necessary to use a global counter
|
||||
// (totalBytes) that will increase regardless of the system reboot.
|
||||
objectStore = aTransaction.objectStore(STATS_STORE_NAME);
|
||||
|
||||
// Now, systemBytes will hold the old totalBytes and totalBytes will
|
||||
// keep the increasing counter. |counters| will keep the track of
|
||||
// accumulated values.
|
||||
let counters = {};
|
||||
|
||||
objectStore.openCursor().onsuccess = function(event) {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor){
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.value.rxSystemBytes = cursor.value.rxTotalBytes;
|
||||
cursor.value.txSystemBytes = cursor.value.txTotalBytes;
|
||||
|
||||
if (cursor.value.appId == 0) {
|
||||
let netId = cursor.value.network[0] + '' + cursor.value.network[1];
|
||||
if (!counters[netId]) {
|
||||
counters[netId] = {
|
||||
rxCounter: 0,
|
||||
txCounter: 0,
|
||||
lastRx: 0,
|
||||
lastTx: 0
|
||||
};
|
||||
}
|
||||
|
||||
let rxDiff = cursor.value.rxSystemBytes - counters[netId].lastRx;
|
||||
let txDiff = cursor.value.txSystemBytes - counters[netId].lastTx;
|
||||
if (rxDiff < 0 || txDiff < 0) {
|
||||
// System reboot between samples, so take the current one.
|
||||
rxDiff = cursor.value.rxSystemBytes;
|
||||
txDiff = cursor.value.txSystemBytes;
|
||||
}
|
||||
|
||||
counters[netId].rxCounter += rxDiff;
|
||||
counters[netId].txCounter += txDiff;
|
||||
cursor.value.rxTotalBytes = counters[netId].rxCounter;
|
||||
cursor.value.txTotalBytes = counters[netId].txCounter;
|
||||
|
||||
counters[netId].lastRx = cursor.value.rxSystemBytes;
|
||||
counters[netId].lastTx = cursor.value.txSystemBytes;
|
||||
} else {
|
||||
cursor.value.rxTotalBytes = cursor.value.rxSystemBytes;
|
||||
cursor.value.txTotalBytes = cursor.value.txSystemBytes;
|
||||
}
|
||||
|
||||
cursor.update(cursor.value);
|
||||
cursor.continue();
|
||||
};
|
||||
|
||||
// Create object store for alarms.
|
||||
objectStore = db.createObjectStore(ALARMS_STORE_NAME, { keyPath: "id", autoIncrement: true });
|
||||
objectStore.createIndex("alarm", ['networkId','threshold'], { unique: false });
|
||||
objectStore.createIndex("manifestURL", "manifestURL", { unique: false });
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Created alarms store for version 5");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
importData: function importData(aStats) {
|
||||
let stats = { appId: aStats.appId,
|
||||
network: [aStats.networkId, aStats.networkType],
|
||||
timestamp: aStats.timestamp,
|
||||
rxBytes: aStats.rxBytes,
|
||||
txBytes: aStats.txBytes,
|
||||
rxTotalBytes: aStats.rxTotalBytes,
|
||||
txTotalBytes: aStats.txTotalBytes };
|
||||
let stats = { appId: aStats.appId,
|
||||
network: [aStats.networkId, aStats.networkType],
|
||||
timestamp: aStats.timestamp,
|
||||
rxBytes: aStats.rxBytes,
|
||||
txBytes: aStats.txBytes,
|
||||
rxSystemBytes: aStats.rxSystemBytes,
|
||||
txSystemBytes: aStats.txSystemBytes,
|
||||
rxTotalBytes: aStats.rxTotalBytes,
|
||||
txTotalBytes: aStats.txTotalBytes };
|
||||
|
||||
return stats;
|
||||
},
|
||||
|
@ -160,18 +225,20 @@ NetworkStatsDB.prototype = {
|
|||
saveStats: function saveStats(aStats, aResultCb) {
|
||||
let timestamp = this.normalizeDate(aStats.date);
|
||||
|
||||
let stats = { appId: aStats.appId,
|
||||
networkId: aStats.networkId,
|
||||
networkType: aStats.networkType,
|
||||
timestamp: timestamp,
|
||||
rxBytes: (aStats.appId == 0) ? 0 : aStats.rxBytes,
|
||||
txBytes: (aStats.appId == 0) ? 0 : aStats.txBytes,
|
||||
rxTotalBytes: (aStats.appId == 0) ? aStats.rxBytes : 0,
|
||||
txTotalBytes: (aStats.appId == 0) ? aStats.txBytes : 0 };
|
||||
let stats = { appId: aStats.appId,
|
||||
networkId: aStats.networkId,
|
||||
networkType: aStats.networkType,
|
||||
timestamp: timestamp,
|
||||
rxBytes: (aStats.appId == 0) ? 0 : aStats.rxBytes,
|
||||
txBytes: (aStats.appId == 0) ? 0 : aStats.txBytes,
|
||||
rxSystemBytes: (aStats.appId == 0) ? aStats.rxBytes : 0,
|
||||
txSystemBytes: (aStats.appId == 0) ? aStats.txBytes : 0,
|
||||
rxTotalBytes: (aStats.appId == 0) ? aStats.rxBytes : 0,
|
||||
txTotalBytes: (aStats.appId == 0) ? aStats.txBytes : 0 };
|
||||
|
||||
stats = this.importData(stats);
|
||||
|
||||
this.dbNewTxn("readwrite", function(aTxn, aStore) {
|
||||
this.dbNewTxn(STATS_STORE_NAME, "readwrite", function(aTxn, aStore) {
|
||||
if (DEBUG) {
|
||||
debug("Filtered time: " + new Date(timestamp));
|
||||
debug("New stats: " + JSON.stringify(stats));
|
||||
|
@ -241,21 +308,29 @@ NetworkStatsDB.prototype = {
|
|||
// |txTotalBytes|/|rxTotalBytes| and the last |txTotalBytes|/|rxTotalBytes|.
|
||||
// Else, the incoming data is per-app data (|newSample.appId| is not 0),
|
||||
// the |txBytes|/|rxBytes| is directly the new |txBytes|/|rxBytes|.
|
||||
let rxDiff = 0;
|
||||
let txDiff = 0;
|
||||
if (aNewSample.appId == 0) {
|
||||
let rxDiff = aNewSample.rxTotalBytes - lastSample.rxTotalBytes;
|
||||
let txDiff = aNewSample.txTotalBytes - lastSample.txTotalBytes;
|
||||
rxDiff = aNewSample.rxSystemBytes - lastSample.rxSystemBytes;
|
||||
txDiff = aNewSample.txSystemBytes - lastSample.txSystemBytes;
|
||||
if (rxDiff < 0 || txDiff < 0) {
|
||||
rxDiff = aNewSample.rxTotalBytes;
|
||||
txDiff = aNewSample.txTotalBytes;
|
||||
rxDiff = aNewSample.rxSystemBytes;
|
||||
txDiff = aNewSample.txSystemBytes;
|
||||
}
|
||||
aNewSample.rxBytes = rxDiff;
|
||||
aNewSample.txBytes = txDiff;
|
||||
|
||||
aNewSample.rxTotalBytes = lastSample.rxTotalBytes + rxDiff;
|
||||
aNewSample.txTotalBytes = lastSample.txTotalBytes + txDiff;
|
||||
} else {
|
||||
rxDiff = aNewSample.rxBytes;
|
||||
txDiff = aNewSample.txBytes;
|
||||
}
|
||||
|
||||
if (diff == 1) {
|
||||
// New element.
|
||||
|
||||
// If the incoming data is per-data data, new |rxTotalBytes|/|txTotalBytes|
|
||||
// If the incoming data is per-app data, new |rxTotalBytes|/|txTotalBytes|
|
||||
// needs to be obtained by adding new |rxBytes|/|txBytes| to last
|
||||
// |rxTotalBytes|/|txTotalBytes|.
|
||||
if (aNewSample.appId != 0) {
|
||||
|
@ -277,13 +352,15 @@ NetworkStatsDB.prototype = {
|
|||
let data = [];
|
||||
for (let i = diff - 2; i >= 0; i--) {
|
||||
let time = aNewSample.timestamp - SAMPLE_RATE * (i + 1);
|
||||
let sample = { appId: aNewSample.appId,
|
||||
network: aNewSample.network,
|
||||
timestamp: time,
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
rxTotalBytes: lastSample.rxTotalBytes,
|
||||
txTotalBytes: lastSample.txTotalBytes };
|
||||
let sample = { appId: aNewSample.appId,
|
||||
network: aNewSample.network,
|
||||
timestamp: time,
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
rxSystemBytes: lastSample.rxSystemBytes,
|
||||
txSystemBytes: lastSample.txSystemBytes,
|
||||
rxTotalBytes: lastSample.rxTotalBytes,
|
||||
txTotalBytes: lastSample.txTotalBytes };
|
||||
|
||||
data.push(sample);
|
||||
}
|
||||
|
@ -293,27 +370,20 @@ NetworkStatsDB.prototype = {
|
|||
return;
|
||||
}
|
||||
if (diff == 0 || diff < 0) {
|
||||
// New element received before samplerate period.
|
||||
// It means that device has been restarted (or clock / timezone change).
|
||||
// Update element.
|
||||
// New element received before samplerate period. It means that device has
|
||||
// been restarted (or clock / timezone change).
|
||||
// Update element. If diff < 0, clock or timezone changed back. Place data
|
||||
// in the last sample.
|
||||
|
||||
// If diff < 0, clock or timezone changed back. Place data in the last sample.
|
||||
// Old |rxTotalBytes|/|txTotalBytes| needs to get updated by adding the
|
||||
// last |rxTotalBytes|/|txTotalBytes|.
|
||||
lastSample.rxBytes += rxDiff;
|
||||
lastSample.txBytes += txDiff;
|
||||
lastSample.rxSystemBytes = aNewSample.rxSystemBytes;
|
||||
lastSample.txSystemBytes = aNewSample.txSystemBytes;
|
||||
lastSample.rxTotalBytes += rxDiff;
|
||||
lastSample.txTotalBytes += txDiff;
|
||||
|
||||
lastSample.rxBytes += aNewSample.rxBytes;
|
||||
lastSample.txBytes += aNewSample.txBytes;
|
||||
|
||||
// If incoming data is obtained from netd, last |rxTotalBytes|/|txTotalBytes|
|
||||
// needs to get updated by replacing the new |rxTotalBytes|/|txTotalBytes|.
|
||||
if (aNewSample.appId == 0) {
|
||||
lastSample.rxTotalBytes = aNewSample.rxTotalBytes;
|
||||
lastSample.txTotalBytes = aNewSample.txTotalBytes;
|
||||
} else {
|
||||
// Else, the incoming data is per-app data, old |rxTotalBytes|/
|
||||
// |txTotalBytes| needs to get updated by adding the new
|
||||
// |rxBytes|/|txBytes| to last |rxTotalBytes|/|txTotalBytes|.
|
||||
lastSample.rxTotalBytes += aNewSample.rxBytes;
|
||||
lastSample.txTotalBytes += aNewSample.txBytes;
|
||||
}
|
||||
if (DEBUG) {
|
||||
debug("Update: " + JSON.stringify(lastSample));
|
||||
}
|
||||
|
@ -379,7 +449,7 @@ NetworkStatsDB.prototype = {
|
|||
let self = this;
|
||||
|
||||
// Clear and save an empty sample to keep sync with system counters
|
||||
this.dbNewTxn("readwrite", function(aTxn, aStore) {
|
||||
this.dbNewTxn(STATS_STORE_NAME, "readwrite", function(aTxn, aStore) {
|
||||
let sample = null;
|
||||
let request = aStore.index("network").openCursor(network, "prev");
|
||||
request.onsuccess = function onsuccess(event) {
|
||||
|
@ -431,6 +501,33 @@ NetworkStatsDB.prototype = {
|
|||
this.clearInterfaceStats(aNetworks[index], callback);
|
||||
},
|
||||
|
||||
getCurrentStats: function getCurrentStats(aNetwork, aDate, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug("Get current stats for " + JSON.stringify(aNetwork) + " since " + aDate);
|
||||
}
|
||||
|
||||
this.dbNewTxn(STATS_STORE_NAME, "readonly", function(txn, store) {
|
||||
let request = null;
|
||||
let network = [aNetwork.id, aNetwork.type];
|
||||
if (aDate) {
|
||||
let start = this.normalizeDate(aDate);
|
||||
let lowerFilter = [0, network, start];
|
||||
let range = this.dbGlobal.IDBKeyRange.lowerBound(lowerFilter, false);
|
||||
request = store.openCursor(range);
|
||||
} else {
|
||||
request = store.index("network").openCursor(network, "prev");
|
||||
}
|
||||
|
||||
request.onsuccess = function onsuccess(event) {
|
||||
txn.result = null;
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
txn.result = cursor.value;
|
||||
}
|
||||
};
|
||||
}.bind(this), aResultCb);
|
||||
},
|
||||
|
||||
find: function find(aResultCb, aNetwork, aStart, aEnd, aAppId, aManifestURL) {
|
||||
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
let start = this.normalizeDate(aStart);
|
||||
|
@ -443,7 +540,7 @@ NetworkStatsDB.prototype = {
|
|||
debug("End time: " + new Date(end));
|
||||
}
|
||||
|
||||
this.dbNewTxn("readonly", function(aTxn, aStore) {
|
||||
this.dbNewTxn(STATS_STORE_NAME, "readonly", function(aTxn, aStore) {
|
||||
let network = [aNetwork.id, aNetwork.type];
|
||||
let lowerFilter = [aAppId, network, start];
|
||||
let upperFilter = [aAppId, network, end];
|
||||
|
@ -503,7 +600,7 @@ NetworkStatsDB.prototype = {
|
|||
},
|
||||
|
||||
getAvailableNetworks: function getAvailableNetworks(aResultCb) {
|
||||
this.dbNewTxn("readonly", function(aTxn, aStore) {
|
||||
this.dbNewTxn(STATS_STORE_NAME, "readonly", function(aTxn, aStore) {
|
||||
if (!aTxn.result) {
|
||||
aTxn.result = [];
|
||||
}
|
||||
|
@ -522,7 +619,7 @@ NetworkStatsDB.prototype = {
|
|||
},
|
||||
|
||||
isNetworkAvailable: function isNetworkAvailable(aNetwork, aResultCb) {
|
||||
this.dbNewTxn("readonly", function(aTxn, aStore) {
|
||||
this.dbNewTxn(STATS_STORE_NAME, "readonly", function(aTxn, aStore) {
|
||||
if (!aTxn.result) {
|
||||
aTxn.result = false;
|
||||
}
|
||||
|
@ -546,10 +643,159 @@ NetworkStatsDB.prototype = {
|
|||
},
|
||||
|
||||
logAllRecords: function logAllRecords(aResultCb) {
|
||||
this.dbNewTxn("readonly", function(aTxn, aStore) {
|
||||
this.dbNewTxn(STATS_STORE_NAME, "readonly", function(aTxn, aStore) {
|
||||
aStore.mozGetAll().onsuccess = function onsuccess(event) {
|
||||
aTxn.result = event.target.result;
|
||||
};
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
alarmToRecord: function alarmToRecord(aAlarm) {
|
||||
let record = { networkId: aAlarm.networkId,
|
||||
threshold: aAlarm.threshold,
|
||||
data: aAlarm.data,
|
||||
manifestURL: aAlarm.manifestURL,
|
||||
pageURL: aAlarm.pageURL };
|
||||
|
||||
if (aAlarm.id) {
|
||||
record.id = aAlarm.id;
|
||||
}
|
||||
|
||||
return record;
|
||||
},
|
||||
|
||||
recordToAlarm: function recordToalarm(aRecord) {
|
||||
let alarm = { networkId: aRecord.networkId,
|
||||
threshold: aRecord.threshold,
|
||||
data: aRecord.data,
|
||||
manifestURL: aRecord.manifestURL,
|
||||
pageURL: aRecord.pageURL };
|
||||
|
||||
if (aRecord.id) {
|
||||
alarm.id = aRecord.id;
|
||||
}
|
||||
|
||||
return alarm;
|
||||
},
|
||||
|
||||
addAlarm: function addAlarm(aAlarm, aResultCb) {
|
||||
this.dbNewTxn(ALARMS_STORE_NAME, "readwrite", function(txn, store) {
|
||||
if (DEBUG) {
|
||||
debug("Going to add " + JSON.stringify(aAlarm));
|
||||
}
|
||||
|
||||
let record = this.alarmToRecord(aAlarm);
|
||||
store.put(record).onsuccess = function setResult(aEvent) {
|
||||
txn.result = aEvent.target.result;
|
||||
if (DEBUG) {
|
||||
debug("Request successful. New record ID: " + txn.result);
|
||||
}
|
||||
};
|
||||
}.bind(this), aResultCb);
|
||||
},
|
||||
|
||||
getFirstAlarm: function getFirstAlarm(aNetworkId, aResultCb) {
|
||||
let self = this;
|
||||
|
||||
this.dbNewTxn(ALARMS_STORE_NAME, "readonly", function(txn, store) {
|
||||
if (DEBUG) {
|
||||
debug("Get first alarm for network " + aNetworkId);
|
||||
}
|
||||
|
||||
let lowerFilter = [aNetworkId, 0];
|
||||
let upperFilter = [aNetworkId, ""];
|
||||
let range = IDBKeyRange.bound(lowerFilter, upperFilter);
|
||||
|
||||
store.index("alarm").openCursor(range).onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
txn.result = null;
|
||||
if (cursor) {
|
||||
txn.result = self.recordToAlarm(cursor.value);
|
||||
}
|
||||
};
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
removeAlarm: function removeAlarm(aAlarmId, aManifestURL, aResultCb) {
|
||||
this.dbNewTxn(ALARMS_STORE_NAME, "readwrite", function(txn, store) {
|
||||
if (DEBUG) {
|
||||
debug("Remove alarm " + aAlarmId);
|
||||
}
|
||||
|
||||
store.get(aAlarmId).onsuccess = function onsuccess(event) {
|
||||
let record = event.target.result;
|
||||
txn.result = false;
|
||||
if (!record || (aManifestURL && record.manifestURL != aManifestURL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.delete(aAlarmId);
|
||||
txn.result = true;
|
||||
}
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
removeAlarms: function removeAlarms(aManifestURL, aResultCb) {
|
||||
this.dbNewTxn(ALARMS_STORE_NAME, "readwrite", function(txn, store) {
|
||||
if (DEBUG) {
|
||||
debug("Remove alarms of " + aManifestURL);
|
||||
}
|
||||
|
||||
store.index("manifestURL").openCursor(aManifestURL)
|
||||
.onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
cursor.delete();
|
||||
cursor.continue();
|
||||
}
|
||||
}
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
updateAlarm: function updateAlarm(aAlarm, aResultCb) {
|
||||
let self = this;
|
||||
this.dbNewTxn(ALARMS_STORE_NAME, "readwrite", function(txn, store) {
|
||||
if (DEBUG) {
|
||||
debug("Update alarm " + aAlarm.id);
|
||||
}
|
||||
|
||||
let record = self.alarmToRecord(aAlarm);
|
||||
store.openCursor(record.id).onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
txn.result = false;
|
||||
if (cursor) {
|
||||
cursor.update(record);
|
||||
txn.result = true;
|
||||
}
|
||||
}
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
getAlarms: function getAlarms(aNetworkId, aManifestURL, aResultCb) {
|
||||
this.dbNewTxn(ALARMS_STORE_NAME, "readonly", function(txn, store) {
|
||||
if (DEBUG) {
|
||||
debug("Get alarms for " + aManifestURL);
|
||||
}
|
||||
|
||||
txn.result = [];
|
||||
store.index("manifestURL").openCursor(aManifestURL)
|
||||
.onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aNetworkId || cursor.value.networkId == aNetworkId) {
|
||||
let alarm = { id: cursor.value.id,
|
||||
networkId: cursor.value.networkId,
|
||||
threshold: cursor.value.threshold,
|
||||
data: cursor.value.data };
|
||||
|
||||
txn.result.push(alarm);
|
||||
}
|
||||
|
||||
cursor.continue();
|
||||
}
|
||||
}, aResultCb);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
|||
// NetworkStatsData
|
||||
const nsIClassInfo = Ci.nsIClassInfo;
|
||||
const NETWORKSTATSDATA_CID = Components.ID("{3b16fe17-5583-483a-b486-b64a3243221c}");
|
||||
const nsIDOMMozNetworkStatsData = Components.interfaces.nsIDOMMozNetworkStatsData;
|
||||
const nsIDOMMozNetworkStatsData = Ci.nsIDOMMozNetworkStatsData;
|
||||
|
||||
function NetworkStatsData(aData) {
|
||||
this.rxBytes = aData.rxBytes;
|
||||
|
@ -40,10 +40,10 @@ function NetworkStatsData(aData) {
|
|||
|
||||
NetworkStatsData.prototype = {
|
||||
__exposedProps__: {
|
||||
rxBytes: 'r',
|
||||
txBytes: 'r',
|
||||
date: 'r',
|
||||
},
|
||||
rxBytes: 'r',
|
||||
txBytes: 'r',
|
||||
date: 'r',
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSDATA_CID,
|
||||
classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSDATA_CID,
|
||||
|
@ -58,7 +58,7 @@ NetworkStatsData.prototype = {
|
|||
// NetworkStatsInterface
|
||||
const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
|
||||
const NETWORKSTATSINTERFACE_CID = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
|
||||
const nsIDOMMozNetworkStatsInterface = Components.interfaces.nsIDOMMozNetworkStatsInterface;
|
||||
const nsIDOMMozNetworkStatsInterface = Ci.nsIDOMMozNetworkStatsInterface;
|
||||
|
||||
function NetworkStatsInterface(aNetwork) {
|
||||
if (DEBUG) {
|
||||
|
@ -70,9 +70,9 @@ function NetworkStatsInterface(aNetwork) {
|
|||
|
||||
NetworkStatsInterface.prototype = {
|
||||
__exposedProps__: {
|
||||
id: 'r',
|
||||
type: 'r',
|
||||
},
|
||||
id: 'r',
|
||||
type: 'r',
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSINTERFACE_CID,
|
||||
classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSINTERFACE_CID,
|
||||
|
@ -87,7 +87,7 @@ NetworkStatsInterface.prototype = {
|
|||
// NetworkStats
|
||||
const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1";
|
||||
const NETWORKSTATS_CID = Components.ID("{b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}");
|
||||
const nsIDOMMozNetworkStats = Components.interfaces.nsIDOMMozNetworkStats;
|
||||
const nsIDOMMozNetworkStats = Ci.nsIDOMMozNetworkStats;
|
||||
|
||||
function NetworkStats(aWindow, aStats) {
|
||||
if (DEBUG) {
|
||||
|
@ -106,12 +106,12 @@ function NetworkStats(aWindow, aStats) {
|
|||
|
||||
NetworkStats.prototype = {
|
||||
__exposedProps__: {
|
||||
manifestURL: 'r',
|
||||
network: 'r',
|
||||
start: 'r',
|
||||
end: 'r',
|
||||
data: 'r',
|
||||
},
|
||||
manifestURL: 'r',
|
||||
network: 'r',
|
||||
start: 'r',
|
||||
end: 'r',
|
||||
data: 'r',
|
||||
},
|
||||
|
||||
classID : NETWORKSTATS_CID,
|
||||
classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATS_CID,
|
||||
|
@ -125,11 +125,40 @@ NetworkStats.prototype = {
|
|||
nsIDOMMozNetworkStatsInterface])
|
||||
}
|
||||
|
||||
// NetworkStatsAlarm
|
||||
const NETWORKSTATSALARM_CID = Components.ID("{063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c}");
|
||||
const nsIDOMMozNetworkStatsAlarm = Ci.nsIDOMMozNetworkStatsAlarm;
|
||||
|
||||
function NetworkStatsAlarm(aAlarm) {
|
||||
this.alarmId = aAlarm.id;
|
||||
this.network = new NetworkStatsInterface(aAlarm.network);
|
||||
this.threshold = aAlarm.threshold;
|
||||
this.data = aAlarm.data;
|
||||
}
|
||||
|
||||
NetworkStatsAlarm.prototype = {
|
||||
__exposedProps__: {
|
||||
alarmId: 'r',
|
||||
network: 'r',
|
||||
threshold: 'r',
|
||||
data: 'r',
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSALARM_CID,
|
||||
classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSALARM_CID,
|
||||
contractID:"@mozilla.org/networkstatsalarm;1",
|
||||
classDescription: "NetworkStatsAlarm",
|
||||
interfaces: [nsIDOMMozNetworkStatsAlarm],
|
||||
flags: nsIClassInfo.DOM_OBJECT}),
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsAlarm])
|
||||
};
|
||||
|
||||
// NetworkStatsManager
|
||||
|
||||
const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
|
||||
const NETWORKSTATSMANAGER_CID = Components.ID("{5f033d31-c9a2-4e2d-83aa-6a807f1e0c11}");
|
||||
const nsIDOMMozNetworkStatsManager = Components.interfaces.nsIDOMMozNetworkStatsManager;
|
||||
const NETWORKSTATSMANAGER_CID = Components.ID("{50d109b8-0d7f-4208-81fe-5f07a759f159}");
|
||||
const nsIDOMMozNetworkStatsManager = Ci.nsIDOMMozNetworkStatsManager;
|
||||
|
||||
function NetworkStatsManager() {
|
||||
if (DEBUG) {
|
||||
|
@ -189,6 +218,52 @@ NetworkStatsManager.prototype = {
|
|||
return request;
|
||||
},
|
||||
|
||||
addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) {
|
||||
this.checkPrivileges();
|
||||
|
||||
if (!aOptions) {
|
||||
aOptions = Object.create(null);
|
||||
}
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:SetAlarm",
|
||||
{id: this.getRequestId(request),
|
||||
data: {network: aNetwork,
|
||||
threshold: aThreshold,
|
||||
startTime: aOptions.startTime,
|
||||
data: aOptions.data,
|
||||
manifestURL: this.manifestURL,
|
||||
pageURL: this.pageURL}});
|
||||
return request;
|
||||
},
|
||||
|
||||
getAllAlarms: function getAllAlarms(aNetwork) {
|
||||
this.checkPrivileges();
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:GetAlarms",
|
||||
{id: this.getRequestId(request),
|
||||
data: {network: aNetwork,
|
||||
manifestURL: this.manifestURL}});
|
||||
return request;
|
||||
},
|
||||
|
||||
removeAlarms: function removeAlarms(aAlarmId) {
|
||||
this.checkPrivileges();
|
||||
|
||||
if (aAlarmId == 0) {
|
||||
aAlarmId = -1;
|
||||
}
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:RemoveAlarms",
|
||||
{id: this.getRequestId(request),
|
||||
data: {alarmId: aAlarmId,
|
||||
manifestURL: this.manifestURL}});
|
||||
|
||||
return request;
|
||||
},
|
||||
|
||||
getAvailableNetworks: function getAvailableNetworks() {
|
||||
this.checkPrivileges();
|
||||
|
||||
|
@ -212,8 +287,8 @@ NetworkStatsManager.prototype = {
|
|||
if (DEBUG) {
|
||||
debug("NetworkStatsmanager::receiveMessage: " + aMessage.name);
|
||||
}
|
||||
let msg = aMessage.json;
|
||||
|
||||
let msg = aMessage.json;
|
||||
let req = this.takeRequest(msg.id);
|
||||
if (!req) {
|
||||
if (DEBUG) {
|
||||
|
@ -260,6 +335,30 @@ NetworkStatsManager.prototype = {
|
|||
Services.DOMRequest.fireSuccess(req, true);
|
||||
break;
|
||||
|
||||
case "NetworkStats:SetAlarm:Return":
|
||||
case "NetworkStats:RemoveAlarms:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, msg.result);
|
||||
break;
|
||||
|
||||
case "NetworkStats:GetAlarms:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let alarms = Cu.createArrayIn(this._window);
|
||||
for (let i = 0; i < msg.result.length; i++) {
|
||||
alarms.push(new NetworkStatsAlarm(msg.result[i]));
|
||||
}
|
||||
|
||||
Services.DOMRequest.fireSuccess(req, alarms);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG) {
|
||||
debug("Wrong message: " + aMessage.name);
|
||||
|
@ -293,7 +392,21 @@ NetworkStatsManager.prototype = {
|
|||
this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
|
||||
"NetworkStats:GetAvailableNetworks:Return",
|
||||
"NetworkStats:Clear:Return",
|
||||
"NetworkStats:ClearAll:Return"]);
|
||||
"NetworkStats:ClearAll:Return",
|
||||
"NetworkStats:SetAlarm:Return",
|
||||
"NetworkStats:GetAlarms:Return",
|
||||
"NetworkStats:RemoveAlarms:Return"]);
|
||||
|
||||
// Init app properties.
|
||||
let appsService = Cc["@mozilla.org/AppsService;1"]
|
||||
.getService(Ci.nsIAppsService);
|
||||
|
||||
this.manifestURL = appsService.getManifestURLByLocalId(principal.appId);
|
||||
|
||||
let isApp = !!this.manifestURL.length;
|
||||
if (isApp) {
|
||||
this.pageURL = principal.URI.spec;
|
||||
}
|
||||
},
|
||||
|
||||
// Called from DOMRequestIpcHelper
|
||||
|
@ -316,7 +429,8 @@ NetworkStatsManager.prototype = {
|
|||
flags: nsIClassInfo.DOM_OBJECT})
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData,
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm,
|
||||
NetworkStatsData,
|
||||
NetworkStatsInterface,
|
||||
NetworkStats,
|
||||
NetworkStatsManager]);
|
||||
|
|
|
@ -7,6 +7,9 @@ contract @mozilla.org/networkStats;1 {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}
|
|||
component {f540615b-d803-43ff-8200-2a9d145a5645} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkstatsinterface;1 {f540615b-d803-43ff-8200-2a9d145a5645}
|
||||
|
||||
component {5f033d31-c9a2-4e2d-83aa-6a807f1e0c11} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStatsManager;1 {5f033d31-c9a2-4e2d-83aa-6a807f1e0c11}
|
||||
component {063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkstatsalarm;1 {063ebeb2-5c6e-47ae-bdcd-5e6ebdc7a68c}
|
||||
|
||||
component {50d109b8-0d7f-4208-81fe-5f07a759f159} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStatsManager;1 {50d109b8-0d7f-4208-81fe-5f07a759f159}
|
||||
category JavaScript-navigator-property mozNetworkStats @mozilla.org/networkStatsManager;1
|
||||
|
|
|
@ -22,6 +22,8 @@ Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
|
|||
const NET_NETWORKSTATSSERVICE_CONTRACTID = "@mozilla.org/network/netstatsservice;1";
|
||||
const NET_NETWORKSTATSSERVICE_CID = Components.ID("{18725604-e9ac-488a-8aa0-2471e7f6c0a4}");
|
||||
|
||||
const TOPIC_BANDWIDTH_CONTROL = "netd-bandwidth-control"
|
||||
|
||||
const TOPIC_INTERFACE_REGISTERED = "network-interface-registered";
|
||||
const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
|
||||
const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
|
||||
|
@ -50,6 +52,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
|
|||
"@mozilla.org/settingsService;1",
|
||||
"nsISettingsService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "messenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
this.NetworkStatsService = {
|
||||
init: function() {
|
||||
debug("Service started");
|
||||
|
@ -57,6 +63,7 @@ this.NetworkStatsService = {
|
|||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, false);
|
||||
Services.obs.addObserver(this, TOPIC_INTERFACE_UNREGISTERED, false);
|
||||
Services.obs.addObserver(this, TOPIC_BANDWIDTH_CONTROL, false);
|
||||
Services.obs.addObserver(this, "profile-after-change", false);
|
||||
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
|
@ -87,6 +94,9 @@ this.NetworkStatsService = {
|
|||
this.messages = ["NetworkStats:Get",
|
||||
"NetworkStats:Clear",
|
||||
"NetworkStats:ClearAll",
|
||||
"NetworkStats:SetAlarm",
|
||||
"NetworkStats:GetAlarms",
|
||||
"NetworkStats:RemoveAlarms",
|
||||
"NetworkStats:GetAvailableNetworks",
|
||||
"NetworkStats:SampleRate",
|
||||
"NetworkStats:MaxStorageAge"];
|
||||
|
@ -107,6 +117,9 @@ this.NetworkStatsService = {
|
|||
|
||||
this.updateQueue = [];
|
||||
this.isQueueRunning = false;
|
||||
|
||||
this._currentAlarms = {};
|
||||
this.initAlarms();
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
|
@ -129,6 +142,15 @@ this.NetworkStatsService = {
|
|||
case "NetworkStats:ClearAll":
|
||||
this.clearDB(mm, msg);
|
||||
break;
|
||||
case "NetworkStats:SetAlarm":
|
||||
this.setAlarm(mm, msg);
|
||||
break;
|
||||
case "NetworkStats:GetAlarms":
|
||||
this.getAlarms(mm, msg);
|
||||
break;
|
||||
case "NetworkStats:RemoveAlarms":
|
||||
this.removeAlarms(mm, msg);
|
||||
break;
|
||||
case "NetworkStats:GetAvailableNetworks":
|
||||
this.getAvailableNetworks(mm, msg);
|
||||
break;
|
||||
|
@ -146,7 +168,7 @@ this.NetworkStatsService = {
|
|||
case TOPIC_INTERFACE_REGISTERED:
|
||||
case TOPIC_INTERFACE_UNREGISTERED:
|
||||
|
||||
// If new interface is registered (notified from NetworkManager),
|
||||
// If new interface is registered (notified from NetworkService),
|
||||
// the stats are updated for the new interface without waiting to
|
||||
// complete the updating period.
|
||||
|
||||
|
@ -158,9 +180,27 @@ this.NetworkStatsService = {
|
|||
break;
|
||||
}
|
||||
|
||||
this._updateCurrentAlarm(netId);
|
||||
|
||||
debug("NetId: " + netId);
|
||||
this.updateStats(netId);
|
||||
break;
|
||||
|
||||
case TOPIC_BANDWIDTH_CONTROL:
|
||||
debug("Bandwidth message from netd: " + JSON.stringify(aData));
|
||||
|
||||
let interfaceName = aData.substring(aData.lastIndexOf(" ") + 1);
|
||||
for (let networkId in this._networks) {
|
||||
if (interfaceName == this._networks[networkId].interfaceName) {
|
||||
let currentAlarm = this._currentAlarms[networkId];
|
||||
if (Object.getOwnPropertyNames(currentAlarm).length !== 0) {
|
||||
this._fireAlarm(currentAlarm.alarm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "xpcom-shutdown":
|
||||
debug("Service shutdown");
|
||||
|
||||
|
@ -172,6 +212,7 @@ this.NetworkStatsService = {
|
|||
Services.obs.removeObserver(this, "profile-after-change");
|
||||
Services.obs.removeObserver(this, TOPIC_INTERFACE_REGISTERED);
|
||||
Services.obs.removeObserver(this, TOPIC_INTERFACE_UNREGISTERED);
|
||||
Services.obs.removeObserver(this, TOPIC_BANDWIDTH_CONTROL);
|
||||
|
||||
this.timer.cancel();
|
||||
this.timer = null;
|
||||
|
@ -266,6 +307,25 @@ this.NetworkStatsService = {
|
|||
});
|
||||
},
|
||||
|
||||
initAlarms: function initAlarms() {
|
||||
debug("Init usage alarms");
|
||||
let self = this;
|
||||
|
||||
for (let netId in this._networks) {
|
||||
this._currentAlarms[netId] = Object.create(null);
|
||||
|
||||
this._db.getFirstAlarm(netId, function getResult(error, result) {
|
||||
if (!error && result) {
|
||||
self._setAlarm(result, function onSet(error, success) {
|
||||
if (error == "InvalidStateError") {
|
||||
self._fireAlarm(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Function called from manager to get stats from database.
|
||||
* In order to return updated stats, first is performed a call to
|
||||
|
@ -482,8 +542,8 @@ this.NetworkStatsService = {
|
|||
}
|
||||
} else {
|
||||
// The caller is a function that has pushed new elements to the queue,
|
||||
// if isQueueRunning is false it means there is no processing currently being
|
||||
// done, so start.
|
||||
// if isQueueRunning is false it means there is no processing currently
|
||||
// being done, so start.
|
||||
if (this.isQueueRunning) {
|
||||
if(this.updateQueue.length > 1) {
|
||||
return;
|
||||
|
@ -515,7 +575,7 @@ this.NetworkStatsService = {
|
|||
let interfaceName = this._networks[aNetId].interfaceName;
|
||||
debug("Update stats for " + interfaceName);
|
||||
|
||||
// Request stats to NetworkManager, which will get stats from netd, passing
|
||||
// Request stats to NetworkService, which will get stats from netd, passing
|
||||
// 'networkStatsAvailable' as a callback.
|
||||
if (interfaceName) {
|
||||
networkService.getNetworkInterfaceStats(interfaceName,
|
||||
|
@ -706,6 +766,262 @@ this.NetworkStatsService = {
|
|||
debug(JSON.stringify(aResult));
|
||||
});
|
||||
},
|
||||
|
||||
getAlarms: function getAlarms(mm, msg) {
|
||||
let network = msg.data.network;
|
||||
let manifestURL = msg.data.manifestURL;
|
||||
|
||||
let netId = null;
|
||||
if (network) {
|
||||
netId = this.getNetworkId(network.id, network.type);
|
||||
if (!this._networks[netId]) {
|
||||
mm.sendAsyncMessage("NetworkStats:GetAlarms:Return",
|
||||
{ id: msg.id, error: "InvalidInterface", result: null });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let self = this;
|
||||
this._db.getAlarms(netId, manifestURL, function onCompleted(error, result) {
|
||||
if (error) {
|
||||
mm.sendAsyncMessage("NetworkStats:GetAlarms:Return",
|
||||
{ id: msg.id, error: error, result: result });
|
||||
return;
|
||||
}
|
||||
|
||||
let alarms = []
|
||||
// NetworkStatsManager must return the network instead of the networkId.
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
let alarm = result[i];
|
||||
alarms.push({ id: alarm.id,
|
||||
network: self._networks[alarm.networkId].network,
|
||||
threshold: alarm.threshold,
|
||||
data: alarm.data });
|
||||
}
|
||||
|
||||
mm.sendAsyncMessage("NetworkStats:GetAlarms:Return",
|
||||
{ id: msg.id, error: null, result: alarms });
|
||||
});
|
||||
},
|
||||
|
||||
removeAlarms: function removeAlarms(mm, msg) {
|
||||
let alarmId = msg.data.alarmId;
|
||||
let manifestURL = msg.data.manifestURL;
|
||||
|
||||
let self = this;
|
||||
let callback = function onRemove(error, result) {
|
||||
if (error) {
|
||||
mm.sendAsyncMessage("NetworkStats:RemoveAlarms:Return",
|
||||
{ id: msg.id, error: error, result: result });
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i in self._currentAlarms) {
|
||||
let currentAlarm = self._currentAlarms[i].alarm;
|
||||
if (currentAlarm && ((alarmId == currentAlarm.id) ||
|
||||
(alarmId == -1 && currentAlarm.manifestURL == manifestURL))) {
|
||||
|
||||
self._updateCurrentAlarm(currentAlarm.networkId);
|
||||
}
|
||||
}
|
||||
|
||||
mm.sendAsyncMessage("NetworkStats:RemoveAlarms:Return",
|
||||
{ id: msg.id, error: error, result: true });
|
||||
};
|
||||
|
||||
if (alarmId == -1) {
|
||||
this._db.removeAlarms(manifestURL, callback);
|
||||
} else {
|
||||
this._db.removeAlarm(alarmId, manifestURL, callback);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Function called from manager to set an alarm.
|
||||
*/
|
||||
setAlarm: function setAlarm(mm, msg) {
|
||||
let options = msg.data;
|
||||
let network = options.network;
|
||||
let threshold = options.threshold;
|
||||
|
||||
debug("Set alarm at " + threshold + " for " + JSON.stringify(network));
|
||||
|
||||
if (threshold < 0) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: "InvalidThresholdValue", result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
let netId = this.getNetworkId(network.id, network.type);
|
||||
if (!this._networks[netId]) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: "InvalidiConnectionType", result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
let newAlarm = {
|
||||
id: null,
|
||||
networkId: netId,
|
||||
threshold: threshold,
|
||||
absoluteThreshold: null,
|
||||
startTime: options.startTime,
|
||||
data: options.data,
|
||||
pageURL: options.pageURL,
|
||||
manifestURL: options.manifestURL
|
||||
};
|
||||
|
||||
let self = this;
|
||||
this._updateThreshold(newAlarm, function onUpdate(error, _threshold) {
|
||||
if (error) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: error, result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
newAlarm.absoluteThreshold = _threshold.absoluteThreshold;
|
||||
self._db.addAlarm(newAlarm, function addSuccessCb(error, newId) {
|
||||
if (error) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: error, result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
newAlarm.id = newId;
|
||||
self._setAlarm(newAlarm, function onSet(error, success) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: error, result: newId });
|
||||
|
||||
if (error == "InvalidStateError") {
|
||||
self._fireAlarm(newAlarm);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_setAlarm: function _setAlarm(aAlarm, aCallback) {
|
||||
let currentAlarm = this._currentAlarms[aAlarm.networkId];
|
||||
if (Object.getOwnPropertyNames(currentAlarm).length !== 0 &&
|
||||
aAlarm.absoluteThreshold > currentAlarm.alarm.absoluteThreshold) {
|
||||
aCallback(null, true);
|
||||
return;
|
||||
}
|
||||
|
||||
let self = this;
|
||||
|
||||
this._updateThreshold(aAlarm, function onUpdate(aError, aThreshold) {
|
||||
if (aError) {
|
||||
aCallback(aError, null);
|
||||
return;
|
||||
}
|
||||
|
||||
let callback = function onAlarmSet(aError) {
|
||||
if (aError) {
|
||||
debug("Set alarm error: " + aError);
|
||||
aCallback("netdError", null);
|
||||
return;
|
||||
}
|
||||
|
||||
self._currentAlarms[aAlarm.networkId].alarm = aAlarm;
|
||||
|
||||
aCallback(null, true);
|
||||
};
|
||||
|
||||
debug("Set alarm " + JSON.stringify(aAlarm));
|
||||
let interfaceName = self._networks[aAlarm.networkId].interfaceName;
|
||||
if (interfaceName) {
|
||||
networkService.setNetworkInterfaceAlarm(interfaceName,
|
||||
aThreshold.systemThreshold,
|
||||
callback);
|
||||
return;
|
||||
}
|
||||
|
||||
aCallback(null, true);
|
||||
});
|
||||
},
|
||||
|
||||
_updateThreshold: function _updateThreshold(aAlarm, aCallback) {
|
||||
let self = this;
|
||||
this.updateStats(aAlarm.networkId, function onStatsUpdated(aResult, aMessage) {
|
||||
self._db.getCurrentStats(self._networks[aAlarm.networkId].network,
|
||||
aAlarm.startTime,
|
||||
function onStatsFound(error, result) {
|
||||
if (error) {
|
||||
debug("Error getting stats for " +
|
||||
JSON.stringify(self._networks[aAlarm.networkId]) + ": " + error);
|
||||
aCallback(error, result);
|
||||
return;
|
||||
}
|
||||
|
||||
let offset = aAlarm.threshold - result.rxTotalBytes - result.txTotalBytes;
|
||||
|
||||
// Alarm set to a threshold lower than current rx/tx bytes.
|
||||
if (offset <= 0) {
|
||||
aCallback("InvalidStateError", null);
|
||||
return;
|
||||
}
|
||||
|
||||
let threshold = {
|
||||
systemThreshold: result.rxSystemBytes + result.txSystemBytes + offset,
|
||||
absoluteThreshold: result.rxTotalBytes + result.txTotalBytes + offset
|
||||
};
|
||||
|
||||
aCallback(null, threshold);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_fireAlarm: function _fireAlarm(aAlarm) {
|
||||
debug("Fire alarm");
|
||||
|
||||
let self = this;
|
||||
this._db.removeAlarm(aAlarm.id, null, function onRemove(aError, aResult){
|
||||
if (!aError && !aResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._fireSystemMessage(aAlarm);
|
||||
self._updateCurrentAlarm(aAlarm.networkId);
|
||||
});
|
||||
},
|
||||
|
||||
_updateCurrentAlarm: function _updateCurrentAlarm(aNetworkId) {
|
||||
this._currentAlarms[aNetworkId] = Object.create(null);
|
||||
|
||||
let self = this;
|
||||
this._db.getFirstAlarm(aNetworkId, function onGet(error, result){
|
||||
if (error) {
|
||||
debug("Error getting the first alarm");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
let interfaceName = self._networks[aNetworkId].interfaceName;
|
||||
networkService.setNetworkInterfaceAlarm(interfaceName, -1,
|
||||
function onComplete(){});
|
||||
return;
|
||||
}
|
||||
|
||||
self._setAlarm(result, function onSet(error, success){
|
||||
if (error == "InvalidStateError") {
|
||||
self._fireAlarm(result);
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_fireSystemMessage: function _fireSystemMessage(aAlarm) {
|
||||
debug("Fire system message: " + JSON.stringify(aAlarm));
|
||||
|
||||
let manifestURI = Services.io.newURI(aAlarm.manifestURL, null, null);
|
||||
let pageURI = Services.io.newURI(aAlarm.pageURL, null, null);
|
||||
|
||||
let alarm = { "id": aAlarm.id,
|
||||
"threshold": aAlarm.threshold,
|
||||
"data": aAlarm.data };
|
||||
messenger.sendMessage("networkstats-alarm", alarm, pageURI, manifestURI);
|
||||
}
|
||||
};
|
||||
|
||||
NetworkStatsService.init();
|
||||
|
|
|
@ -15,5 +15,6 @@ MOCHITEST_FILES = \
|
|||
test_networkstats_disabled.html \
|
||||
test_networkstats_enabled_no_perm.html \
|
||||
test_networkstats_enabled_perm.html \
|
||||
test_networkstats_alarms.html \
|
||||
$(NULL)
|
||||
endif
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for NetworkStats alarms</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
function test() {
|
||||
ok(true, "Checking if no alarms are set.");
|
||||
|
||||
req = navigator.mozNetworkStats.getAllAlarms();
|
||||
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Succeeded to get alarms.");
|
||||
ok(Array.isArray(req.result) && req.result.length == 0,
|
||||
"There are no alarms set.");
|
||||
next();
|
||||
};
|
||||
|
||||
req.onerror = function () {
|
||||
ok(false, "getAllAlarms() shouldn't fail!");
|
||||
}
|
||||
}
|
||||
|
||||
var req;
|
||||
var index = -1;
|
||||
|
||||
var wifi = {'type': 0, 'id': '0'};
|
||||
var mobile = {'type': 1, 'id': '1'};
|
||||
|
||||
var steps = [
|
||||
function () {
|
||||
ok(true, "Calling getAllAlarms() with invalid network.");
|
||||
|
||||
req = navigator.mozNetworkStats.getAllAlarms(mobile);
|
||||
|
||||
req.onsuccess = function () {
|
||||
ok(false, "getAllAlarms() shouldn't succeed!");
|
||||
};
|
||||
|
||||
req.onerror = function () {
|
||||
ok(req.error.name == "InvalidInterface", "Get InvalidInterface error");
|
||||
next();
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "Calling addAlarm() with invalid network or parameters.");
|
||||
|
||||
try {
|
||||
navigator.mozNetworkStats.addAlarm();
|
||||
} catch(ex) {
|
||||
ok(ex.result == SpecialPowers.Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS,
|
||||
"addAlarm() throws NS_ERROR_XPC_NOT_ENOUGH_ARGS exception when no parameters");
|
||||
}
|
||||
|
||||
try {
|
||||
navigator.mozNetworkStats.addAlarm(100000);
|
||||
} catch(ex) {
|
||||
ok(ex.result == SpecialPowers.Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS,
|
||||
"addAlarm() throws NS_ERROR_XPC_NOT_ENOUGH_ARGS exception when no network");
|
||||
}
|
||||
|
||||
try {
|
||||
navigator.mozNetworkStats.addAlarm(wifi);
|
||||
} catch(ex) {
|
||||
ok(ex.result == SpecialPowers.Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS,
|
||||
"addAlarm() throws NS_ERROR_XPC_NOT_ENOUGH_ARGS exception when no threshold");
|
||||
}
|
||||
|
||||
req = navigator.mozNetworkStats.addAlarm(mobile, -100000);
|
||||
|
||||
req.onsuccess = function () {
|
||||
ok(false, "addAlarm() shouldn't succeed with negative threshold.");
|
||||
};
|
||||
|
||||
req.onerror = function () {
|
||||
ok(req.error.name == "InvalidThresholdValue", "Get InvalidThresholdValue error");
|
||||
next();
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "Calling addAlarm()");
|
||||
|
||||
req = navigator.mozNetworkStats.addAlarm(wifi, 1000000);
|
||||
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Succeeded to add alarm. AlarmId: " + req.result);
|
||||
next();
|
||||
};
|
||||
req.onerror = function () {
|
||||
ok(false, "addAlarm() shouldn't fail.");
|
||||
};
|
||||
},
|
||||
function () {
|
||||
ok(true, "Calling getAllAlarms()");
|
||||
|
||||
req = navigator.mozNetworkStats.getAllAlarms(wifi);
|
||||
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 1, "Only one alarm");
|
||||
ok(req.result[0].alarmId == 1, "Get correct alarmId");
|
||||
next();
|
||||
};
|
||||
|
||||
req.onerror = function () {
|
||||
ok(false, "getAllAlarms() shouldn't fail.");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "Calling removeAlarms() to remove alarms.");
|
||||
|
||||
req = navigator.mozNetworkStats.removeAlarms();
|
||||
|
||||
req.onsuccess = function () {
|
||||
ok(req.result, "Succeeded to remove alarms.");
|
||||
next();
|
||||
};
|
||||
|
||||
req.onerror = function () {
|
||||
ok(false, "removeAlarms() shouldn't fail.");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "Checking if all alarms are removed.");
|
||||
|
||||
req = navigator.mozNetworkStats.getAllAlarms();
|
||||
|
||||
req.onsuccess = function () {
|
||||
ok(Array.isArray(req.result) && req.result.length == 0,
|
||||
"Succeeded to remove all alarms.");
|
||||
next();
|
||||
};
|
||||
|
||||
req.onerror = function () {
|
||||
ok(false, "getAllAlarms() shouldn't fail.");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "all done!\n");
|
||||
SpecialPowers.removePermission("networkstats-manage", document);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
];
|
||||
|
||||
function next() {
|
||||
index += 1;
|
||||
if (index >= steps.length) {
|
||||
ok(false, "Shouldn't get here!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
steps[index]();
|
||||
} catch(ex) {
|
||||
ok(false, "Caught exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.addPermission("networkstats-manage", true, document);
|
||||
SpecialPowers.pushPrefEnv({'set': [["dom.mozNetworkStats.enabled", true]]}, test);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -13,10 +13,8 @@
|
|||
<script type="application/javascript">
|
||||
|
||||
function test() {
|
||||
ok('mozNetworkStats' in navigator, "navigator.mozMozNetworkStats should exist");
|
||||
ok(navigator.mozNetworkStats, "navigator.mozNetworkStats returns an object");
|
||||
|
||||
netStats = navigator.mozNetworkStats;
|
||||
netStats = window.navigator.mozNetworkStats;
|
||||
ok(netStats, "mozNetworkStats exists");
|
||||
|
||||
// Test IDL attributes
|
||||
ok('sampleRate' in netStats,
|
||||
|
|
|
@ -7,12 +7,28 @@ Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
|
|||
|
||||
const netStatsDb = new NetworkStatsDB();
|
||||
|
||||
function clearWholeDB(callback) {
|
||||
netStatsDb.dbNewTxn("readwrite", function(aTxn, aStore) {
|
||||
aStore.delete();
|
||||
function clearStore(store, callback) {
|
||||
netStatsDb.dbNewTxn(store, "readwrite", function(aTxn, aStore) {
|
||||
aStore.openCursor().onsuccess = function (event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor){
|
||||
cursor.delete();
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
}, callback);
|
||||
}
|
||||
|
||||
add_test(function prepareDatabase() {
|
||||
// Clear whole database to avoid starting tests with unknown state
|
||||
// due to the previous tests.
|
||||
clearStore('net_stats', function() {
|
||||
clearStore('net_alarm', function() {
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function filterTimestamp(date) {
|
||||
var sampleRate = netStatsDb.sampleRate;
|
||||
var offset = date.getTimezoneOffset() * 60 * 1000;
|
||||
|
@ -122,15 +138,17 @@ add_test(function test_clear_interface() {
|
|||
add_test(function test_internalSaveStats_singleSample() {
|
||||
var networks = getNetworks();
|
||||
|
||||
var stats = { appId: 0,
|
||||
network: [networks[0].id, networks[0].type],
|
||||
timestamp: Date.now(),
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
rxTotalBytes: 1234,
|
||||
txTotalBytes: 1234 };
|
||||
var stats = { appId: 0,
|
||||
network: [networks[0].id, networks[0].type],
|
||||
timestamp: Date.now(),
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
rxSystemBytes: 1234,
|
||||
txSystemBytes: 1234,
|
||||
rxTotalBytes: 1234,
|
||||
txTotalBytes: 1234 };
|
||||
|
||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||
netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
|
||||
netStatsDb._saveStats(txn, store, stats);
|
||||
}, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
@ -143,6 +161,8 @@ add_test(function test_internalSaveStats_singleSample() {
|
|||
do_check_eq(result[0].timestamp, stats.timestamp);
|
||||
do_check_eq(result[0].rxBytes, stats.rxBytes);
|
||||
do_check_eq(result[0].txBytes, stats.txBytes);
|
||||
do_check_eq(result[0].rxSystemBytes, stats.rxSystemBytes);
|
||||
do_check_eq(result[0].txSystemBytes, stats.txSystemBytes);
|
||||
do_check_eq(result[0].rxTotalBytes, stats.rxTotalBytes);
|
||||
do_check_eq(result[0].txTotalBytes, stats.txTotalBytes);
|
||||
run_next_test();
|
||||
|
@ -161,16 +181,18 @@ add_test(function test_internalSaveStats_arraySamples() {
|
|||
var samples = 2;
|
||||
var stats = [];
|
||||
for (var i = 0; i < samples; i++) {
|
||||
stats.push({ appId: 0,
|
||||
network: network,
|
||||
timestamp: Date.now() + (10 * i),
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
rxTotalBytes: 1234,
|
||||
txTotalBytes: 1234 });
|
||||
stats.push({ appId: 0,
|
||||
network: network,
|
||||
timestamp: Date.now() + (10 * i),
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
rxSystemBytes: 1234,
|
||||
txSystemBytes: 1234,
|
||||
rxTotalBytes: 1234,
|
||||
txTotalBytes: 1234 });
|
||||
}
|
||||
|
||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||
netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
|
||||
netStatsDb._saveStats(txn, store, stats);
|
||||
}, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
@ -182,7 +204,6 @@ add_test(function test_internalSaveStats_arraySamples() {
|
|||
// an empty sample to keep totalBytes synchronized with netd counters
|
||||
result.shift();
|
||||
do_check_eq(result.length, samples);
|
||||
|
||||
var success = true;
|
||||
for (var i = 1; i < samples; i++) {
|
||||
if (result[i].appId != stats[i].appId ||
|
||||
|
@ -190,6 +211,8 @@ add_test(function test_internalSaveStats_arraySamples() {
|
|||
result[i].timestamp != stats[i].timestamp ||
|
||||
result[i].rxBytes != stats[i].rxBytes ||
|
||||
result[i].txBytes != stats[i].txBytes ||
|
||||
result[i].rxSystemBytes != stats[i].rxSystemBytes ||
|
||||
result[i].txSystemBytes != stats[i].txSystemBytes ||
|
||||
result[i].rxTotalBytes != stats[i].rxTotalBytes ||
|
||||
result[i].txTotalBytes != stats[i].txTotalBytes) {
|
||||
success = false;
|
||||
|
@ -213,18 +236,20 @@ add_test(function test_internalRemoveOldStats() {
|
|||
var samples = 10;
|
||||
var stats = [];
|
||||
for (var i = 0; i < samples - 1; i++) {
|
||||
stats.push({ appId: 0,
|
||||
network: network, timestamp: Date.now() + (10 * i),
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234 });
|
||||
stats.push({ appId: 0,
|
||||
network: network, timestamp: Date.now() + (10 * i),
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxSystemBytes: 1234, txSystemBytes: 1234,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234 });
|
||||
}
|
||||
|
||||
stats.push({ appId: 0,
|
||||
network: network, timestamp: Date.now() + (10 * samples),
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234 });
|
||||
stats.push({ appId: 0,
|
||||
network: network, timestamp: Date.now() + (10 * samples),
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxSystemBytes: 1234, txSystemBytes: 1234,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234 });
|
||||
|
||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||
netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
|
||||
netStatsDb._saveStats(txn, store, stats);
|
||||
var date = stats[stats.length - 1].timestamp
|
||||
+ (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1;
|
||||
|
@ -245,10 +270,10 @@ add_test(function test_internalRemoveOldStats() {
|
|||
function processSamplesDiff(networks, lastStat, newStat, callback) {
|
||||
netStatsDb.clearStats(networks, function (error, result){
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||
netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
|
||||
netStatsDb._saveStats(txn, store, lastStat);
|
||||
}, function(error, result) {
|
||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||
netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
|
||||
let request = store.index("network").openCursor(newStat.network, "prev");
|
||||
request.onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
|
@ -273,25 +298,29 @@ add_test(function test_processSamplesDiffSameSample() {
|
|||
var sampleRate = netStatsDb.sampleRate;
|
||||
var date = filterTimestamp(new Date());
|
||||
|
||||
var lastStat = { appId: 0,
|
||||
network: network, timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234 };
|
||||
var lastStat = { appId: 0,
|
||||
network: network, timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxSystemBytes: 1234, txSystemBytes: 1234,
|
||||
rxTotalBytes: 2234, txTotalBytes: 2234 };
|
||||
|
||||
var newStat = { appId: 0,
|
||||
network: network, timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 2234, txTotalBytes: 2234 };
|
||||
var newStat = { appId: 0,
|
||||
network: network, timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxSystemBytes: 2234, txSystemBytes: 2234,
|
||||
rxTotalBytes: 2234, txTotalBytes: 2234 };
|
||||
|
||||
processSamplesDiff(networks, lastStat, newStat, function(result) {
|
||||
do_check_eq(result.length, 1);
|
||||
do_check_eq(result[0].appId, newStat.appId);
|
||||
do_check_true(compareNetworks(result[0].network, newStat.network));
|
||||
do_check_eq(result[0].timestamp, newStat.timestamp);
|
||||
do_check_eq(result[0].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
|
||||
do_check_eq(result[0].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
|
||||
do_check_eq(result[0].rxTotalBytes, newStat.rxTotalBytes);
|
||||
do_check_eq(result[0].txTotalBytes, newStat.txTotalBytes);
|
||||
do_check_eq(result[0].rxBytes, newStat.rxSystemBytes - lastStat.rxSystemBytes);
|
||||
do_check_eq(result[0].txBytes, newStat.txSystemBytes - lastStat.txSystemBytes);
|
||||
do_check_eq(result[0].rxTotalBytes, lastStat.rxTotalBytes + newStat.rxSystemBytes - lastStat.rxSystemBytes);
|
||||
do_check_eq(result[0].txTotalBytes, lastStat.txTotalBytes + newStat.txSystemBytes - lastStat.txSystemBytes);
|
||||
do_check_eq(result[0].rxSystemBytes, newStat.rxSystemBytes);
|
||||
do_check_eq(result[0].txSystemBytes, newStat.txSystemBytes);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
@ -303,25 +332,29 @@ add_test(function test_processSamplesDiffNextSample() {
|
|||
var sampleRate = netStatsDb.sampleRate;
|
||||
var date = filterTimestamp(new Date());
|
||||
|
||||
var lastStat = { appId: 0,
|
||||
network: network, timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234 };
|
||||
var lastStat = { appId: 0,
|
||||
network: network, timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxSystemBytes: 1234, txSystemBytes: 1234,
|
||||
rxTotalBytes: 2234, txTotalBytes: 2234 };
|
||||
|
||||
var newStat = { appId: 0,
|
||||
network: network, timestamp: date + sampleRate,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 500, txTotalBytes: 500 };
|
||||
var newStat = { appId: 0,
|
||||
network: network, timestamp: date + sampleRate,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxSystemBytes: 1734, txSystemBytes: 1734,
|
||||
rxTotalBytes: 0, txTotalBytes: 0 };
|
||||
|
||||
processSamplesDiff(networks, lastStat, newStat, function(result) {
|
||||
do_check_eq(result.length, 2);
|
||||
do_check_eq(result[1].appId, newStat.appId);
|
||||
do_check_true(compareNetworks(result[1].network, newStat.network));
|
||||
do_check_eq(result[1].timestamp, newStat.timestamp);
|
||||
do_check_eq(result[1].rxBytes, newStat.rxTotalBytes);
|
||||
do_check_eq(result[1].txBytes, newStat.txTotalBytes);
|
||||
do_check_eq(result[1].rxTotalBytes, newStat.rxTotalBytes);
|
||||
do_check_eq(result[1].txTotalBytes, newStat.txTotalBytes);
|
||||
do_check_eq(result[1].rxBytes, newStat.rxSystemBytes - lastStat.rxSystemBytes);
|
||||
do_check_eq(result[1].txBytes, newStat.txSystemBytes - lastStat.txSystemBytes);
|
||||
do_check_eq(result[1].rxSystemBytes, newStat.rxSystemBytes);
|
||||
do_check_eq(result[1].txSystemBytes, newStat.txSystemBytes);
|
||||
do_check_eq(result[1].rxTotalBytes, lastStat.rxTotalBytes + newStat.rxSystemBytes - lastStat.rxSystemBytes);
|
||||
do_check_eq(result[1].txTotalBytes, lastStat.txTotalBytes + newStat.txSystemBytes - lastStat.txSystemBytes);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
@ -332,15 +365,17 @@ add_test(function test_processSamplesDiffSamplesLost() {
|
|||
var samples = 5;
|
||||
var sampleRate = netStatsDb.sampleRate;
|
||||
var date = filterTimestamp(new Date());
|
||||
var lastStat = { appId: 0,
|
||||
network: network, timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234 };
|
||||
var lastStat = { appId: 0,
|
||||
network: network, timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxSystemBytes: 1234, txSystemBytes: 1234,
|
||||
rxTotalBytes: 2234, txTotalBytes: 2234};
|
||||
|
||||
var newStat = { appId: 0,
|
||||
network: network, timestamp: date + (sampleRate * samples),
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 2234, txTotalBytes: 2234 };
|
||||
var newStat = { appId: 0,
|
||||
network: network, timestamp: date + (sampleRate * samples),
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxSystemBytes: 2234, txSystemBytes: 2234,
|
||||
rxTotalBytes: 0, txTotalBytes: 0 };
|
||||
|
||||
processSamplesDiff(networks, lastStat, newStat, function(result) {
|
||||
do_check_eq(result.length, samples + 1);
|
||||
|
@ -349,8 +384,10 @@ add_test(function test_processSamplesDiffSamplesLost() {
|
|||
do_check_eq(result[samples].timestamp, newStat.timestamp);
|
||||
do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
|
||||
do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
|
||||
do_check_eq(result[samples].rxTotalBytes, newStat.rxTotalBytes);
|
||||
do_check_eq(result[samples].txTotalBytes, newStat.txTotalBytes);
|
||||
do_check_eq(result[samples].rxSystemBytes, newStat.rxSystemBytes);
|
||||
do_check_eq(result[samples].txSystemBytes, newStat.txSystemBytes);
|
||||
do_check_eq(result[samples].rxTotalBytes, lastStat.rxTotalBytes + newStat.rxSystemBytes - lastStat.rxSystemBytes);
|
||||
do_check_eq(result[samples].txTotalBytes, lastStat.txTotalBytes + newStat.txSystemBytes - lastStat.txSystemBytes);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
@ -366,8 +403,7 @@ add_test(function test_saveStats() {
|
|||
rxBytes: 2234,
|
||||
txBytes: 2234};
|
||||
|
||||
netStatsDb.clearStats(networks, function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
clearStore('net_stats', function() {
|
||||
netStatsDb.saveStats(stats, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.logAllRecords(function(error, result) {
|
||||
|
@ -377,8 +413,10 @@ add_test(function test_saveStats() {
|
|||
do_check_true(compareNetworks(result[0].network, network));
|
||||
let timestamp = filterTimestamp(stats.date);
|
||||
do_check_eq(result[0].timestamp, timestamp);
|
||||
do_check_eq(result[0].rxBytes, 0);
|
||||
do_check_eq(result[0].txBytes, 0);
|
||||
do_check_eq(result[0].rxBytes, stats.rxBytes);
|
||||
do_check_eq(result[0].txBytes, stats.txBytes);
|
||||
do_check_eq(result[0].rxSystemBytes, stats.rxBytes);
|
||||
do_check_eq(result[0].txSystemBytes, stats.txBytes);
|
||||
do_check_eq(result[0].rxTotalBytes, stats.rxBytes);
|
||||
do_check_eq(result[0].txTotalBytes, stats.txBytes);
|
||||
run_next_test();
|
||||
|
@ -416,6 +454,8 @@ add_test(function test_saveAppStats() {
|
|||
do_check_eq(result[1].timestamp, timestamp);
|
||||
do_check_eq(result[1].rxBytes, stats.rxBytes);
|
||||
do_check_eq(result[1].txBytes, stats.txBytes);
|
||||
do_check_eq(result[1].rxSystemBytes, 0);
|
||||
do_check_eq(result[1].txSystemBytes, 0);
|
||||
do_check_eq(result[1].rxTotalBytes, 0);
|
||||
do_check_eq(result[1].txTotalBytes, 0);
|
||||
run_next_test();
|
||||
|
@ -427,7 +467,7 @@ add_test(function test_saveAppStats() {
|
|||
function prepareFind(network, stats, callback) {
|
||||
netStatsDb.clearStats(network, function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||
netStatsDb.dbNewTxn("net_stats", "readwrite", function(txn, store) {
|
||||
netStatsDb._saveStats(txn, store, stats);
|
||||
}, function(error, result) {
|
||||
callback(error, result);
|
||||
|
@ -449,15 +489,18 @@ add_test(function test_find () {
|
|||
start = new Date(start - sampleRate);
|
||||
var stats = [];
|
||||
for (var i = 0; i < samples; i++) {
|
||||
stats.push({ appId: appId,
|
||||
network: networkWifi, timestamp: saveDate + (sampleRate * i),
|
||||
rxBytes: 0, txBytes: 10,
|
||||
rxTotalBytes: 0, txTotalBytes: 0 });
|
||||
stats.push({ appId: appId,
|
||||
network: networkWifi, timestamp: saveDate + (sampleRate * i),
|
||||
rxBytes: 0, txBytes: 10,
|
||||
rxSystemBytes: 0, txSystemBytes: 0,
|
||||
rxTotalBytes: 0, txTotalBytes: 0});
|
||||
|
||||
stats.push({ appId: appId,
|
||||
network: networkMobile, timestamp: saveDate + (sampleRate * i),
|
||||
rxBytes: 0, txBytes: 10,
|
||||
rxTotalBytes: 0, txTotalBytes: 0 });
|
||||
|
||||
stats.push({ appId: appId,
|
||||
network: networkMobile, timestamp: saveDate + (sampleRate * i),
|
||||
rxBytes: 0, txBytes: 10,
|
||||
rxSystemBytes: 0, txSystemBytes: 0,
|
||||
rxTotalBytes: 0, txTotalBytes: 0});
|
||||
}
|
||||
|
||||
prepareFind(networks[0], stats, function(error, result) {
|
||||
|
@ -554,21 +597,22 @@ add_test(function test_saveMultipleAppStats () {
|
|||
|
||||
if (index == keys.length - 1) {
|
||||
netStatsDb.logAllRecords(function(error, result) {
|
||||
// Again, result has two samples more than expected samples because
|
||||
// clear inserts one empty sample for each network to keep totalBytes
|
||||
// synchronized with netd counters. so the first two samples have to
|
||||
// be discarted.
|
||||
result.shift();
|
||||
result.shift();
|
||||
// Again, result has two samples more than expected samples because
|
||||
// clear inserts one empty sample for each network to keep totalBytes
|
||||
// synchronized with netd counters. so the first two samples have to
|
||||
// be discarted.
|
||||
result.shift();
|
||||
result.shift();
|
||||
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 4);
|
||||
do_check_eq(result[0].appId, 1);
|
||||
do_check_true(compareNetworks(result[0].network,[networkWifi.id, networkWifi.type]));
|
||||
do_check_eq(result[0].rxBytes, 0);
|
||||
do_check_eq(result[0].txBytes, 10);
|
||||
run_next_test();
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 4);
|
||||
do_check_eq(result[0].appId, 1);
|
||||
do_check_true(compareNetworks(result[0].network, [networkWifi.id, networkWifi.type]));
|
||||
do_check_eq(result[0].rxBytes, 0);
|
||||
do_check_eq(result[0].txBytes, 10);
|
||||
run_next_test();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
|
@ -577,12 +621,188 @@ add_test(function test_saveMultipleAppStats () {
|
|||
});
|
||||
});
|
||||
|
||||
var networkWifi = '00';
|
||||
var networkMobile = '11';
|
||||
|
||||
var examplePageURL = "http://example.com/index.html";
|
||||
var exampleManifestURL = "http://example.com/manifest.webapp";
|
||||
|
||||
var testPageURL = "http://test.com/index.html";
|
||||
var testManifestURL = "http://test.com/manifest.webapp";
|
||||
|
||||
var alarms = [{ id: null,
|
||||
networkId: networkWifi,
|
||||
threshold: 10000,
|
||||
data: {foo: "something"},
|
||||
pageURL: examplePageURL,
|
||||
manifestURL: exampleManifestURL },
|
||||
{ id: null,
|
||||
networkId: networkWifi,
|
||||
threshold: 1000,
|
||||
data: {foo: "else"},
|
||||
pageURL: examplePageURL,
|
||||
manifestURL: exampleManifestURL },
|
||||
{ id: null,
|
||||
networkId: networkMobile,
|
||||
threshold: 100,
|
||||
data: {foo: "to"},
|
||||
pageURL: examplePageURL,
|
||||
manifestURL: exampleManifestURL },
|
||||
{ id: null,
|
||||
networkId: networkMobile,
|
||||
threshold: 10,
|
||||
data: {foo: "test"},
|
||||
pageURL: testPageURL,
|
||||
manifestURL: testManifestURL }];
|
||||
|
||||
var alarmsDbId = 1;
|
||||
|
||||
add_test(function test_addAlarm() {
|
||||
// Add alarms[0] -> DB: [ alarms[0] (id: 1) ]
|
||||
// Check the insertion is OK.
|
||||
netStatsDb.addAlarm(alarms[0], function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
alarmsDbId = result;
|
||||
netStatsDb.getAlarms(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, exampleManifestURL, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 1);
|
||||
do_check_eq(result[0].id, alarmsDbId);
|
||||
do_check_eq(result[0].networkId, alarms[0].networkId);
|
||||
do_check_eq(result[0].threshold, alarms[0].threshold);
|
||||
do_check_eq(result[0].data.foo, alarms[0].data.foo);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_getFirstAlarm() {
|
||||
// Add alarms[1] -> DB: [ alarms[0] (id: 1), alarms[1] (id: 2) ]
|
||||
// Check first alarm is alarms[1] because threshold is lower.
|
||||
alarmsDbId += 1;
|
||||
netStatsDb.addAlarm(alarms[1], function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result, alarmsDbId);
|
||||
netStatsDb.getFirstAlarm(networkWifi, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.id, alarmsDbId);
|
||||
do_check_eq(result.networkId, alarms[1].networkId);
|
||||
do_check_eq(result.threshold, alarms[1].threshold);
|
||||
do_check_eq(result.data.foo, alarms[1].data.foo);
|
||||
do_check_eq(result.pageURL, alarms[1].pageURL);
|
||||
do_check_eq(result.manifestURL, alarms[1].manifestURL);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_removeAlarm() {
|
||||
// Remove alarms[1] (id: 2) -> DB: [ alarms[0] (id: 1) ]
|
||||
// Check get first return alarms[0].
|
||||
netStatsDb.removeAlarm(alarmsDbId, alarms[0].manifestURL, function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.getFirstAlarm(networkWifi, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.id, alarmsDbId - 1);
|
||||
do_check_eq(result.networkId, alarms[0].networkId);
|
||||
do_check_eq(result.threshold, alarms[0].threshold);
|
||||
do_check_eq(result.data.foo, alarms[0].data.foo);
|
||||
do_check_eq(result.pageURL, alarms[0].pageURL);
|
||||
do_check_eq(result.manifestURL, alarms[0].manifestURL);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_removeAppAlarm() {
|
||||
// Remove alarms[0] (id: 1) -> DB: [ ]
|
||||
netStatsDb.removeAlarm(alarmsDbId - 1, alarms[0].manifestURL, function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.getAlarms(networkWifi, exampleManifestURL, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 0);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_getAlarms() {
|
||||
// Add all alarms -> DB: [ alarms[0] (id: 3),
|
||||
// alarms[1] (id: 4),
|
||||
// alarms[2] (id: 5),
|
||||
// alarms[3] (id: 6) ]
|
||||
// Check that getAlarms for wifi returns 2 alarms.
|
||||
// Check that getAlarms for all connections returns 3 alarms.
|
||||
|
||||
var callback = function () {
|
||||
netStatsDb.getAlarms(networkWifi, exampleManifestURL, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 2);
|
||||
netStatsDb.getAlarms(null, exampleManifestURL, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 3);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var index = 0;
|
||||
|
||||
var addFunction = function () {
|
||||
alarmsDbId += 1;
|
||||
netStatsDb.addAlarm(alarms[index], function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
index += 1;
|
||||
do_check_eq(result, alarmsDbId);
|
||||
if (index >= alarms.length) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
addFunction();
|
||||
});
|
||||
};
|
||||
|
||||
addFunction();
|
||||
});
|
||||
|
||||
add_test(function test_removeAppAllAlarms() {
|
||||
// Remove all alarms for exampleManifestURL -> DB: [ alarms[3] (id: 6) ]
|
||||
netStatsDb.removeAlarms(exampleManifestURL, function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.getAlarms(null, exampleManifestURL, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 0);
|
||||
netStatsDb.getAlarms(null, testManifestURL, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 1);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_updateAlarm() {
|
||||
// Update alarms[3] (id: 6) -> DB: [ alarms[3]* (id: 6) ]
|
||||
|
||||
var updatedAlarm = alarms[1];
|
||||
updatedAlarm.id = alarmsDbId;
|
||||
updatedAlarm.threshold = 10;
|
||||
|
||||
netStatsDb.updateAlarm(updatedAlarm, function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.getFirstAlarm(networkWifi, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.id, updatedAlarm.id);
|
||||
do_check_eq(result.networkId, updatedAlarm.networkId);
|
||||
do_check_eq(result.threshold, updatedAlarm.threshold);
|
||||
do_check_eq(result.data.foo, updatedAlarm.data.foo);
|
||||
do_check_eq(result.pageURL, updatedAlarm.pageURL);
|
||||
do_check_eq(result.manifestURL, updatedAlarm.manifestURL);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
|
||||
// Clear whole database to avoid start tests with unknown state
|
||||
// due to previous tests.
|
||||
clearWholeDB(function(){
|
||||
run_next_test();
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче