mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound, a=merge CLOSED TREE
This commit is contained in:
commit
433355ab1d
@ -1378,7 +1378,7 @@ pref("devtools.toolbox.splitconsoleHeight", 100);
|
||||
|
||||
// Toolbox Button preferences
|
||||
pref("devtools.command-button-pick.enabled", true);
|
||||
pref("devtools.command-button-frames.enabled", false);
|
||||
pref("devtools.command-button-frames.enabled", true);
|
||||
pref("devtools.command-button-splitconsole.enabled", true);
|
||||
pref("devtools.command-button-paintflashing.enabled", false);
|
||||
pref("devtools.command-button-tilt.enabled", false);
|
||||
|
@ -55,6 +55,35 @@ XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Pocket",
|
||||
"resource:///modules/Pocket.jsm");
|
||||
|
||||
// Can't use XPCOMUtils for these because the scripts try to define the variables
|
||||
// on window, and so the defineProperty inside defineLazyGetter fails.
|
||||
Object.defineProperty(window, "pktApi", {
|
||||
get: function() {
|
||||
// Avoid this getter running again:
|
||||
delete window.pktApi;
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/pocket/pktApi.js", window);
|
||||
return window.pktApi;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
function pktUIGetter(prop) {
|
||||
return {
|
||||
get: function() {
|
||||
// Avoid either of these getters running again:
|
||||
delete window.pktUI;
|
||||
delete window.pktUIMessaging;
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/pocket/main.js", window);
|
||||
return window[prop];
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
};
|
||||
}
|
||||
Object.defineProperty(window, "pktUI", pktUIGetter("pktUI"));
|
||||
Object.defineProperty(window, "pktUIMessaging", pktUIGetter("pktUIMessaging"));
|
||||
|
||||
const nsIWebNavigation = Ci.nsIWebNavigation;
|
||||
|
||||
var gLastBrowserCharset = null;
|
||||
@ -4171,7 +4200,6 @@ var XULBrowserWindow = {
|
||||
BookmarkingUI.onLocationChange();
|
||||
SocialUI.updateState(location);
|
||||
UITour.onLocationChange(location);
|
||||
Pocket.onLocationChange(browser, aLocationURI);
|
||||
}
|
||||
|
||||
// Utility functions for disabling find
|
||||
|
@ -1307,7 +1307,4 @@
|
||||
# starting with an empty iframe here in browser.xul from a Ts standpoint.
|
||||
</deck>
|
||||
|
||||
<script type="application/javascript" src="chrome://browser/content/pocket/pktApi.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/pocket/main.js"/>
|
||||
|
||||
</window>
|
||||
|
@ -77,8 +77,8 @@ add_task(function* checkAllTheJS() {
|
||||
" browser/base/content/test/general/browser_parsable_script.js");
|
||||
return;
|
||||
}
|
||||
// Request a 10 minutes timeout (30 seconds * 20) for debug builds.
|
||||
requestLongerTimeout(20);
|
||||
// Request a 15 minutes timeout (30 seconds * 30) for debug builds.
|
||||
requestLongerTimeout(30);
|
||||
}
|
||||
|
||||
let uris;
|
||||
|
@ -2176,9 +2176,9 @@ let CustomizableUIInternal = {
|
||||
// current placement settings.
|
||||
|
||||
// This allows a widget to be both built-in by default but also able to be
|
||||
// destroyed based on criteria that may not be available when the widget is
|
||||
// created -- for example, because some other feature in the browser
|
||||
// supersedes the widget.
|
||||
// destroyed and removed from the area based on criteria that may not be
|
||||
// available when the widget is created -- for example, because some other
|
||||
// feature in the browser supersedes the widget.
|
||||
let conditionalDestroyPromise = aData.conditionalDestroyPromise || null;
|
||||
delete aData.conditionalDestroyPromise;
|
||||
|
||||
@ -2195,6 +2195,7 @@ let CustomizableUIInternal = {
|
||||
conditionalDestroyPromise.then(shouldDestroy => {
|
||||
if (shouldDestroy) {
|
||||
this.destroyWidget(widget.id);
|
||||
this.removeWidgetFromArea(widget.id);
|
||||
}
|
||||
}, err => {
|
||||
Cu.reportError(err);
|
||||
|
@ -1106,8 +1106,13 @@ if (Services.prefs.getBoolPref("browser.pocket.enabled")) {
|
||||
viewId: "PanelUI-pocketView",
|
||||
label: label,
|
||||
tooltiptext: tooltiptext,
|
||||
onViewShowing: Pocket.onPanelViewShowing,
|
||||
onViewHiding: Pocket.onPanelViewHiding,
|
||||
// Use forwarding functions here to avoid loading Pocket.jsm on startup:
|
||||
onViewShowing: function() {
|
||||
return Pocket.onPanelViewShowing.apply(this, arguments);
|
||||
},
|
||||
onViewHiding: function() {
|
||||
return Pocket.onPanelViewHiding.apply(this, arguments);
|
||||
},
|
||||
|
||||
// If the user has the "classic" Pocket add-on installed, use that instead
|
||||
// and destroy the widget.
|
||||
|
@ -6,8 +6,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
||||
"resource:///modules/CustomizableUI.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ScrollbarSampler",
|
||||
"resource:///modules/ScrollbarSampler.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Pocket",
|
||||
"resource:///modules/Pocket.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
|
||||
|
@ -865,6 +865,12 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.OT_audio-only.OT_publisher .OT_video-element,
|
||||
.OT_audio-only.OT_subscriber .OT_video-element {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.OT_video-disabled-indicator {
|
||||
opacity: 1;
|
||||
border: none;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -81,9 +81,7 @@ LoopRoomsCache.prototype = {
|
||||
try {
|
||||
return (this._cache = yield CommonUtils.readJSON(this.path));
|
||||
} catch(error) {
|
||||
// This is really complex due to OSFile's error handling, see bug 1160109.
|
||||
if ((OS.Constants.libc && error.unixErrno != OS.Constants.libc.ENOENT) ||
|
||||
(OS.Constants.Win && error.winLastError != OS.Constants.Win.ERROR_FILE_NOT_FOUND)) {
|
||||
if (!error.becauseNoSuchFile) {
|
||||
MozLoopService.log.debug("Error reading the cache:", error);
|
||||
}
|
||||
return (this._cache = {});
|
||||
|
@ -66,30 +66,6 @@ let Pocket = {
|
||||
window.pktUI.pocketPanelDidHide(event);
|
||||
},
|
||||
|
||||
// Called on tab/urlbar/location changes and after customization. Update
|
||||
// anything that is tab specific.
|
||||
onLocationChange(browser, locationURI) {
|
||||
if (!locationURI) {
|
||||
return;
|
||||
}
|
||||
let widget = CustomizableUI.getWidget("pocket-button");
|
||||
for (let instance of widget.instances) {
|
||||
let node = instance.node;
|
||||
if (!node ||
|
||||
node.ownerDocument != browser.ownerDocument) {
|
||||
continue;
|
||||
}
|
||||
if (node) {
|
||||
let win = browser.ownerDocument.defaultView;
|
||||
node.disabled = win.pktApi.isUserLoggedIn() &&
|
||||
!locationURI.schemeIs("http") &&
|
||||
!locationURI.schemeIs("https") &&
|
||||
!(locationURI.schemeIs("about") &&
|
||||
locationURI.spec.toLowerCase().startsWith("about:reader?url="));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_urlToSave: null,
|
||||
_titleToSave: null,
|
||||
savePage(browser, url, title) {
|
||||
|
@ -293,7 +293,11 @@ InternalScheduler.prototype = {
|
||||
// the last success.
|
||||
prefs.set("lastSync", new Date().toString());
|
||||
this.state = this.STATE_OK;
|
||||
this._logManager.resetFileLog(this._logManager.REASON_SUCCESS);
|
||||
this._logManager.resetFileLog().then(result => {
|
||||
if (result == this._logManager.ERROR_LOG_WRITTEN) {
|
||||
Cu.reportError("Reading List sync encountered an error - see about:sync-log for the log file.");
|
||||
}
|
||||
});
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
|
||||
this._currentErrorBackoff = 0; // error retry interval is reset on success.
|
||||
return intervals.schedule;
|
||||
@ -307,7 +311,11 @@ InternalScheduler.prototype = {
|
||||
this._currentErrorBackoff = 0; // error retry interval is reset on success.
|
||||
this.log.info("Can't sync due to FxA account state " + err.message);
|
||||
this.state = this.STATE_OK;
|
||||
this._logManager.resetFileLog(this._logManager.REASON_SUCCESS);
|
||||
this._logManager.resetFileLog().then(result => {
|
||||
if (result == this._logManager.ERROR_LOG_WRITTEN) {
|
||||
Cu.reportError("Reading List sync encountered an error - see about:sync-log for the log file.");
|
||||
}
|
||||
});
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
|
||||
// it's unfortunate that we are probably going to hit this every
|
||||
// 2 hours, but it should be invisible to the user.
|
||||
@ -317,7 +325,7 @@ InternalScheduler.prototype = {
|
||||
this.STATE_ERROR_AUTHENTICATION : this.STATE_ERROR_OTHER;
|
||||
this.log.error("Sync failed, now in state '${state}': ${err}",
|
||||
{state: this.state, err});
|
||||
this._logManager.resetFileLog(this._logManager.REASON_ERROR);
|
||||
this._logManager.resetFileLog();
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
|
||||
// We back-off on error retries until it hits our normally scheduled interval.
|
||||
this._currentErrorBackoff = this._currentErrorBackoff == 0 ? intervals.retry :
|
||||
|
@ -3,15 +3,7 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
let temp = {}
|
||||
Cu.import("resource:///modules/devtools/gDevTools.jsm", temp);
|
||||
let DevTools = temp.DevTools;
|
||||
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", temp);
|
||||
let LayoutHelpers = temp.LayoutHelpers;
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/Loader.jsm", temp);
|
||||
let devtools = temp.devtools;
|
||||
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
|
||||
let Toolbox = devtools.Toolbox;
|
||||
|
||||
@ -40,19 +32,27 @@ function test() {
|
||||
let json = JSON.parse(event.data);
|
||||
if (json.name == "toolbox-close") {
|
||||
ok("Got the `toolbox-close` message");
|
||||
window.removeEventListener("message", onMessage);
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
function testCustomHost(toolbox) {
|
||||
function testCustomHost(t) {
|
||||
toolbox = t;
|
||||
is(toolbox.doc.defaultView.top, window, "Toolbox is included in browser.xul");
|
||||
is(toolbox.doc, iframe.contentDocument, "Toolbox is in the custom iframe");
|
||||
executeSoon(() => gBrowser.removeCurrentTab());
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
window.removeEventListener("message", onMessage);
|
||||
iframe.remove();
|
||||
finish();
|
||||
|
||||
// Even if we received "toolbox-close", the toolbox may still be destroying
|
||||
// toolbox.destroy() returns a singleton promise that ensures
|
||||
// everything is cleaned up before proceeding.
|
||||
toolbox.destroy().then(() => {
|
||||
toolbox = iframe = target = tab = null;
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -37,11 +37,15 @@ function start() {
|
||||
|
||||
function testSelectTool(aToolbox) {
|
||||
toolbox = aToolbox;
|
||||
toolbox.once("options-selected", testRegisterFails);
|
||||
toolbox.once("options-selected", () => {
|
||||
testRegisterFails().then(testRegisterInstallingWorker);
|
||||
});
|
||||
toolbox.selectTool("options");
|
||||
}
|
||||
|
||||
function testRegisterFails() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let output = doc.getElementById("output");
|
||||
let button = doc.getElementById("button");
|
||||
|
||||
@ -50,7 +54,7 @@ function testRegisterFails() {
|
||||
is(output.textContent,
|
||||
"SecurityError",
|
||||
"SecurityError expected");
|
||||
testRegisterInstallingWorker();
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
if (output.textContent !== "No output") {
|
||||
@ -61,6 +65,8 @@ function testRegisterFails() {
|
||||
button.removeEventListener('click', onClick);
|
||||
doTheCheck();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testRegisterInstallingWorker() {
|
||||
@ -73,7 +79,7 @@ function testRegisterInstallingWorker() {
|
||||
is(output.textContent,
|
||||
"Installing worker/",
|
||||
"Installing worker expected");
|
||||
toggleServiceWorkersTestingCheckbox().then(finishUp);
|
||||
testRegisterFailsWhenToolboxCloses();
|
||||
}
|
||||
|
||||
if (output.textContent !== "No output") {
|
||||
@ -87,6 +93,30 @@ function testRegisterInstallingWorker() {
|
||||
});
|
||||
}
|
||||
|
||||
// Workers should be turned back off when we closes the toolbox
|
||||
function testRegisterFailsWhenToolboxCloses() {
|
||||
info("Testing it disable worker when closing the toolbox");
|
||||
toolbox.destroy()
|
||||
.then(reload)
|
||||
.then(testRegisterFails)
|
||||
.then(finishUp);
|
||||
}
|
||||
|
||||
function reload() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||
doc = content.document;
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
let mm = getFrameScript();
|
||||
mm.sendAsyncMessage("devtools:test:reload");
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function toggleServiceWorkersTestingCheckbox() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
@ -101,24 +131,13 @@ function toggleServiceWorkersTestingCheckbox() {
|
||||
info("Checking checkbox to enable service workers testing");
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||
doc = content.document;
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
cbx.click();
|
||||
|
||||
let mm = getFrameScript();
|
||||
mm.sendAsyncMessage("devtools:test:reload");
|
||||
|
||||
return deferred.promise;
|
||||
return reload();
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
toolbox.destroy().then(function() {
|
||||
gBrowser.removeCurrentTab();
|
||||
toolbox = doc = null;
|
||||
finish();
|
||||
});
|
||||
gBrowser.removeCurrentTab();
|
||||
toolbox = doc = null;
|
||||
finish();
|
||||
}
|
||||
|
@ -316,8 +316,8 @@ OptionsPanel.prototype = {
|
||||
|
||||
if (this.target.activeTab) {
|
||||
this.target.client.attachTab(this.target.activeTab._actor, (response) => {
|
||||
this._origJavascriptEnabled = response.javascriptEnabled;
|
||||
this.disableJSNode.checked = !this._origJavascriptEnabled;
|
||||
this._origJavascriptEnabled = !response.javascriptEnabled;
|
||||
this.disableJSNode.checked = this._origJavascriptEnabled;
|
||||
this.disableJSNode.addEventListener("click", this._disableJSClicked, false);
|
||||
});
|
||||
} else {
|
||||
@ -370,25 +370,29 @@ OptionsPanel.prototype = {
|
||||
}
|
||||
|
||||
let deferred = promise.defer();
|
||||
|
||||
this.destroyPromise = deferred.promise;
|
||||
|
||||
this._removeListeners();
|
||||
|
||||
if (this.target.activeTab) {
|
||||
this.disableJSNode.removeEventListener("click", this._disableJSClicked, false);
|
||||
// If JavaScript is disabled we need to revert it to it's original value.
|
||||
let options = {
|
||||
"javascriptEnabled": this._origJavascriptEnabled
|
||||
};
|
||||
this.target.activeTab.reconfigure(options, () => {
|
||||
this.toolbox = null;
|
||||
this.disableJSNode.removeEventListener("click", this._disableJSClicked);
|
||||
// FF41+ automatically cleans up state in actor on disconnect
|
||||
if (!this.target.activeTab.traits.noTabReconfigureOnClose) {
|
||||
let options = {
|
||||
"javascriptEnabled": this._origJavascriptEnabled,
|
||||
"performReload": false
|
||||
};
|
||||
this.target.activeTab.reconfigure(options, deferred.resolve);
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
}
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
this.panelWin = this.panelDoc = this.disableJSNode = null;
|
||||
this.panelWin = this.panelDoc = this.disableJSNode = this.toolbox = null;
|
||||
|
||||
return deferred.promise;
|
||||
return this.destroyPromise;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -54,7 +54,6 @@ function setPrefDefaults() {
|
||||
Services.prefs.setBoolPref("devtools.performance.ui.show-platform-data", true);
|
||||
Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
|
||||
Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true);
|
||||
Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
|
@ -1753,7 +1753,8 @@ Toolbox.prototype = {
|
||||
|
||||
// Now that we are closing the toolbox we can re-enable the cache settings
|
||||
// and disable the service workers testing settings for the current tab.
|
||||
if (this.target.activeTab) {
|
||||
// FF41+ automatically cleans up state in actor on disconnect.
|
||||
if (this.target.activeTab && !this.target.activeTab.traits.noTabReconfigureOnClose) {
|
||||
this.target.activeTab.reconfigure({
|
||||
"cacheDisabled": false,
|
||||
"serviceWorkersTestingEnabled": false
|
||||
|
@ -1502,10 +1502,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
}
|
||||
let nameWithQuery = this._getUriNameWithQuery(uri);
|
||||
let hostPort = this._getUriHostPort(uri);
|
||||
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(uri.spec));
|
||||
|
||||
let file = $(".requests-menu-file", target);
|
||||
file.setAttribute("value", nameWithQuery);
|
||||
file.setAttribute("tooltiptext", nameWithQuery);
|
||||
file.setAttribute("tooltiptext", unicodeUrl);
|
||||
|
||||
let domain = $(".requests-menu-domain", target);
|
||||
domain.setAttribute("value", hostPort);
|
||||
@ -1986,7 +1987,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
if (!(aUrl instanceof Ci.nsIURL)) {
|
||||
aUrl = nsIURL(aUrl);
|
||||
}
|
||||
let name = NetworkHelper.convertToUnicode(unescape(aUrl.fileName)) || "/";
|
||||
let name = NetworkHelper.convertToUnicode(unescape(aUrl.fileName || aUrl.filePath || "/"));
|
||||
let query = NetworkHelper.convertToUnicode(unescape(aUrl.query));
|
||||
return name + (query ? "?" + query : "");
|
||||
},
|
||||
|
@ -24,6 +24,7 @@ support-files =
|
||||
html_sorting-test-page.html
|
||||
html_statistics-test-page.html
|
||||
html_status-codes-test-page.html
|
||||
html_api-calls-test-page.html
|
||||
html_copy-as-curl.html
|
||||
html_curl-utils.html
|
||||
sjs_content-type-test-server.sjs
|
||||
@ -37,6 +38,7 @@ support-files =
|
||||
[browser_net_aaa_leaktest.js]
|
||||
[browser_net_accessibility-01.js]
|
||||
[browser_net_accessibility-02.js]
|
||||
[browser_net_api-calls.js]
|
||||
[browser_net_autoscroll.js]
|
||||
[browser_net_cached-status.js]
|
||||
[browser_net_charts-01.js]
|
||||
|
40
browser/devtools/netmonitor/test/browser_net_api-calls.js
Normal file
40
browser/devtools/netmonitor/test/browser_net_api-calls.js
Normal file
@ -0,0 +1,40 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests whether API call URLs (without a filename) are correctly displayed (including Unicode)
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(API_CALLS_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, L10N, EVENTS, Editor, NetMonitorView } = aMonitor.panelWin;
|
||||
let { RequestsMenu, NetworkDetails } = NetMonitorView;
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
|
||||
const REQUEST_URIS = [
|
||||
"http://example.com/api/fileName.xml",
|
||||
"http://example.com/api/file%E2%98%A2.xml",
|
||||
"http://example.com/api/ascii/get/",
|
||||
"http://example.com/api/unicode/%E2%98%A2/",
|
||||
"http://example.com/api/search/?q=search%E2%98%A2"
|
||||
];
|
||||
|
||||
Task.spawn(function*() {
|
||||
yield waitForNetworkEvents(aMonitor, 5);
|
||||
|
||||
REQUEST_URIS.forEach(function(uri, index) {
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(index), "GET", uri);
|
||||
});
|
||||
|
||||
yield teardown(aMonitor);
|
||||
finish();
|
||||
});
|
||||
|
||||
aDebuggee.performRequests();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ function test() {
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
|
||||
Task.spawn(function () {
|
||||
Task.spawn(function*() {
|
||||
yield waitForNetworkEvents(aMonitor, 1, 6);
|
||||
|
||||
RequestsMenu.selectedItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
@ -10,11 +10,13 @@ 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 { CurlUtils } = Cu.import("resource:///modules/devtools/Curl.jsm", {});
|
||||
let NetworkHelper = devtools.require("devtools/toolkit/webconsole/network-helper");
|
||||
let TargetFactory = devtools.TargetFactory;
|
||||
let Toolbox = devtools.Toolbox;
|
||||
|
||||
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test/";
|
||||
|
||||
const API_CALLS_URL = EXAMPLE_URL + "html_api-calls-test-page.html";
|
||||
const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
|
||||
const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
|
||||
const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
|
||||
@ -278,8 +280,9 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
|
||||
let { attachment, target } = aRequestItem
|
||||
|
||||
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
|
||||
let name = uri.fileName || "/";
|
||||
let query = uri.query;
|
||||
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(aUrl));
|
||||
let name = NetworkHelper.convertToUnicode(unescape(uri.fileName || uri.filePath || "/"));
|
||||
let query = NetworkHelper.convertToUnicode(unescape(uri.query));
|
||||
let hostPort = uri.hostPort;
|
||||
let remoteAddress = attachment.remoteAddress;
|
||||
|
||||
@ -297,13 +300,13 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
|
||||
if (fuzzyUrl) {
|
||||
ok(target.querySelector(".requests-menu-file").getAttribute("value").startsWith(
|
||||
name + (query ? "?" + query : "")), "The displayed file is correct.");
|
||||
ok(target.querySelector(".requests-menu-file").getAttribute("tooltiptext").startsWith(
|
||||
name + (query ? "?" + query : "")), "The tooltip file is correct.");
|
||||
ok(target.querySelector(".requests-menu-file").getAttribute("tooltiptext").startsWith(unicodeUrl),
|
||||
"The tooltip file is correct.");
|
||||
} else {
|
||||
is(target.querySelector(".requests-menu-file").getAttribute("value"),
|
||||
name + (query ? "?" + query : ""), "The displayed file is correct.");
|
||||
is(target.querySelector(".requests-menu-file").getAttribute("tooltiptext"),
|
||||
name + (query ? "?" + query : ""), "The tooltip file is correct.");
|
||||
unicodeUrl, "The tooltip file is correct.");
|
||||
}
|
||||
|
||||
is(target.querySelector(".requests-menu-domain").getAttribute("value"),
|
||||
|
@ -0,0 +1,46 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<title>Network Monitor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>API calls request test</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
function get(aAddress, aCallback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", aAddress, true);
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState == this.DONE) {
|
||||
aCallback();
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function performRequests() {
|
||||
get("/api/fileName.xml", function() {
|
||||
get("/api/file%E2%98%A2.xml", function() {
|
||||
get("/api/ascii/get/", function() {
|
||||
get("/api/unicode/%E2%98%A2/", function() {
|
||||
get("/api/search/?q=search%E2%98%A2", function() {
|
||||
// Done.
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -131,11 +131,13 @@ StyleEditorPanel.prototype = {
|
||||
this._target.off("close", this.destroy);
|
||||
this._target = null;
|
||||
this._toolbox = null;
|
||||
this._panelWin = null;
|
||||
this._panelDoc = null;
|
||||
this._debuggee.destroy();
|
||||
this._debuggee = null;
|
||||
|
||||
this.UI.destroy();
|
||||
this.UI = null;
|
||||
}
|
||||
|
||||
return promise.resolve(null);
|
||||
|
@ -234,7 +234,14 @@ ElementStyle.prototype = {
|
||||
|
||||
return null;
|
||||
});
|
||||
}).then(null, promiseWarn);
|
||||
}).then(null, e => {
|
||||
// populate is often called after a setTimeout,
|
||||
// the connection may already be closed.
|
||||
if (this.destroyed) {
|
||||
return;
|
||||
}
|
||||
return promiseWarn(e);
|
||||
});
|
||||
this.populated = populated;
|
||||
return this.populated;
|
||||
},
|
||||
|
@ -5,8 +5,7 @@
|
||||
<script type="text/javascript">
|
||||
function test() {
|
||||
console.log("start");
|
||||
console.clear()
|
||||
console.timeStamp()
|
||||
console.clear();
|
||||
console.log("end");
|
||||
}
|
||||
</script>
|
||||
|
@ -1334,6 +1334,11 @@ WebConsoleFrame.prototype = {
|
||||
break;
|
||||
}
|
||||
|
||||
case "timeStamp": {
|
||||
// console.timeStamp() doesn't need to display anything.
|
||||
return null;
|
||||
}
|
||||
|
||||
default:
|
||||
Cu.reportError("Unknown Console API log level: " + level);
|
||||
return null;
|
||||
|
@ -45,15 +45,97 @@ let consoleAppender;
|
||||
// A set of all preference roots used by all instances.
|
||||
let allBranches = new Set();
|
||||
|
||||
// A storage appender that is flushable to a file on disk. Policies for
|
||||
// when to flush, to what file, log rotation etc are up to the consumer
|
||||
// (although it does maintain a .sawError property to help the consumer decide
|
||||
// based on its policies)
|
||||
function FlushableStorageAppender(formatter) {
|
||||
Log.StorageStreamAppender.call(this, formatter);
|
||||
this.sawError = false;
|
||||
}
|
||||
|
||||
FlushableStorageAppender.prototype = {
|
||||
__proto__: Log.StorageStreamAppender.prototype,
|
||||
|
||||
append(message) {
|
||||
if (message.level >= Log.Level.Error) {
|
||||
this.sawError = true;
|
||||
}
|
||||
Log.StorageStreamAppender.prototype.append.call(this, message);
|
||||
},
|
||||
|
||||
reset() {
|
||||
Log.StorageStreamAppender.prototype.reset.call(this);
|
||||
this.sawError = false;
|
||||
},
|
||||
|
||||
// Flush the current stream to a file. Somewhat counter-intuitively, you
|
||||
// must pass a log which will be written to with details of the operation.
|
||||
flushToFile: Task.async(function* (subdirArray, filename, log) {
|
||||
let inStream = this.getInputStream();
|
||||
this.reset();
|
||||
if (!inStream) {
|
||||
log.debug("Failed to flush log to a file - no input stream");
|
||||
return;
|
||||
}
|
||||
log.debug("Flushing file log");
|
||||
log.trace("Beginning stream copy to " + filename + ": " + Date.now());
|
||||
try {
|
||||
yield this._copyStreamToFile(inStream, subdirArray, filename, log);
|
||||
log.trace("onCopyComplete", Date.now());
|
||||
} catch (ex) {
|
||||
log.error("Failed to copy log stream to file", ex);
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Copy an input stream to the named file, doing everything off the main
|
||||
* thread.
|
||||
* subDirArray is an array of path components, relative to the profile
|
||||
* directory, where the file will be created.
|
||||
* outputFileName is the filename to create.
|
||||
* Returns a promise that is resolved on completion or rejected with an error.
|
||||
*/
|
||||
_copyStreamToFile: Task.async(function* (inputStream, subdirArray, outputFileName, log) {
|
||||
// The log data could be large, so we don't want to pass it all in a single
|
||||
// message, so use BUFFER_SIZE chunks.
|
||||
const BUFFER_SIZE = 8192;
|
||||
|
||||
// get a binary stream
|
||||
let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
|
||||
binaryStream.setInputStream(inputStream);
|
||||
|
||||
let outputDirectory = OS.Path.join(OS.Constants.Path.profileDir, ...subdirArray);
|
||||
yield OS.File.makeDir(outputDirectory, { ignoreExisting: true, from: OS.Constants.Path.profileDir });
|
||||
let fullOutputFileName = OS.Path.join(outputDirectory, outputFileName);
|
||||
let output = yield OS.File.open(fullOutputFileName, { write: true} );
|
||||
try {
|
||||
while (true) {
|
||||
let available = binaryStream.available();
|
||||
if (!available) {
|
||||
break;
|
||||
}
|
||||
let chunk = binaryStream.readByteArray(Math.min(available, BUFFER_SIZE));
|
||||
yield output.write(new Uint8Array(chunk));
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
binaryStream.close(); // inputStream is closed by the binaryStream
|
||||
yield output.close();
|
||||
} catch (ex) {
|
||||
log.error("Failed to close the input stream", ex);
|
||||
}
|
||||
}
|
||||
log.trace("finished copy to", fullOutputFileName);
|
||||
}),
|
||||
}
|
||||
|
||||
// The public LogManager object.
|
||||
function LogManager(prefRoot, logNames, logFilePrefix) {
|
||||
this.init(prefRoot, logNames, logFilePrefix);
|
||||
}
|
||||
|
||||
LogManager.prototype = {
|
||||
REASON_SUCCESS: "success",
|
||||
REASON_ERROR: "error",
|
||||
|
||||
_cleaningUpFileLogs: false,
|
||||
|
||||
_prefObservers: [],
|
||||
@ -102,11 +184,11 @@ LogManager.prototype = {
|
||||
return observer;
|
||||
}
|
||||
|
||||
this._observeConsolePref = setupAppender(consoleAppender, "log.appender.console", Log.Level.Error, true);
|
||||
this._observeConsolePref = setupAppender(consoleAppender, "log.appender.console", Log.Level.Fatal, true);
|
||||
this._observeDumpPref = setupAppender(dumpAppender, "log.appender.dump", Log.Level.Error, true);
|
||||
|
||||
// The file appender doesn't get the special singleton behaviour.
|
||||
let fapp = this._fileAppender = new Log.StorageStreamAppender(formatter);
|
||||
let fapp = this._fileAppender = new FlushableStorageAppender(formatter);
|
||||
// the stream gets a default of Debug as the user must go out of their way
|
||||
// to see the stuff spewed to it.
|
||||
this._observeStreamPref = setupAppender(fapp, "log.appender.file.level", Log.Level.Debug);
|
||||
@ -150,109 +232,62 @@ LogManager.prototype = {
|
||||
return ["weave", "logs"];
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy an input stream to the named file, doing everything off the main
|
||||
* thread.
|
||||
* outputFileName is a string with the tail of the filename - the file will
|
||||
* be created in the log directory.
|
||||
* Returns a promise that is resolved<undefined> on completion or rejected if
|
||||
* there is an error.
|
||||
*/
|
||||
_copyStreamToFile: Task.async(function* (inputStream, outputFileName) {
|
||||
// The log data could be large, so we don't want to pass it all in a single
|
||||
// message, so use BUFFER_SIZE chunks.
|
||||
const BUFFER_SIZE = 8192;
|
||||
|
||||
// get a binary stream
|
||||
let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
|
||||
binaryStream.setInputStream(inputStream);
|
||||
// We assume the profile directory exists, but not that the dirs under it do.
|
||||
let profd = FileUtils.getDir("ProfD", []);
|
||||
let outputFile = FileUtils.getDir("ProfD", this._logFileSubDirectoryEntries);
|
||||
yield OS.File.makeDir(outputFile.path, { ignoreExisting: true, from: profd.path });
|
||||
outputFile.append(outputFileName);
|
||||
let output = yield OS.File.open(outputFile.path, { write: true} );
|
||||
try {
|
||||
while (true) {
|
||||
let available = binaryStream.available();
|
||||
if (!available) {
|
||||
break;
|
||||
}
|
||||
let chunk = binaryStream.readByteArray(Math.min(available, BUFFER_SIZE));
|
||||
yield output.write(new Uint8Array(chunk));
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
binaryStream.close(); // inputStream is closed by the binaryStream
|
||||
yield output.close();
|
||||
} catch (ex) {
|
||||
this._log.error("Failed to close the input stream", ex);
|
||||
}
|
||||
}
|
||||
this._log.trace("finished copy to", outputFile.path);
|
||||
}),
|
||||
// Result values for resetFileLog.
|
||||
SUCCESS_LOG_WRITTEN: "success-log-written",
|
||||
ERROR_LOG_WRITTEN: "error-log-written",
|
||||
|
||||
/**
|
||||
* Possibly generate a log file for all accumulated log messages and refresh
|
||||
* the input & output streams.
|
||||
* Returns a promise that resolves on completion or rejects if the file could
|
||||
* not be written.
|
||||
* Whether a "success" or "error" log is written is determined based on
|
||||
* whether an "Error" log entry was written to any of the logs.
|
||||
* Returns a promise that resolves on completion with either null (for no
|
||||
* file written or on error), SUCCESS_LOG_WRITTEN if a "success" log was
|
||||
* written, or ERROR_LOG_WRITTEN if an "error" log was written.
|
||||
*/
|
||||
resetFileLog: Task.async(function* (reason) {
|
||||
resetFileLog: Task.async(function* () {
|
||||
try {
|
||||
let flushToFile;
|
||||
let reasonPrefix;
|
||||
switch (reason) {
|
||||
case this.REASON_SUCCESS:
|
||||
flushToFile = this._prefs.get("log.appender.file.logOnSuccess", false);
|
||||
reasonPrefix = "success";
|
||||
break;
|
||||
case this.REASON_ERROR:
|
||||
flushToFile = this._prefs.get("log.appender.file.logOnError", true);
|
||||
reasonPrefix = "error";
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid reason");
|
||||
let reason;
|
||||
if (this._fileAppender.sawError) {
|
||||
reason = this.ERROR_LOG_WRITTEN;
|
||||
flushToFile = this._prefs.get("log.appender.file.logOnError", true);
|
||||
reasonPrefix = "error";
|
||||
} else {
|
||||
reason = this.SUCCESS_LOG_WRITTEN;
|
||||
flushToFile = this._prefs.get("log.appender.file.logOnSuccess", false);
|
||||
reasonPrefix = "success";
|
||||
}
|
||||
|
||||
// might as well avoid creating an input stream if we aren't going to use it.
|
||||
if (!flushToFile) {
|
||||
this._fileAppender.reset();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
let inStream = this._fileAppender.getInputStream();
|
||||
this._fileAppender.reset();
|
||||
if (inStream) {
|
||||
this._log.debug("Flushing file log");
|
||||
// We have reasonPrefix at the start of the filename so all "error"
|
||||
// logs are grouped in about:sync-log.
|
||||
let filename = reasonPrefix + "-" + this.logFilePrefix + "-" + Date.now() + ".txt";
|
||||
this._log.trace("Beginning stream copy to " + filename + ": " +
|
||||
Date.now());
|
||||
try {
|
||||
yield this._copyStreamToFile(inStream, filename);
|
||||
this._log.trace("onCopyComplete", Date.now());
|
||||
} catch (ex) {
|
||||
this._log.error("Failed to copy log stream to file", ex);
|
||||
return;
|
||||
}
|
||||
// It's not completely clear to markh why we only do log cleanups
|
||||
// for errors, but for now the Sync semantics have been copied...
|
||||
// (one theory is that only cleaning up on error makes it less
|
||||
// likely old error logs would be removed, but that's not true if
|
||||
// there are occasional errors - let's address this later!)
|
||||
if (reason == this.REASON_ERROR && !this._cleaningUpFileLogs) {
|
||||
this._log.trace("Scheduling cleanup.");
|
||||
// Note we don't return/yield or otherwise wait on this promise - it
|
||||
// continues in the background
|
||||
this.cleanupLogs().catch(err => {
|
||||
this._log.error("Failed to cleanup logs", err);
|
||||
});
|
||||
}
|
||||
// We have reasonPrefix at the start of the filename so all "error"
|
||||
// logs are grouped in about:sync-log.
|
||||
let filename = reasonPrefix + "-" + this.logFilePrefix + "-" + Date.now() + ".txt";
|
||||
yield this._fileAppender.flushToFile(this._logFileSubDirectoryEntries, filename, this._log);
|
||||
|
||||
// It's not completely clear to markh why we only do log cleanups
|
||||
// for errors, but for now the Sync semantics have been copied...
|
||||
// (one theory is that only cleaning up on error makes it less
|
||||
// likely old error logs would be removed, but that's not true if
|
||||
// there are occasional errors - let's address this later!)
|
||||
if (reason == this.ERROR_LOG_WRITTEN && !this._cleaningUpFileLogs) {
|
||||
this._log.trace("Scheduling cleanup.");
|
||||
// Note we don't return/yield or otherwise wait on this promise - it
|
||||
// continues in the background
|
||||
this.cleanupLogs().catch(err => {
|
||||
this._log.error("Failed to cleanup logs", err);
|
||||
});
|
||||
}
|
||||
return reason;
|
||||
} catch (ex) {
|
||||
this._log.error("Failed to resetFileLog", ex)
|
||||
this._log.error("Failed to resetFileLog", ex);
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
|
||||
|
@ -30,10 +30,10 @@ add_task(function* test_noPrefs() {
|
||||
|
||||
let log = Log.repository.getLogger("TestLog");
|
||||
let [capp, dapp, fapps] = getAppenders(log);
|
||||
// the "dump" and "console" appenders should get Error level
|
||||
equal(capp.level, Log.Level.Error);
|
||||
// The console appender gets "Fatal" while the "dump" appender gets "Error" levels
|
||||
equal(capp.level, Log.Level.Fatal);
|
||||
equal(dapp.level, Log.Level.Error);
|
||||
// and the file (stream) appender gets Dump by default
|
||||
// and the file (stream) appender gets Debug by default
|
||||
equal(fapps.length, 1, "only 1 file appender");
|
||||
equal(fapps[0].level, Log.Level.Debug);
|
||||
lm.finalize();
|
||||
@ -62,7 +62,7 @@ add_task(function* test_PrefChanges() {
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.console", "xxx");
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.dump", "xxx");
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.file.level", "xxx");
|
||||
equal(capp.level, Log.Level.Error);
|
||||
equal(capp.level, Log.Level.Fatal);
|
||||
equal(dapp.level, Log.Level.Error);
|
||||
equal(fapp.level, Log.Level.Debug);
|
||||
lm.finalize();
|
||||
@ -131,5 +131,99 @@ add_task(function* test_logFileErrorDefault() {
|
||||
yield lm.resetFileLog(lm.REASON_ERROR);
|
||||
// One error log file exists.
|
||||
checkLogFile("error");
|
||||
|
||||
lm.finalize();
|
||||
});
|
||||
|
||||
// Test that we correctly write success logs.
|
||||
add_task(function* test_logFileSuccess() {
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnError", false);
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnSuccess", false);
|
||||
|
||||
let lm = new LogManager("log-manager.test.", ["TestLog2"], "test");
|
||||
|
||||
let log = Log.repository.getLogger("TestLog2");
|
||||
log.info("an info message");
|
||||
yield lm.resetFileLog();
|
||||
// Zero log files exist.
|
||||
checkLogFile(null);
|
||||
|
||||
// Reset logOnSuccess and do it again - log should appear.
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnSuccess", true);
|
||||
log.info("an info message");
|
||||
yield lm.resetFileLog();
|
||||
|
||||
checkLogFile("success");
|
||||
|
||||
// Now test with no "reason" specified and no "error" record.
|
||||
log.info("an info message");
|
||||
yield lm.resetFileLog();
|
||||
// should get a "success" entry.
|
||||
checkLogFile("success");
|
||||
|
||||
// With no "reason" and an error record - should get no success log.
|
||||
log.error("an error message");
|
||||
yield lm.resetFileLog();
|
||||
// should get no entry
|
||||
checkLogFile(null);
|
||||
|
||||
// And finally now with no error, to ensure that the fact we had an error
|
||||
// previously doesn't persist after the .resetFileLog call.
|
||||
log.info("an info message");
|
||||
yield lm.resetFileLog();
|
||||
checkLogFile("success");
|
||||
|
||||
lm.finalize();
|
||||
});
|
||||
|
||||
// Test that we correctly write error logs.
|
||||
add_task(function* test_logFileError() {
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnError", false);
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnSuccess", false);
|
||||
|
||||
let lm = new LogManager("log-manager.test.", ["TestLog2"], "test");
|
||||
|
||||
let log = Log.repository.getLogger("TestLog2");
|
||||
log.info("an info message");
|
||||
let reason = yield lm.resetFileLog();
|
||||
Assert.equal(reason, null, "null returned when no file created.");
|
||||
// Zero log files exist.
|
||||
checkLogFile(null);
|
||||
|
||||
// Reset logOnSuccess - success logs should appear if no error records.
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnSuccess", true);
|
||||
log.info("an info message");
|
||||
reason = yield lm.resetFileLog();
|
||||
Assert.equal(reason, lm.SUCCESS_LOG_WRITTEN);
|
||||
checkLogFile("success");
|
||||
|
||||
// Set logOnError and unset logOnSuccess - error logs should appear.
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnSuccess", false);
|
||||
Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnError", true);
|
||||
log.error("an error message");
|
||||
reason = yield lm.resetFileLog();
|
||||
Assert.equal(reason, lm.ERROR_LOG_WRITTEN);
|
||||
checkLogFile("error");
|
||||
|
||||
// Now test with no "error" record.
|
||||
log.info("an info message");
|
||||
reason = yield lm.resetFileLog();
|
||||
// should get no file
|
||||
Assert.equal(reason, null);
|
||||
checkLogFile(null);
|
||||
|
||||
// With an error record we should get an error log.
|
||||
log.error("an error message");
|
||||
reason = yield lm.resetFileLog();
|
||||
// should get en error log
|
||||
Assert.equal(reason, lm.ERROR_LOG_WRITTEN);
|
||||
checkLogFile("error");
|
||||
|
||||
// And finally now with success, to ensure that the fact we had an error
|
||||
// previously doesn't persist after the .resetFileLog call.
|
||||
log.info("an info message");
|
||||
yield lm.resetFileLog();
|
||||
checkLogFile(null);
|
||||
|
||||
lm.finalize();
|
||||
});
|
||||
|
@ -602,7 +602,8 @@ ErrorHandler.prototype = {
|
||||
this._log.debug(engine_name + " failed: " + Utils.exceptionStr(exception));
|
||||
break;
|
||||
case "weave:service:login:error":
|
||||
this.resetFileLog(this._logManager.REASON_ERROR);
|
||||
this._log.error("Sync encountered a login error");
|
||||
this.resetFileLog();
|
||||
|
||||
if (this.shouldReportError()) {
|
||||
this.notifyOnNextTick("weave:ui:login:error");
|
||||
@ -617,7 +618,8 @@ ErrorHandler.prototype = {
|
||||
this.service.logout();
|
||||
}
|
||||
|
||||
this.resetFileLog(this._logManager.REASON_ERROR);
|
||||
this._log.error("Sync encountered an error");
|
||||
this.resetFileLog();
|
||||
|
||||
if (this.shouldReportError()) {
|
||||
this.notifyOnNextTick("weave:ui:sync:error");
|
||||
@ -642,8 +644,8 @@ ErrorHandler.prototype = {
|
||||
}
|
||||
|
||||
if (Status.service == SYNC_FAILED_PARTIAL) {
|
||||
this._log.debug("Some engines did not sync correctly.");
|
||||
this.resetFileLog(this._logManager.REASON_ERROR);
|
||||
this._log.error("Some engines did not sync correctly.");
|
||||
this.resetFileLog();
|
||||
|
||||
if (this.shouldReportError()) {
|
||||
this.dontIgnoreErrors = false;
|
||||
@ -651,7 +653,7 @@ ErrorHandler.prototype = {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.resetFileLog(this._logManager.REASON_SUCCESS);
|
||||
this.resetFileLog();
|
||||
}
|
||||
this.dontIgnoreErrors = false;
|
||||
this.notifyOnNextTick("weave:ui:sync:finish");
|
||||
@ -681,19 +683,18 @@ ErrorHandler.prototype = {
|
||||
/**
|
||||
* Generate a log file for the sync that just completed
|
||||
* and refresh the input & output streams.
|
||||
*
|
||||
* @param reason
|
||||
* A constant from the LogManager that indicates the reason for the
|
||||
* reset.
|
||||
*/
|
||||
resetFileLog: function resetFileLog(reason) {
|
||||
let onComplete = () => {
|
||||
resetFileLog: function resetFileLog() {
|
||||
let onComplete = logType => {
|
||||
Svc.Obs.notify("weave:service:reset-file-log");
|
||||
this._log.trace("Notified: " + Date.now());
|
||||
if (logType == this._logManager.ERROR_LOG_WRITTEN) {
|
||||
Cu.reportError("Sync encountered an error - see about:sync-log for the log file.");
|
||||
}
|
||||
};
|
||||
// Note we do not return the promise here - the caller doesn't need to wait
|
||||
// for this to complete.
|
||||
this._logManager.resetFileLog(reason).then(onComplete, onComplete);
|
||||
this._logManager.resetFileLog().then(onComplete, onComplete);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -54,7 +54,7 @@ pref("services.sync.addons.ignoreUserEnabledChanges", false);
|
||||
// Comma-delimited list of hostnames to trust for add-on install.
|
||||
pref("services.sync.addons.trustedSourceHostnames", "addons.mozilla.org");
|
||||
|
||||
pref("services.sync.log.appender.console", "Warn");
|
||||
pref("services.sync.log.appender.console", "Fatal");
|
||||
pref("services.sync.log.appender.dump", "Error");
|
||||
pref("services.sync.log.appender.file.level", "Trace");
|
||||
pref("services.sync.log.appender.file.logOnError", true);
|
||||
|
@ -134,6 +134,9 @@ let AddonWatcher = {
|
||||
longestDuration: Math.round(Math.log2(Preferences.get("browser.addon-watch.limits.longestDuration", 128))),
|
||||
};
|
||||
|
||||
// By default, warn only after an add-on has been spotted misbehaving 3 times.
|
||||
let tolerance = Preferences.get("browser.addon-watch.tolerance", 3);
|
||||
|
||||
for (let item of snapshot.componentsData) {
|
||||
let addonId = item.addonId;
|
||||
if (!item.isSystem || !addonId) {
|
||||
@ -192,6 +195,10 @@ let AddonWatcher = {
|
||||
|
||||
stats.alerts[filter] = (stats.alerts[filter] || 0) + 1;
|
||||
|
||||
if (stats.alerts[filter] % tolerance != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
this._callback(addonId, filter);
|
||||
} catch (ex) {
|
||||
|
@ -3421,6 +3421,16 @@ var InspectorActor = exports.InspectorActor = protocol.ActorClass({
|
||||
this.tabActor = tabActor;
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
protocol.Actor.prototype.destroy.call(this);
|
||||
},
|
||||
|
||||
// Forces destruction of the actor and all its children
|
||||
// like highlighter, walker and style actors.
|
||||
disconnect: function() {
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
get window() this.tabActor.window,
|
||||
|
||||
getWalker: method(function(options={}) {
|
||||
|
@ -725,7 +725,15 @@ function TabActor(aConnection)
|
||||
// Used on b2g to catch activity frames and in chrome to list all frames
|
||||
this.listenForNewDocShells = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
|
||||
|
||||
this.traits = { reconfigure: true, frames: true };
|
||||
this.traits = {
|
||||
reconfigure: true,
|
||||
// Supports frame listing via `listFrames` request and `frameUpdate` events
|
||||
// as well as frame switching via `switchToFrame` request
|
||||
frames: true,
|
||||
// Do not require to send reconfigure request to reset the document state
|
||||
// to what it was before using the TabActor
|
||||
noTabReconfigureOnClose: true
|
||||
};
|
||||
|
||||
this._workerActorList = null;
|
||||
this._workerActorPool = null;
|
||||
@ -1300,6 +1308,7 @@ TabActor.prototype = {
|
||||
// during Firefox shutdown.
|
||||
if (this.docShell) {
|
||||
this._progressListener.unwatch(this.docShell);
|
||||
this._restoreDocumentSettings();
|
||||
}
|
||||
if (this._progressListener) {
|
||||
this._progressListener.destroy();
|
||||
@ -1390,14 +1399,19 @@ TabActor.prototype = {
|
||||
onReconfigure: function (aRequest) {
|
||||
let options = aRequest.options || {};
|
||||
|
||||
this._toggleDevtoolsSettings(options);
|
||||
if (!this.docShell) {
|
||||
// The tab is already closed.
|
||||
return {};
|
||||
}
|
||||
this._toggleDevToolsSettings(options);
|
||||
|
||||
return {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle logic to enable/disable JS/cache/Service Worker testing.
|
||||
*/
|
||||
_toggleDevtoolsSettings: function(options) {
|
||||
_toggleDevToolsSettings: function(options) {
|
||||
// Wait a tick so that the response packet can be dispatched before the
|
||||
// subsequent navigation event packet.
|
||||
let reload = false;
|
||||
@ -1429,6 +1443,16 @@ TabActor.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Opposite of the _toggleDevToolsSettings method, that reset document state
|
||||
* when closing the toolbox.
|
||||
*/
|
||||
_restoreDocumentSettings: function () {
|
||||
this._restoreJavascript();
|
||||
this._setCacheDisabled(false);
|
||||
this._setServiceWorkersTestingEnabled(false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable or enable the cache via docShell.
|
||||
*/
|
||||
@ -1437,29 +1461,46 @@ TabActor.prototype = {
|
||||
let disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
|
||||
Ci.nsIRequest.INHIBIT_CACHING;
|
||||
|
||||
if (this.docShell) {
|
||||
this.docShell.defaultLoadFlags = disabled ? disable : enable;
|
||||
}
|
||||
this.docShell.defaultLoadFlags = disabled ? disable : enable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable or enable JS via docShell.
|
||||
*/
|
||||
_wasJavascriptEnabled: null,
|
||||
_setJavascriptEnabled: function(allow) {
|
||||
if (this.docShell) {
|
||||
this.docShell.allowJavascript = allow;
|
||||
if (this._wasJavascriptEnabled === null) {
|
||||
this._wasJavascriptEnabled = this.docShell.allowJavascript;
|
||||
}
|
||||
this.docShell.allowJavascript = allow;
|
||||
},
|
||||
|
||||
/**
|
||||
* Restore JS state, before the actor modified it.
|
||||
*/
|
||||
_restoreJavascript: function () {
|
||||
if (this._wasJavascriptEnabled !== null) {
|
||||
this._setJavascriptEnabled(this._wasJavascriptEnabled);
|
||||
this._wasJavascriptEnabled = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return JS allowed status.
|
||||
*/
|
||||
_getJavascriptEnabled: function() {
|
||||
if (!this.docShell) {
|
||||
// The tab is already closed.
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.docShell.allowJavascript;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable or enable the service workers testing features.
|
||||
*/
|
||||
_setServiceWorkersTestingEnabled: function(enabled) {
|
||||
if (!this.docShell) {
|
||||
// The tab is already closed.
|
||||
return null;
|
||||
}
|
||||
|
||||
let windowUtils = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.serviceWorkersTestingEnabled = enabled;
|
||||
@ -1479,18 +1520,6 @@ TabActor.prototype = {
|
||||
return this.docShell.defaultLoadFlags === disable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return JS allowed status.
|
||||
*/
|
||||
_getJavascriptEnabled: function() {
|
||||
if (!this.docShell) {
|
||||
// The tab is already closed.
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.docShell.allowJavascript;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return service workers testing allowed status.
|
||||
*/
|
||||
|
@ -179,6 +179,19 @@ function safeCall(aCallback, ...aArgs) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that will call the passed callback catching and logging
|
||||
* any exceptions.
|
||||
*
|
||||
* @param aCallback
|
||||
* The callback method to call
|
||||
*/
|
||||
function makeSafe(aCallback) {
|
||||
return function(...aArgs) {
|
||||
safeCall(aCallback, ...aArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an exception thrown by a provider API method.
|
||||
*/
|
||||
@ -243,6 +256,26 @@ function callProviderAsync(aProvider, aMethod, ...aArgs) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a method on a provider if it exists and consumes any thrown exception.
|
||||
* Parameters after aMethod are passed to aProvider.aMethod() and an additional
|
||||
* callback is added for the provider to return a result to.
|
||||
*
|
||||
* @param aProvider
|
||||
* The provider to call
|
||||
* @param aMethod
|
||||
* The method name to call
|
||||
* @return {Promise}
|
||||
* @resolves The result the provider returns, or |undefined| if the provider
|
||||
* does not implement the method or the method throws.
|
||||
* @rejects Never
|
||||
*/
|
||||
function promiseCallProvider(aProvider, aMethod, ...aArgs) {
|
||||
return new Promise(resolve => {
|
||||
callProviderAsync(aProvider, aMethod, ...aArgs, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently selected locale for display.
|
||||
* @return the selected locale or "en-US" if none is selected
|
||||
@ -2101,11 +2134,12 @@ var AddonManagerInternal = {
|
||||
*
|
||||
* @param aID
|
||||
* The ID of the add-on to retrieve
|
||||
* @param aCallback
|
||||
* The callback to pass the retrieved add-on to
|
||||
* @throws if the aID or aCallback arguments are not specified
|
||||
* @return {Promise}
|
||||
* @resolves The found Addon or null if no such add-on exists.
|
||||
* @rejects Never
|
||||
* @throws if the aID argument is not specified
|
||||
*/
|
||||
getAddonByID: function AMI_getAddonByID(aID, aCallback) {
|
||||
getAddonByID: function AMI_getAddonByID(aID) {
|
||||
if (!gStarted)
|
||||
throw Components.Exception("AddonManager is not initialized",
|
||||
Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
@ -2114,24 +2148,9 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aID must be a non-empty string",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
if (typeof aCallback != "function")
|
||||
throw Components.Exception("aCallback must be a function",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
new AsyncObjectCaller(this.providers, "getAddonByID", {
|
||||
nextObject: function getAddonByID_nextObject(aCaller, aProvider) {
|
||||
callProviderAsync(aProvider, "getAddonByID", aID,
|
||||
function getAddonByID_safeCall(aAddon) {
|
||||
if (aAddon)
|
||||
safeCall(aCallback, aAddon);
|
||||
else
|
||||
aCaller.callNext();
|
||||
});
|
||||
},
|
||||
|
||||
noMoreObjects: function getAddonByID_noMoreObjects(aCaller) {
|
||||
safeCall(aCallback, null);
|
||||
}
|
||||
let promises = [for (p of this.providers) promiseCallProvider(p, "getAddonByID", aID)];
|
||||
return Promise.all(promises).then(aAddons => {
|
||||
return aAddons.find(a => !!a) || null;
|
||||
});
|
||||
},
|
||||
|
||||
@ -2180,11 +2199,12 @@ var AddonManagerInternal = {
|
||||
*
|
||||
* @param aIDs
|
||||
* The array of IDs to retrieve
|
||||
* @param aCallback
|
||||
* The callback to pass an array of Addons to
|
||||
* @throws if the aID or aCallback arguments are not specified
|
||||
* @return {Promise}
|
||||
* @resolves The array of found add-ons.
|
||||
* @rejects Never
|
||||
* @throws if the aIDs argument is not specified
|
||||
*/
|
||||
getAddonsByIDs: function AMI_getAddonsByIDs(aIDs, aCallback) {
|
||||
getAddonsByIDs: function AMI_getAddonsByIDs(aIDs) {
|
||||
if (!gStarted)
|
||||
throw Components.Exception("AddonManager is not initialized",
|
||||
Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
@ -2193,25 +2213,8 @@ var AddonManagerInternal = {
|
||||
throw Components.Exception("aIDs must be an array",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
if (typeof aCallback != "function")
|
||||
throw Components.Exception("aCallback must be a function",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let addons = [];
|
||||
|
||||
new AsyncObjectCaller(aIDs, null, {
|
||||
nextObject: function getAddonsByIDs_nextObject(aCaller, aID) {
|
||||
AddonManagerInternal.getAddonByID(aID,
|
||||
function getAddonsByIDs_getAddonByID(aAddon) {
|
||||
addons.push(aAddon);
|
||||
aCaller.callNext();
|
||||
});
|
||||
},
|
||||
|
||||
noMoreObjects: function getAddonsByIDs_noMoreObjects(aCaller) {
|
||||
safeCall(aCallback, addons);
|
||||
}
|
||||
});
|
||||
let promises = [AddonManagerInternal.getAddonByID(i) for (i of aIDs)];
|
||||
return Promise.all(promises);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -2876,7 +2879,13 @@ this.AddonManager = {
|
||||
},
|
||||
|
||||
getAddonByID: function AM_getAddonByID(aID, aCallback) {
|
||||
AddonManagerInternal.getAddonByID(aID, aCallback);
|
||||
if (typeof aCallback != "function")
|
||||
throw Components.Exception("aCallback must be a function",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
AddonManagerInternal.getAddonByID(aID)
|
||||
.then(makeSafe(aCallback))
|
||||
.catch(logger.error);
|
||||
},
|
||||
|
||||
getAddonBySyncGUID: function AM_getAddonBySyncGUID(aGUID, aCallback) {
|
||||
@ -2884,7 +2893,13 @@ this.AddonManager = {
|
||||
},
|
||||
|
||||
getAddonsByIDs: function AM_getAddonsByIDs(aIDs, aCallback) {
|
||||
AddonManagerInternal.getAddonsByIDs(aIDs, aCallback);
|
||||
if (typeof aCallback != "function")
|
||||
throw Components.Exception("aCallback must be a function",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
AddonManagerInternal.getAddonsByIDs(aIDs)
|
||||
.then(makeSafe(aCallback))
|
||||
.catch(logger.error);
|
||||
},
|
||||
|
||||
getAddonsWithOperationsByTypes:
|
||||
|
@ -27,9 +27,25 @@ function test_functions() {
|
||||
if (typeof AddonManager[prop] != "function")
|
||||
continue;
|
||||
|
||||
let args = [];
|
||||
|
||||
// Getter functions need a callback and in some cases not having one will
|
||||
// throw before checking if the add-ons manager is initialized so pass in
|
||||
// an empty one.
|
||||
if (prop.startsWith("get")) {
|
||||
// For now all getter functions with more than one argument take the
|
||||
// callback in the second argument.
|
||||
if (AddonManager[prop].length > 1) {
|
||||
args.push(undefined, () => {});
|
||||
}
|
||||
else {
|
||||
args.push(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
do_print("AddonManager." + prop);
|
||||
AddonManager[prop]();
|
||||
AddonManager[prop](...args);
|
||||
do_throw(prop + " did not throw an exception");
|
||||
}
|
||||
catch (e) {
|
||||
|
Loading…
Reference in New Issue
Block a user