mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
968ab88a55
@ -484,7 +484,7 @@ const CustomizableWidgets = [
|
||||
// First sort and filter the list of tabs for each client. Note that the
|
||||
// SyncedTabs module promises that the objects it returns are never
|
||||
// shared, so we are free to mutate those objects directly.
|
||||
const maxTabs = 15;
|
||||
const maxTabs = 50;
|
||||
for (let client of clients) {
|
||||
let tabs = client.tabs;
|
||||
tabs.sort((a, b) => b.lastUsed - a.lastUsed);
|
||||
|
@ -686,7 +686,9 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
||||
.PanelUI-remotetabs-prefs-button {
|
||||
-moz-appearance: none;
|
||||
background-color: #0096dd;
|
||||
color: white;
|
||||
/* !important for the color as an OSX specific rule when a lightweight theme
|
||||
is used for buttons in the toolbox overrides. See bug 1238531 for details */
|
||||
color: white !important;
|
||||
border-radius: 2px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
|
||||
add_task(function* capture() {
|
||||
if (!shouldCapture()) {
|
||||
return;
|
||||
|
@ -5,6 +5,7 @@
|
||||
"use strict";
|
||||
|
||||
const {AddonWatcher} = Cu.import("resource://gre/modules/AddonWatcher.jsm", {});
|
||||
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
|
||||
function setup() {
|
||||
requestLongerTimeout(10);
|
||||
@ -23,7 +24,8 @@ function shouldCapture() {
|
||||
// Automation isn't able to schedule test jobs to only run on nightlies so we handle it here
|
||||
// (see also: bug 1116275). Try pushes and local builds should also capture.
|
||||
let capture = AppConstants.MOZ_UPDATE_CHANNEL == "nightly" ||
|
||||
AppConstants.SOURCE_REVISION_URL.includes("/try/rev/") ||
|
||||
(AppConstants.SOURCE_REVISION_URL.includes("/try/rev/") &&
|
||||
env.get("MOZSCREENSHOTS_SETS")) ||
|
||||
AppConstants.SOURCE_REVISION_URL == "";
|
||||
if (!capture) {
|
||||
ok(true, "Capturing is disabled for this MOZ_UPDATE_CHANNEL or REPO");
|
||||
|
@ -15,6 +15,7 @@ const DEFAULT_FAVICON_TAB = `data:text/html,<meta charset="utf-8">
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
this.Tabs = {
|
||||
init(libDir) {},
|
||||
@ -25,6 +26,9 @@ this.Tabs = {
|
||||
fiveTabsHelper();
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
hoverTab(browserWindow.gBrowser.tabs[3]);
|
||||
yield new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, 3000);
|
||||
});
|
||||
}),
|
||||
},
|
||||
|
||||
@ -48,6 +52,9 @@ this.Tabs = {
|
||||
hoverTab(newTabButton);
|
||||
browserWindow.gBrowser.tabs[browserWindow.gBrowser.tabs.length - 1].
|
||||
setAttribute("beforehovered", true);
|
||||
yield new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, 3000);
|
||||
});
|
||||
}),
|
||||
},
|
||||
|
||||
@ -92,6 +99,9 @@ this.Tabs = {
|
||||
browserWindow.gBrowser.pinTab(tab);
|
||||
browserWindow.gBrowser.selectTabAtIndex(4);
|
||||
hoverTab(browserWindow.gBrowser.tabs[6]);
|
||||
yield new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, 3000);
|
||||
});
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -30,16 +30,19 @@ this.WindowSize = {
|
||||
setTimeout(function waitToLeaveFS() {
|
||||
browserWindow.maximize();
|
||||
resolve();
|
||||
}, Services.appinfo.OS == "Darwin" ? 1500 : 0);
|
||||
}, 5000);
|
||||
});
|
||||
}),
|
||||
},
|
||||
|
||||
normal: {
|
||||
applyConfig: Task.async(() => {
|
||||
applyConfig: Task.async(function*() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
browserWindow.fullScreen = false;
|
||||
browserWindow.restore();
|
||||
yield new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, 5000);
|
||||
});
|
||||
}),
|
||||
},
|
||||
|
||||
@ -49,9 +52,7 @@ this.WindowSize = {
|
||||
browserWindow.fullScreen = true;
|
||||
// OS X Lion fullscreen transition takes a while
|
||||
yield new Promise((resolve, reject) => {
|
||||
setTimeout(function waitAfterEnteringFS() {
|
||||
resolve();
|
||||
}, Services.appinfo.OS == "Darwin" ? 1500 : 0);
|
||||
setTimeout(resolve, 5000);
|
||||
});
|
||||
}),
|
||||
},
|
||||
|
@ -33,6 +33,9 @@ const Strings = Services.strings.createBundle(
|
||||
var AboutDebugging = {
|
||||
_prefListeners: [],
|
||||
|
||||
// Pointer to the current React component.
|
||||
_component: null,
|
||||
|
||||
_categories: null,
|
||||
get categories() {
|
||||
// If needed, initialize the list of available categories.
|
||||
@ -66,11 +69,11 @@ var AboutDebugging = {
|
||||
location.hash = "#" + category;
|
||||
|
||||
if (category == "addons") {
|
||||
React.render(React.createElement(AddonsComponent, { client: this.client }),
|
||||
document.querySelector("#addons"));
|
||||
this._component = React.render(React.createElement(AddonsComponent,
|
||||
{client: this.client}), document.querySelector("#addons"));
|
||||
} else if (category == "workers") {
|
||||
React.render(React.createElement(WorkersComponent, { client: this.client }),
|
||||
document.querySelector("#workers"));
|
||||
this._component = React.render(React.createElement(WorkersComponent,
|
||||
{client: this.client}), document.querySelector("#workers"));
|
||||
}
|
||||
},
|
||||
|
||||
@ -82,16 +85,22 @@ var AboutDebugging = {
|
||||
let elements = document.querySelectorAll("input[type=checkbox][data-pref]");
|
||||
Array.map(elements, element => {
|
||||
let pref = element.dataset.pref;
|
||||
|
||||
let updatePref = () => {
|
||||
Services.prefs.setBoolPref(pref, element.checked);
|
||||
};
|
||||
element.addEventListener("change", updatePref, false);
|
||||
let updateCheckbox = () => {
|
||||
|
||||
let onPreferenceChanged = () => {
|
||||
element.checked = Services.prefs.getBoolPref(pref);
|
||||
this.update();
|
||||
};
|
||||
Services.prefs.addObserver(pref, updateCheckbox, false);
|
||||
this._prefListeners.push([pref, updateCheckbox]);
|
||||
updateCheckbox();
|
||||
|
||||
Services.prefs.addObserver(pref, onPreferenceChanged, false);
|
||||
this._prefListeners.push([pref, onPreferenceChanged]);
|
||||
|
||||
// Initialize the current checkbox element.
|
||||
element.checked = Services.prefs.getBoolPref(pref);
|
||||
});
|
||||
|
||||
// Link buttons to their associated actions.
|
||||
@ -112,6 +121,12 @@ var AboutDebugging = {
|
||||
});
|
||||
},
|
||||
|
||||
update() {
|
||||
if (this._component) {
|
||||
this._component.setState({});
|
||||
}
|
||||
},
|
||||
|
||||
loadAddonFromFile() {
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(window,
|
||||
|
@ -41,8 +41,10 @@ exports.AddonsComponent = React.createClass({
|
||||
let client = this.props.client;
|
||||
let targets = this.state.extensions;
|
||||
let name = Strings.GetStringFromName("extensions");
|
||||
let debugDisabled = !Services.prefs.getBoolPref("devtools.chrome.enabled");
|
||||
return React.createElement("div", null,
|
||||
React.createElement(TargetListComponent, { name, targets, client })
|
||||
React.createElement(TargetListComponent,
|
||||
{ name, targets, client, debugDisabled })
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -23,8 +23,10 @@ exports.TargetListComponent = React.createClass({
|
||||
|
||||
render() {
|
||||
let client = this.props.client;
|
||||
let debugDisabled = this.props.debugDisabled;
|
||||
let targets = this.props.targets.sort(LocaleCompare).map(target => {
|
||||
return React.createElement(TargetComponent, { client, target });
|
||||
return React.createElement(TargetComponent,
|
||||
{ client, target, debugDisabled });
|
||||
});
|
||||
return (
|
||||
React.createElement("div", { id: this.props.id, className: "targets" },
|
||||
|
@ -54,6 +54,8 @@ exports.TargetComponent = React.createClass({
|
||||
|
||||
render() {
|
||||
let target = this.props.target;
|
||||
let debugDisabled = this.props.debugDisabled;
|
||||
|
||||
return React.createElement("div", { className: "target" },
|
||||
React.createElement("img", {
|
||||
className: "target-icon",
|
||||
@ -62,8 +64,11 @@ exports.TargetComponent = React.createClass({
|
||||
React.createElement("div", { className: "target-name" }, target.name),
|
||||
React.createElement("div", { className: "target-url" }, target.url)
|
||||
),
|
||||
React.createElement("button", { onClick: this.debug },
|
||||
Strings.GetStringFromName("debug"))
|
||||
React.createElement("button", {
|
||||
className: "debug-button",
|
||||
onClick: this.debug,
|
||||
disabled: debugDisabled,
|
||||
}, Strings.GetStringFromName("debug"))
|
||||
);
|
||||
},
|
||||
});
|
||||
|
@ -9,5 +9,6 @@ support-files =
|
||||
service-workers/empty-sw.js
|
||||
|
||||
[browser_addons_install.js]
|
||||
[browser_addons_toggle_debug.js]
|
||||
[browser_service_workers.js]
|
||||
[browser_service_workers_timeout.js]
|
||||
|
@ -2,33 +2,13 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
var {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
|
||||
const ADDON_ID = "test-devtools@mozilla.org";
|
||||
const ADDON_NAME = "test-devtools";
|
||||
|
||||
add_task(function *() {
|
||||
let { tab, document } = yield openAboutDebugging("addons");
|
||||
|
||||
// Mock the file picker to select a test addon
|
||||
let MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(null);
|
||||
let file = get_supports_file("addons/unpacked/install.rdf");
|
||||
MockFilePicker.returnFiles = [file.file];
|
||||
|
||||
// Wait for a message sent by the addon's bootstrap.js file
|
||||
let promise = new Promise(done => {
|
||||
Services.obs.addObserver(function listener() {
|
||||
Services.obs.removeObserver(listener, "test-devtools", false);
|
||||
ok(true, "Addon installed and running its bootstrap.js file");
|
||||
done();
|
||||
}, "test-devtools", false);
|
||||
});
|
||||
// Trigger the file picker by clicking on the button
|
||||
document.getElementById("load-addon-from-file").click();
|
||||
|
||||
// Wait for the addon execution
|
||||
yield promise;
|
||||
yield installAddon(document, "addons/unpacked/install.rdf", "test-devtools");
|
||||
|
||||
// Check that the addon appears in the UI
|
||||
let names = [...document.querySelectorAll("#addons .target-name")];
|
||||
@ -36,21 +16,7 @@ add_task(function *() {
|
||||
ok(names.includes(ADDON_NAME), "The addon name appears in the list of addons: " + names);
|
||||
|
||||
// Now uninstall this addon
|
||||
yield new Promise(done => {
|
||||
AddonManager.getAddonByID(ADDON_ID, addon => {
|
||||
let listener = {
|
||||
onUninstalled: function(aUninstalledAddon) {
|
||||
if (aUninstalledAddon != addon) {
|
||||
return;
|
||||
}
|
||||
AddonManager.removeAddonListener(listener);
|
||||
done();
|
||||
}
|
||||
};
|
||||
AddonManager.addAddonListener(listener);
|
||||
addon.uninstall();
|
||||
});
|
||||
});
|
||||
yield uninstallAddon(ADDON_ID);
|
||||
|
||||
// Ensure that the UI removes the addon from the list
|
||||
names = [...document.querySelectorAll("#addons .target-name")];
|
||||
|
@ -0,0 +1,58 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that individual Debug buttons are disabled when "Addons debugging"
|
||||
// is disabled.
|
||||
// Test that the buttons are updated dynamically if the preference changes.
|
||||
|
||||
const ADDON_ID = "test-devtools@mozilla.org";
|
||||
|
||||
add_task(function* () {
|
||||
info("Turn off addon debugging.");
|
||||
yield new Promise(resolve => {
|
||||
let options = {"set": [
|
||||
["devtools.chrome.enabled", false],
|
||||
]};
|
||||
SpecialPowers.pushPrefEnv(options, resolve);
|
||||
});
|
||||
|
||||
let { tab, document } = yield openAboutDebugging("addons");
|
||||
|
||||
info("Install a test addon.");
|
||||
yield installAddon(document, "addons/unpacked/install.rdf", "test-devtools");
|
||||
|
||||
let addonDebugCheckbox = document.querySelector("#enable-addon-debugging");
|
||||
ok(!addonDebugCheckbox.checked, "Addons debugging should be disabled.");
|
||||
|
||||
info("Check all debug buttons are disabled.");
|
||||
let debugButtons = [...document.querySelectorAll("#addons .debug-button")];
|
||||
ok(debugButtons.every(b => b.disabled), "Debug buttons should be disabled");
|
||||
|
||||
info("Click on 'Enable addons debugging' checkbox.");
|
||||
let addonsContainer = document.getElementById("addons");
|
||||
let onAddonsMutation = waitForMutation(addonsContainer,
|
||||
{ subtree: true, attributes: true });
|
||||
addonDebugCheckbox.click();
|
||||
yield onAddonsMutation;
|
||||
|
||||
info("Check all debug buttons are enabled.");
|
||||
ok(addonDebugCheckbox.checked, "Addons debugging should be enabled.");
|
||||
debugButtons = [...document.querySelectorAll("#addons .debug-button")];
|
||||
ok(debugButtons.every(b => !b.disabled), "Debug buttons should be enabled");
|
||||
|
||||
info("Click again on 'Enable addons debugging' checkbox.");
|
||||
onAddonsMutation = waitForMutation(addonsContainer,
|
||||
{ subtree: true, attributes: true });
|
||||
addonDebugCheckbox.click();
|
||||
yield onAddonsMutation;
|
||||
|
||||
info("Check all debug buttons are disabled again.");
|
||||
debugButtons = [...document.querySelectorAll("#addons .debug-button")];
|
||||
ok(debugButtons.every(b => b.disabled), "Debug buttons should be disabled");
|
||||
|
||||
info("Uninstall addon installed earlier.");
|
||||
yield uninstallAddon(ADDON_ID);
|
||||
|
||||
yield closeAboutDebugging(tab);
|
||||
});
|
@ -9,17 +9,6 @@ const HTTP_ROOT = CHROME_ROOT.replace("chrome://mochitests/content/",
|
||||
const SERVICE_WORKER = HTTP_ROOT + "service-workers/empty-sw.js";
|
||||
const TAB_URL = HTTP_ROOT + "service-workers/empty-sw.html";
|
||||
|
||||
function waitForWorkersUpdate(document) {
|
||||
return new Promise(done => {
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
observer.disconnect();
|
||||
done();
|
||||
});
|
||||
var target = document.getElementById("service-workers");
|
||||
observer.observe(target, { childList: true });
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function *() {
|
||||
yield new Promise(done => {
|
||||
let options = {"set": [
|
||||
@ -32,7 +21,8 @@ add_task(function *() {
|
||||
|
||||
let swTab = yield addTab(TAB_URL);
|
||||
|
||||
yield waitForWorkersUpdate(document);
|
||||
let serviceWorkersElement = document.getElementById("service-workers");
|
||||
yield waitForMutation(serviceWorkersElement, { childList: true });
|
||||
|
||||
// Check that the service worker appears in the UI
|
||||
let names = [...document.querySelectorAll("#service-workers .target-name")];
|
||||
@ -40,7 +30,8 @@ add_task(function *() {
|
||||
ok(names.includes(SERVICE_WORKER), "The service worker url appears in the list: " + names);
|
||||
|
||||
// Finally, unregister the service worker itself
|
||||
let aboutDebuggingUpdate = waitForWorkersUpdate(document);
|
||||
let aboutDebuggingUpdate = waitForMutation(serviceWorkersElement,
|
||||
{ childList: true });
|
||||
|
||||
// Use message manager to work with e10s
|
||||
let frameScript = function () {
|
||||
|
@ -11,17 +11,6 @@ const TAB_URL = HTTP_ROOT + "service-workers/empty-sw.html";
|
||||
|
||||
const SW_TIMEOUT = 1000;
|
||||
|
||||
function waitForWorkersUpdate(document) {
|
||||
return new Promise(done => {
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
observer.disconnect();
|
||||
done();
|
||||
});
|
||||
var target = document.getElementById("service-workers");
|
||||
observer.observe(target, { childList: true });
|
||||
});
|
||||
}
|
||||
|
||||
function assertHasWorker(expected, document, type, name) {
|
||||
let names = [...document.querySelectorAll("#" + type + " .target-name")];
|
||||
names = names.map(element => element.textContent);
|
||||
@ -45,7 +34,8 @@ add_task(function *() {
|
||||
|
||||
let swTab = yield addTab(TAB_URL);
|
||||
|
||||
yield waitForWorkersUpdate(document);
|
||||
let serviceWorkersElement = document.getElementById("service-workers");
|
||||
yield waitForMutation(serviceWorkersElement, { childList: true });
|
||||
|
||||
assertHasWorker(true, document, "service-workers", SERVICE_WORKER);
|
||||
|
||||
@ -100,7 +90,7 @@ add_task(function *() {
|
||||
// Now ensure that the worker is correctly destroyed
|
||||
// after we destroy the toolbox.
|
||||
// The list should update once it get destroyed.
|
||||
yield waitForWorkersUpdate(document);
|
||||
yield waitForMutation(serviceWorkersElement, { childList: true });
|
||||
|
||||
assertHasWorker(false, document, "service-workers", SERVICE_WORKER);
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
|
||||
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
const Services = require("Services");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
DevToolsUtils.testing = true;
|
||||
@ -27,7 +28,6 @@ function openAboutDebugging(page) {
|
||||
return {
|
||||
tab,
|
||||
document: browser.contentDocument,
|
||||
window: browser.contentWindow
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -80,3 +80,61 @@ function get_supports_file(path) {
|
||||
let fileurl = cr.convertChromeURL(Services.io.newURI(CHROME_ROOT + path, null, null));
|
||||
return fileurl.QueryInterface(Ci.nsIFileURL);
|
||||
}
|
||||
|
||||
function installAddon(document, path, evt) {
|
||||
// Mock the file picker to select a test addon
|
||||
let MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(null);
|
||||
let file = get_supports_file(path);
|
||||
MockFilePicker.returnFiles = [file.file];
|
||||
|
||||
// Wait for a message sent by the addon's bootstrap.js file
|
||||
let onAddonInstalled = new Promise(done => {
|
||||
Services.obs.addObserver(function listener() {
|
||||
Services.obs.removeObserver(listener, evt, false);
|
||||
ok(true, "Addon installed and running its bootstrap.js file");
|
||||
done();
|
||||
}, evt, false);
|
||||
});
|
||||
// Trigger the file picker by clicking on the button
|
||||
document.getElementById("load-addon-from-file").click();
|
||||
|
||||
// Wait for the addon execution
|
||||
return onAddonInstalled;
|
||||
}
|
||||
|
||||
function uninstallAddon(addonId) {
|
||||
// Now uninstall this addon
|
||||
return new Promise(done => {
|
||||
AddonManager.getAddonByID(addonId, addon => {
|
||||
let listener = {
|
||||
onUninstalled: function(uninstalledAddon) {
|
||||
if (uninstalledAddon != addon) {
|
||||
return;
|
||||
}
|
||||
AddonManager.removeAddonListener(listener);
|
||||
done();
|
||||
}
|
||||
};
|
||||
AddonManager.addAddonListener(listener);
|
||||
addon.uninstall();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will resolve after receiving a mutation matching the
|
||||
* provided mutation options on the provided target.
|
||||
* @param {Node} target
|
||||
* @param {Object} mutationOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
function waitForMutation(target, mutationOptions) {
|
||||
return new Promise(resolve => {
|
||||
let observer = new MutationObserver(() => {
|
||||
observer.disconnect();
|
||||
resolve();
|
||||
});
|
||||
observer.observe(target, mutationOptions);
|
||||
});
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ support-files =
|
||||
mockCommands.js
|
||||
|
||||
[browser_cmd_addon.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_cmd_calllog.js]
|
||||
skip-if = true || e10s # Bug 845831
|
||||
[browser_cmd_calllog_chrome.js]
|
||||
|
@ -533,7 +533,7 @@ skip-if = e10s && debug
|
||||
[browser_dbg_variables-view-frame-with.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_variables-view-frozen-sealed-nonext.js]
|
||||
skip-if = e10s && debug || buildapp == 'mulet'
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_variables-view-hide-non-enums.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_variables-view-large-array-buffer.js]
|
||||
|
@ -14,7 +14,6 @@ var gFirstSourceLabel = "code_ugly-5.js";
|
||||
var gSecondSourceLabel = "code_ugly-6.js";
|
||||
|
||||
var gOriginalPref = Services.prefs.getBoolPref("devtools.debugger.auto-pretty-print");
|
||||
Services.prefs.setBoolPref("devtools.debugger.auto-pretty-print", true);
|
||||
|
||||
function test(){
|
||||
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
|
||||
@ -27,28 +26,31 @@ function test(){
|
||||
gOptions = gDebugger.DebuggerView.Options;
|
||||
gView = gDebugger.DebuggerView;
|
||||
|
||||
// Should be on by default.
|
||||
Task.spawn(function*() {
|
||||
yield waitForSourceShown(gPanel, gFirstSourceLabel);
|
||||
testSourceIsUgly();
|
||||
|
||||
enableAutoPrettyPrint();
|
||||
testAutoPrettyPrintOn();
|
||||
|
||||
waitForSourceShown(gPanel, gFirstSourceLabel)
|
||||
.then(testSourceIsUgly)
|
||||
.then(() => waitForSourceShown(gPanel, gFirstSourceLabel))
|
||||
.then(testSourceIsPretty)
|
||||
.then(disableAutoPrettyPrint)
|
||||
.then(testAutoPrettyPrintOff)
|
||||
.then(() => {
|
||||
reload(gPanel);
|
||||
yield waitForSourceShown(gPanel, gFirstSourceLabel);
|
||||
testSourceIsUgly();
|
||||
yield waitForSourceShown(gPanel, gFirstSourceLabel);
|
||||
testSourceIsPretty();
|
||||
disableAutoPrettyPrint();
|
||||
testAutoPrettyPrintOff();
|
||||
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN);
|
||||
gSources.selectedIndex = 1;
|
||||
return finished;
|
||||
})
|
||||
.then(testSecondSourceLabel)
|
||||
.then(testSourceIsUgly)
|
||||
// Re-enable auto pretty printing for browser_dbg_auto-pretty-print-02.js
|
||||
.then(enableAutoPrettyPrint)
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(aError));
|
||||
})
|
||||
yield finished;
|
||||
|
||||
testSecondSourceLabel();
|
||||
testSourceIsUgly();
|
||||
|
||||
enableAutoPrettyPrint();
|
||||
yield closeDebuggerAndFinish(gPanel);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ function test() {
|
||||
const textbox = gDebugger.document.getElementById("conditional-breakpoint-panel-textbox");
|
||||
is(textbox.value, "hello", "The expression is correct (2).")
|
||||
|
||||
resumeDebuggerThenCloseAndFinish(gPanel);
|
||||
yield resumeDebuggerThenCloseAndFinish(gPanel);
|
||||
});
|
||||
|
||||
callInTab(gTab, "ermahgerd");
|
||||
|
@ -10,5 +10,5 @@
|
||||
// etc...
|
||||
// etc...
|
||||
function foo(){var a=1;var b=2;bar(a,b);}
|
||||
function bar(c,d){debugger;}
|
||||
function bar(c,d){return 3;}
|
||||
foo();
|
@ -588,9 +588,9 @@ AddonDebugger.prototype = {
|
||||
info("Addon debugger panel shown successfully.");
|
||||
|
||||
this.debuggerPanel = toolbox.getCurrentPanel();
|
||||
yield waitForSourceShown(this.debuggerPanel, '');
|
||||
|
||||
// Wait for the initial resume...
|
||||
yield prepareDebugger(this.debuggerPanel);
|
||||
prepareDebugger(this.debuggerPanel);
|
||||
yield this._attachConsole();
|
||||
}),
|
||||
|
||||
|
@ -116,7 +116,7 @@ var SourceUtils = {
|
||||
clearCache: function() {
|
||||
this._labelsCache.clear();
|
||||
this._groupsCache.clear();
|
||||
this._minifiedCache = new WeakMap();
|
||||
this._minifiedCache.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,7 @@ support-files =
|
||||
[browser_target_remote.js]
|
||||
[browser_target_support.js]
|
||||
[browser_two_tabs.js]
|
||||
skip-if = e10s && debug && os == 'win'
|
||||
skip-if = e10s && debug && os == 'win' # Bug 1231869
|
||||
[browser_toolbox_dynamic_registration.js]
|
||||
[browser_toolbox_getpanelwhenready.js]
|
||||
[browser_toolbox_highlight.js]
|
||||
@ -40,11 +40,8 @@ skip-if = true # Bug 1177463 - Temporarily hide the minimize button
|
||||
[browser_toolbox_options.js]
|
||||
[browser_toolbox_options_disable_buttons.js]
|
||||
[browser_toolbox_options_disable_cache-01.js]
|
||||
skip-if = e10s # Bug 1030318
|
||||
[browser_toolbox_options_disable_cache-02.js]
|
||||
skip-if = e10s # Bug 1030318
|
||||
[browser_toolbox_options_disable_js.js]
|
||||
skip-if = e10s # Bug 1030318
|
||||
# [browser_toolbox_raise.js] # Bug 962258
|
||||
# skip-if = os == "win"
|
||||
[browser_toolbox_ready.js]
|
||||
|
@ -57,29 +57,21 @@ function testJSEnabledIframe(secondPass) {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleJS() {
|
||||
let deferred = promise.defer();
|
||||
let toggleJS = Task.async(function*() {
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
let cbx = panel.panelDoc.getElementById("devtools-disable-javascript");
|
||||
|
||||
cbx.scrollIntoView();
|
||||
|
||||
if (cbx.checked) {
|
||||
info("Clearing checkbox to re-enable JS");
|
||||
} else {
|
||||
info("Checking checkbox to disable JS");
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||
doc = content.document;
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
let browserLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
cbx.click();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
yield browserLoaded;
|
||||
doc = content.document;
|
||||
});
|
||||
|
||||
function testJSDisabled() {
|
||||
info("Testing that JS is disabled");
|
||||
|
@ -48,15 +48,19 @@ function* checkCacheEnabled(tabX, expected) {
|
||||
|
||||
yield reloadTab(tabX);
|
||||
|
||||
let oldGuid = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
|
||||
let doc = content.document;
|
||||
let h1 = doc.querySelector("h1");
|
||||
let oldGuid = h1.textContent;
|
||||
return h1.textContent;
|
||||
});
|
||||
|
||||
yield reloadTab(tabX);
|
||||
|
||||
doc = content.document;
|
||||
h1 = doc.querySelector("h1");
|
||||
let guid = h1.textContent;
|
||||
let guid = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
|
||||
let doc = content.document;
|
||||
let h1 = doc.querySelector("h1");
|
||||
return h1.textContent;
|
||||
});
|
||||
|
||||
if (expected) {
|
||||
is(guid, oldGuid, tabX.title + " cache is enabled");
|
||||
@ -71,14 +75,9 @@ function* setDisableCacheCheckboxChecked(tabX, state) {
|
||||
let panel = tabX.toolbox.getCurrentPanel();
|
||||
let cbx = panel.panelDoc.getElementById("devtools-disable-cache");
|
||||
|
||||
cbx.scrollIntoView();
|
||||
|
||||
// After uising scrollIntoView() we need to wait for the browser to scroll.
|
||||
yield waitForTick();
|
||||
|
||||
if (cbx.checked !== state) {
|
||||
info("Setting disable cache checkbox to " + state + " for " + tabX.title);
|
||||
EventUtils.synthesizeMouseAtCenter(cbx, {}, panel.panelWin);
|
||||
cbx.click();
|
||||
|
||||
// We need to wait for all checkboxes to be updated and the docshells to
|
||||
// apply the new cache settings.
|
||||
|
@ -962,12 +962,13 @@ CssRuleView.prototype = {
|
||||
}
|
||||
|
||||
this._clearRules();
|
||||
this._createEditors();
|
||||
|
||||
let onEditorsReady = this._createEditors();
|
||||
this.refreshPseudoClassPanel();
|
||||
|
||||
// Notify anyone that cares that we refreshed.
|
||||
return onEditorsReady.then(() => {
|
||||
this.emit("ruleview-refreshed");
|
||||
}, e => console.error(e));
|
||||
}).then(null, promiseWarn);
|
||||
},
|
||||
|
||||
@ -1147,9 +1148,10 @@ CssRuleView.prototype = {
|
||||
let container = null;
|
||||
|
||||
if (!this._elementStyle.rules) {
|
||||
return;
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
let editorReadyPromises = [];
|
||||
for (let rule of this._elementStyle.rules) {
|
||||
if (rule.domRule.system) {
|
||||
continue;
|
||||
@ -1158,6 +1160,7 @@ CssRuleView.prototype = {
|
||||
// Initialize rule editor
|
||||
if (!rule.editor) {
|
||||
rule.editor = new RuleEditor(this, rule);
|
||||
editorReadyPromises.push(rule.editor.once("source-link-updated"));
|
||||
}
|
||||
|
||||
// Filter the rules and highlight any matches if there is a search input
|
||||
@ -1211,6 +1214,8 @@ CssRuleView.prototype = {
|
||||
} else {
|
||||
this.searchField.classList.remove("devtools-style-searchbox-no-match");
|
||||
}
|
||||
|
||||
return promise.all(editorReadyPromises);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -110,7 +110,7 @@ skip-if = e10s # Bug 1039528: "inspect element" contextual-menu doesn't work wit
|
||||
[browser_rules_filtereditor-appears-on-swatch-click.js]
|
||||
[browser_rules_filtereditor-commit-on-ENTER.js]
|
||||
[browser_rules_filtereditor-revert-on-ESC.js]
|
||||
skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
|
||||
skip-if = (os == "win" && debug) # bug 963492: win.
|
||||
[browser_rules_guessIndentation.js]
|
||||
[browser_rules_inherited-properties_01.js]
|
||||
[browser_rules_inherited-properties_02.js]
|
||||
@ -139,7 +139,6 @@ skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
|
||||
[browser_rules_original-source-link.js]
|
||||
[browser_rules_pseudo-element_01.js]
|
||||
[browser_rules_pseudo-element_02.js]
|
||||
skip-if = e10s # Bug 1090340
|
||||
[browser_rules_pseudo_lock_options.js]
|
||||
[browser_rules_refresh-no-flicker.js]
|
||||
[browser_rules_refresh-on-attribute-change_01.js]
|
||||
@ -173,7 +172,6 @@ skip-if = e10s # Bug 1090340
|
||||
[browser_rules_strict-search-filter_02.js]
|
||||
[browser_rules_strict-search-filter_03.js]
|
||||
[browser_rules_style-editor-link.js]
|
||||
skip-if = e10s # bug 1040670 Cannot open inline styles in viewSourceUtils
|
||||
[browser_rules_urls-clickable.js]
|
||||
[browser_rules_user-agent-styles.js]
|
||||
[browser_rules_user-agent-styles-uneditable.js]
|
||||
|
@ -19,23 +19,15 @@ function* testPressingEscapeRevertsChanges(view) {
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||
let swatch = propEditor.valueSpan.querySelector(".ruleview-filterswatch");
|
||||
let filterTooltip = view.tooltips.filterEditor;
|
||||
|
||||
let onShow = filterTooltip.tooltip.once("shown");
|
||||
swatch.click();
|
||||
yield onShow;
|
||||
|
||||
let widget = yield filterTooltip.widget;
|
||||
widget.setCssValue("blur(2px)");
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
yield clickOnFilterSwatch(swatch, view);
|
||||
yield setValueInFilterWidget("blur(2px)", view);
|
||||
|
||||
yield waitForComputedStyleProperty("body", null, "filter", "blur(2px)");
|
||||
is(propEditor.valueSpan.textContent, "blur(2px)",
|
||||
"Got expected property value.");
|
||||
|
||||
info("Pressing ESCAPE to close the tooltip");
|
||||
EventUtils.sendKey("ESCAPE", widget.styleWindow);
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
yield pressEscapeToCloseTooltip(view);
|
||||
|
||||
yield waitForComputedStyleProperty("body", null, "filter",
|
||||
"blur(2px) contrast(2)");
|
||||
@ -47,11 +39,11 @@ function* testPressingEscapeRevertsChangesAndDisables(view) {
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||
let swatch = propEditor.valueSpan.querySelector(".ruleview-filterswatch");
|
||||
let filterTooltip = view.tooltips.filterEditor;
|
||||
|
||||
info("Disabling filter property");
|
||||
let onRuleViewChanged = view.once("ruleview-changed");
|
||||
propEditor.enable.click();
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
yield onRuleViewChanged;
|
||||
|
||||
ok(propEditor.element.classList.contains("ruleview-overridden"),
|
||||
"property is overridden.");
|
||||
@ -64,22 +56,15 @@ function* testPressingEscapeRevertsChangesAndDisables(view) {
|
||||
let newValue = yield getRulePropertyValue("filter");
|
||||
is(newValue, "", "filter should have been unset.");
|
||||
|
||||
let onShow = filterTooltip.tooltip.once("shown");
|
||||
swatch.click();
|
||||
yield onShow;
|
||||
yield clickOnFilterSwatch(swatch, view);
|
||||
|
||||
ok(!propEditor.element.classList.contains("ruleview-overridden"),
|
||||
"property overridden is not displayed.");
|
||||
is(propEditor.enable.style.visibility, "hidden",
|
||||
"property enable checkbox is hidden.");
|
||||
|
||||
let widget = yield filterTooltip.widget;
|
||||
widget.setCssValue("blur(2px)");
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
info("Pressing ESCAPE to close the tooltip");
|
||||
EventUtils.sendKey("ESCAPE", widget.styleWindow);
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
yield setValueInFilterWidget("blur(2px)", view);
|
||||
yield pressEscapeToCloseTooltip(view);
|
||||
|
||||
ok(propEditor.element.classList.contains("ruleview-overridden"),
|
||||
"property is overridden.");
|
||||
@ -102,3 +87,34 @@ function* getRulePropertyValue(name) {
|
||||
});
|
||||
return propValue;
|
||||
}
|
||||
|
||||
function* clickOnFilterSwatch(swatch, view) {
|
||||
info("Clicking on a css filter swatch to open the tooltip");
|
||||
|
||||
// Clicking on a cssfilter swatch sets the current filter value in the tooltip
|
||||
// which, in turn, makes the FilterWidget emit an "updated" event that causes
|
||||
// the rule-view to refresh. So we must wait for the ruleview-changed event.
|
||||
let onRuleViewChanged = view.once("ruleview-changed");
|
||||
swatch.click();
|
||||
yield onRuleViewChanged;
|
||||
}
|
||||
|
||||
function* setValueInFilterWidget(value, view) {
|
||||
info("Setting the CSS filter value in the tooltip");
|
||||
|
||||
let filterTooltip = view.tooltips.filterEditor;
|
||||
let widget = yield filterTooltip.widget;
|
||||
let onRuleViewChanged = view.once("ruleview-changed");
|
||||
widget.setCssValue(value);
|
||||
yield onRuleViewChanged;
|
||||
}
|
||||
|
||||
function* pressEscapeToCloseTooltip(view) {
|
||||
info("Pressing ESCAPE to close the tooltip");
|
||||
|
||||
let filterTooltip = view.tooltips.filterEditor;
|
||||
let widget = yield filterTooltip.widget;
|
||||
let onRuleViewChanged = view.once("ruleview-changed");
|
||||
EventUtils.sendKey("ESCAPE", widget.styleWindow);
|
||||
yield onRuleViewChanged;
|
||||
}
|
||||
|
@ -11,11 +11,8 @@ const TEST_URI = URL_ROOT + "doc_pseudoelement.html";
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URI);
|
||||
let {inspector} = yield openRuleView();
|
||||
yield testTopLeft(inspector);
|
||||
});
|
||||
|
||||
function* testTopLeft(inspector) {
|
||||
let node = inspector.markup.walker.frontForRawNode(getNode("#topleft"));
|
||||
let node = yield getNodeFront("#topleft", inspector);
|
||||
let children = yield inspector.markup.walker.children(node);
|
||||
|
||||
is(children.nodes.length, 3, "Element has correct number of children");
|
||||
@ -29,5 +26,4 @@ function* testTopLeft(inspector) {
|
||||
is(afterElement.tagName, "_moz_generated_content_after",
|
||||
"tag name is correct");
|
||||
yield selectNode(afterElement, inspector);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -25,6 +25,8 @@ const {
|
||||
SELECTOR_ELEMENT,
|
||||
SELECTOR_PSEUDO_CLASS
|
||||
} = require("devtools/client/shared/css-parsing-utils");
|
||||
const promise = require("promise");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_strings", function() {
|
||||
return Services.strings.createBundle(
|
||||
@ -40,12 +42,20 @@ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
* for its TextProperties.
|
||||
* Manages creation of new text properties.
|
||||
*
|
||||
* One step of a RuleEditor's instantiation is figuring out what's the original
|
||||
* source link to the parent stylesheet (in case of source maps). This step is
|
||||
* asynchronous and is triggered as soon as the RuleEditor is instantiated (see
|
||||
* updateSourceLink). If you need to know when the RuleEditor is done with this,
|
||||
* you need to listen to the source-link-updated event.
|
||||
*
|
||||
* @param {CssRuleView} ruleView
|
||||
* The CssRuleView containg the document holding this rule editor.
|
||||
* @param {Rule} rule
|
||||
* The Rule object we're editing.
|
||||
*/
|
||||
function RuleEditor(ruleView, rule) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this.ruleView = ruleView;
|
||||
this.doc = this.ruleView.styleDocument;
|
||||
this.rule = rule;
|
||||
@ -235,10 +245,21 @@ RuleEditor.prototype = {
|
||||
let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
|
||||
if (showOrig && !this.rule.isSystem &&
|
||||
this.rule.domRule.type !== ELEMENT_STYLE) {
|
||||
// Only get the original source link if the right pref is set, if the rule
|
||||
// isn't a system rule and if it isn't an inline rule.
|
||||
this.rule.getOriginalSourceStrings().then((strings) => {
|
||||
sourceLabel.setAttribute("value", strings.short);
|
||||
sourceLabel.setAttribute("tooltiptext", strings.full);
|
||||
}, console.error);
|
||||
}, e => console.error(e)).then(() => {
|
||||
this.emit("source-link-updated");
|
||||
});
|
||||
} else {
|
||||
// If we're not getting the original source link, then we can emit the
|
||||
// event immediately (but still asynchronously to give consumers a chance
|
||||
// to register it after having instantiated the RuleEditor).
|
||||
promise.resolve().then(() => {
|
||||
this.emit("source-link-updated");
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -54,6 +54,7 @@ tree.labels.cookies=Cookies
|
||||
tree.labels.localStorage=Local Storage
|
||||
tree.labels.sessionStorage=Session Storage
|
||||
tree.labels.indexedDB=Indexed DB
|
||||
tree.labels.Cache=Cache Storage
|
||||
|
||||
# LOCALIZATION NOTE (table.headers.*.*):
|
||||
# These strings are the header names of the columns in the Storage Table for
|
||||
@ -84,6 +85,9 @@ table.headers.localStorage.value=Value
|
||||
table.headers.sessionStorage.name=Key
|
||||
table.headers.sessionStorage.value=Value
|
||||
|
||||
table.headers.Cache.url=URL
|
||||
table.headers.Cache.status=Status
|
||||
|
||||
table.headers.indexedDB.name=Key
|
||||
table.headers.indexedDB.db=Database Name
|
||||
table.headers.indexedDB.objectStore=Object Store Name
|
||||
|
@ -49,7 +49,6 @@ support-files =
|
||||
[browser_net_charts-01.js]
|
||||
[browser_net_charts-02.js]
|
||||
[browser_net_charts-03.js]
|
||||
skip-if= buildapp == 'mulet'
|
||||
[browser_net_charts-04.js]
|
||||
[browser_net_charts-05.js]
|
||||
[browser_net_charts-06.js]
|
||||
|
@ -7,7 +7,6 @@ support-files =
|
||||
helper_edits.js
|
||||
|
||||
[browser_projecteditor_app_options.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_projecteditor_confirm_unsaved.js]
|
||||
[browser_projecteditor_contextmenu_01.js]
|
||||
[browser_projecteditor_contextmenu_02.js]
|
||||
@ -15,9 +14,7 @@ skip-if = true # Bug 1173950
|
||||
[browser_projecteditor_delete_file.js]
|
||||
skip-if = e10s # Frequent failures in e10s - Bug 1020027
|
||||
[browser_projecteditor_rename_file.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_projecteditor_editing_01.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_projecteditor_editors_image.js]
|
||||
[browser_projecteditor_external_change.js]
|
||||
[browser_projecteditor_immediate_destroy.js]
|
||||
@ -27,7 +24,6 @@ skip-if = buildapp == 'mulet'
|
||||
skip-if = true # Bug 1173950
|
||||
[browser_projecteditor_new_file.js]
|
||||
[browser_projecteditor_saveall.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_projecteditor_stores.js]
|
||||
[browser_projecteditor_tree_selection_01.js]
|
||||
[browser_projecteditor_tree_selection_02.js]
|
||||
|
@ -8,7 +8,6 @@ support-files = head.js
|
||||
[browser_scratchpad_reset_undo.js]
|
||||
[browser_scratchpad_display_outputs_errors.js]
|
||||
[browser_scratchpad_eval_func.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_scratchpad_goto_line_ui.js]
|
||||
[browser_scratchpad_reload_and_run.js]
|
||||
[browser_scratchpad_display_non_error_exceptions.js]
|
||||
|
@ -129,7 +129,6 @@ skip-if = e10s # Test intermittently fails with e10s. Bug 1124162.
|
||||
[browser_tableWidget_basic.js]
|
||||
[browser_tableWidget_keyboard_interaction.js]
|
||||
[browser_tableWidget_mouse_interaction.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_telemetry_button_eyedropper.js]
|
||||
[browser_telemetry_button_paintflashing.js]
|
||||
skip-if = e10s # Bug 937167 - e10s paintflashing
|
||||
@ -156,7 +155,7 @@ skip-if = e10s # Bug 1086492 - Disable tilt for e10s
|
||||
[browser_toolbar_basic.js]
|
||||
[browser_toolbar_tooltip.js]
|
||||
[browser_toolbar_webconsole_errors_count.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # The developertoolbar error count isn't correct with e10s
|
||||
skip-if = e10s # The developertoolbar error count isn't correct with e10s
|
||||
[browser_treeWidget_basic.js]
|
||||
[browser_treeWidget_keyboard_interaction.js]
|
||||
[browser_treeWidget_mouse_interaction.js]
|
||||
|
@ -57,7 +57,10 @@ StoragePanel.prototype = {
|
||||
this.emit("ready");
|
||||
|
||||
return this;
|
||||
}).catch(this.destroy);
|
||||
}).catch(e => {
|
||||
console.log("error while opening storage panel", e);
|
||||
this.destroy();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -61,6 +61,9 @@ const testCases = [
|
||||
[6, 7]],
|
||||
[["indexedDB", "https://sectest1.example.org", "idb-s2", "obj-s2"],
|
||||
[16]],
|
||||
[["Cache", "http://test1.example.org", "plop"],
|
||||
[MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]],
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -14,6 +14,8 @@ const SPLIT_CONSOLE_PREF = "devtools.toolbox.splitconsoleEnabled";
|
||||
const STORAGE_PREF = "devtools.storage.enabled";
|
||||
const DUMPEMIT_PREF = "devtools.dump.emit";
|
||||
const DEBUGGERLOG_PREF = "devtools.debugger.log";
|
||||
// Allows Cache API to be working on usage `http` test page
|
||||
const CACHES_ON_HTTP_PREF = "dom.caches.testing.enabled";
|
||||
const PATH = "browser/devtools/client/storage/test/";
|
||||
const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
|
||||
const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
|
||||
@ -27,6 +29,7 @@ var gToolbox, gPanelWindow, gWindow, gUI;
|
||||
// Services.prefs.setBoolPref(DEBUGGERLOG_PREF, true);
|
||||
|
||||
Services.prefs.setBoolPref(STORAGE_PREF, true);
|
||||
Services.prefs.setBoolPref(CACHES_ON_HTTP_PREF, true);
|
||||
DevToolsUtils.testing = true;
|
||||
registerCleanupFunction(() => {
|
||||
gToolbox = gPanelWindow = gWindow = gUI = null;
|
||||
@ -34,6 +37,7 @@ registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref(SPLIT_CONSOLE_PREF);
|
||||
Services.prefs.clearUserPref(DUMPEMIT_PREF);
|
||||
Services.prefs.clearUserPref(DEBUGGERLOG_PREF);
|
||||
Services.prefs.clearUserPref(CACHES_ON_HTTP_PREF);
|
||||
DevToolsUtils.testing = false;
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
@ -82,35 +86,20 @@ function addTab(url) {
|
||||
* @return {Promise} A promise that resolves after storage inspector is ready
|
||||
*/
|
||||
function* openTabAndSetupStorage(url) {
|
||||
/**
|
||||
* This method iterates over iframes in a window and setups the indexed db
|
||||
* required for this test.
|
||||
*/
|
||||
let setupIDBInFrames = (w, i, c) => {
|
||||
if (w[i] && w[i].idbGenerator) {
|
||||
w[i].setupIDB = w[i].idbGenerator(() => setupIDBInFrames(w, i + 1, c));
|
||||
w[i].setupIDB.next();
|
||||
} else if (w[i] && w[i + 1]) {
|
||||
setupIDBInFrames(w, i + 1, c);
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
};
|
||||
|
||||
let content = yield addTab(url);
|
||||
|
||||
let def = promise.defer();
|
||||
// Setup the indexed db in main window.
|
||||
gWindow = content.wrappedJSObject;
|
||||
if (gWindow.idbGenerator) {
|
||||
gWindow.setupIDB = gWindow.idbGenerator(() => {
|
||||
setupIDBInFrames(gWindow, 0, () => {
|
||||
def.resolve();
|
||||
});
|
||||
});
|
||||
gWindow.setupIDB.next();
|
||||
yield def.promise;
|
||||
|
||||
// Setup the async storages in main window and for all its iframes
|
||||
let callSetup = function*(win) {
|
||||
if (typeof(win.setup) == "function") {
|
||||
yield win.setup();
|
||||
}
|
||||
for(var i = 0; i < win.frames.length; i++) {
|
||||
yield callSetup(win.frames[i]);
|
||||
}
|
||||
}
|
||||
yield callSetup(gWindow);
|
||||
|
||||
// open storage inspector
|
||||
return yield openStoragePanel();
|
||||
@ -501,6 +490,7 @@ function* selectTreeItem(ids) {
|
||||
|
||||
let selector = "[data-id='" + JSON.stringify(ids) + "'] > .tree-widget-item";
|
||||
let target = gPanelWindow.document.querySelector(selector);
|
||||
ok(target, "tree item found with ids " + JSON.stringify(ids));
|
||||
|
||||
let updated = gUI.once("store-objects-updated");
|
||||
|
||||
@ -517,6 +507,7 @@ function* selectTreeItem(ids) {
|
||||
function* selectTableItem(id) {
|
||||
let selector = ".table-widget-cell[data-id='" + id + "']";
|
||||
let target = gPanelWindow.document.querySelector(selector);
|
||||
ok(target, "table item found with ids " + id);
|
||||
|
||||
yield click(target);
|
||||
yield gUI.once("sidebar-updated");
|
||||
|
@ -34,53 +34,63 @@ sessionStorage.setItem("ss2", "This~is~another~array");
|
||||
sessionStorage.setItem("ss3", "this#is~an#object~foo#bar");
|
||||
console.log("added cookies and stuff from main page");
|
||||
|
||||
function success(event) {
|
||||
setupIDB.next(event);
|
||||
}
|
||||
|
||||
window.idbGenerator = function*(callback) {
|
||||
let idbGenerator = function*() {
|
||||
let request = indexedDB.open("idb1", 1);
|
||||
request.onupgradeneeded = success;
|
||||
request.onsuccess = success;
|
||||
request.onerror = function() {
|
||||
throw new Error("error opening db connection");
|
||||
};
|
||||
let event = yield undefined;
|
||||
let db = yield new Promise(done => {
|
||||
request.onupgradeneeded = event => {
|
||||
let db = event.target.result;
|
||||
let store1 = db.createObjectStore("obj1", { keyPath: "id" });
|
||||
store1.createIndex("name", "name", { unique: false });
|
||||
store1.createIndex("email", "email", { unique: true });
|
||||
let store2 = db.createObjectStore("obj2", { keyPath: "id2" });
|
||||
db.createObjectStore("obj2", { keyPath: "id2" });
|
||||
store1.transaction.oncomplete = () => {
|
||||
done(db);
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
store1.add({id: 1, name: "foo", email: "foo@bar.com"}).onsuccess = success;
|
||||
yield undefined;
|
||||
store1.add({id: 2, name: "foo2", email: "foo2@bar.com"}).onsuccess = success;
|
||||
yield undefined;
|
||||
store1.add({id: 3, name: "foo2", email: "foo3@bar.com"}).onsuccess = success;
|
||||
yield undefined;
|
||||
// Prevents AbortError
|
||||
yield new Promise(done => {
|
||||
request.onsuccess = done;
|
||||
});
|
||||
|
||||
let transaction = db.transaction(["obj1", "obj2"], "readwrite");
|
||||
let store1 = transaction.objectStore("obj1");
|
||||
let store2 = transaction.objectStore("obj2");
|
||||
|
||||
store1.add({id: 1, name: "foo", email: "foo@bar.com"});
|
||||
store1.add({id: 2, name: "foo2", email: "foo2@bar.com"});
|
||||
store1.add({id: 3, name: "foo2", email: "foo3@bar.com"});
|
||||
store2.add({
|
||||
id2: 1,
|
||||
name: "foo",
|
||||
email: "foo@bar.com",
|
||||
extra: "baz"}).onsuccess = success;
|
||||
yield undefined;
|
||||
extra: "baz"});
|
||||
|
||||
yield undefined;
|
||||
db.close();
|
||||
|
||||
request = indexedDB.open("idb2", 1);
|
||||
request.onupgradeneeded = success;
|
||||
request.onsuccess = success;
|
||||
event = yield undefined;
|
||||
|
||||
let db2 = yield new Promise(done => {
|
||||
request.onupgradeneeded = event => {
|
||||
let db2 = event.target.result;
|
||||
let store3 = db2.createObjectStore("obj3", { keyPath: "id3" });
|
||||
store3.createIndex("name2", "name2", { unique: true });
|
||||
store3.transaction.oncomplete = () => {
|
||||
done(db2);
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
// Prevents AbortError during close()
|
||||
yield new Promise(done => {
|
||||
request.onsuccess = done;
|
||||
});
|
||||
|
||||
yield undefined;
|
||||
db2.close();
|
||||
console.log("added cookies and stuff from main page");
|
||||
callback();
|
||||
};
|
||||
|
||||
function deleteDB(dbName) {
|
||||
@ -90,6 +100,10 @@ function deleteDB(dbName) {
|
||||
});
|
||||
}
|
||||
|
||||
window.setup = function*() {
|
||||
yield idbGenerator();
|
||||
};
|
||||
|
||||
window.clear = function*() {
|
||||
document.cookie = "c1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/browser";
|
||||
document.cookie = "cs2=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
||||
|
@ -28,53 +28,66 @@ localStorage.setItem("ls2", "foobar-2");
|
||||
sessionStorage.setItem("ss1", "foobar-3");
|
||||
dump("added cookies and stuff from main page\n");
|
||||
|
||||
function success(event) {
|
||||
setupIDB.next(event);
|
||||
}
|
||||
|
||||
window.idbGenerator = function*(callback) {
|
||||
let idbGenerator = function*() {
|
||||
let request = indexedDB.open("idb1", 1);
|
||||
request.onupgradeneeded = success;
|
||||
request.onsuccess = success;
|
||||
request.onerror = function() {
|
||||
throw new Error("error opening db connection");
|
||||
};
|
||||
let event = yield undefined;
|
||||
let db = yield new Promise(done => {
|
||||
request.onupgradeneeded = event => {
|
||||
let db = event.target.result;
|
||||
let store1 = db.createObjectStore("obj1", { keyPath: "id" });
|
||||
store1.createIndex("name", "name", { unique: false });
|
||||
store1.createIndex("email", "email", { unique: true });
|
||||
let store2 = db.createObjectStore("obj2", { keyPath: "id2" });
|
||||
store1.transaction.oncomplete = () => {
|
||||
done(db);
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
store1.add({id: 1, name: "foo", email: "foo@bar.com"}).onsuccess = success;
|
||||
yield undefined;
|
||||
store1.add({id: 2, name: "foo2", email: "foo2@bar.com"}).onsuccess = success;
|
||||
yield undefined;
|
||||
store1.add({id: 3, name: "foo2", email: "foo3@bar.com"}).onsuccess = success;
|
||||
yield undefined;
|
||||
// Prevents AbortError
|
||||
yield new Promise(done => {
|
||||
request.onsuccess = done;
|
||||
});
|
||||
|
||||
let transaction = db.transaction(["obj1", "obj2"], "readwrite");
|
||||
let store1 = transaction.objectStore("obj1");
|
||||
let store2 = transaction.objectStore("obj2");
|
||||
store1.add({id: 1, name: "foo", email: "foo@bar.com"});
|
||||
store1.add({id: 2, name: "foo2", email: "foo2@bar.com"});
|
||||
store1.add({id: 3, name: "foo2", email: "foo3@bar.com"});
|
||||
store2.add({
|
||||
id2: 1,
|
||||
name: "foo",
|
||||
email: "foo@bar.com",
|
||||
extra: "baz"}).onsuccess = success;
|
||||
yield undefined;
|
||||
extra: "baz"
|
||||
});
|
||||
// Prevents AbortError during close()
|
||||
yield new Promise(success => {
|
||||
transaction.oncomplete = success;
|
||||
});
|
||||
|
||||
yield undefined;
|
||||
db.close();
|
||||
|
||||
request = indexedDB.open("idb2", 1);
|
||||
request.onupgradeneeded = success;
|
||||
request.onsuccess = success;
|
||||
event = yield undefined;
|
||||
|
||||
let db2 = yield new Promise(done => {
|
||||
request.onupgradeneeded = event => {
|
||||
let db2 = event.target.result;
|
||||
let store3 = db2.createObjectStore("obj3", { keyPath: "id3" });
|
||||
store3.createIndex("name2", "name2", { unique: true });
|
||||
|
||||
yield undefined;
|
||||
store3.transaction.oncomplete = () => {
|
||||
done(db2);
|
||||
}
|
||||
};
|
||||
});
|
||||
// Prevents AbortError during close()
|
||||
yield new Promise(done => {
|
||||
request.onsuccess = done;
|
||||
});
|
||||
db2.close();
|
||||
|
||||
dump("added cookies and stuff from main page\n");
|
||||
callback();
|
||||
};
|
||||
|
||||
function deleteDB(dbName) {
|
||||
@ -84,6 +97,17 @@ function deleteDB(dbName) {
|
||||
});
|
||||
}
|
||||
|
||||
let cacheGenerator = function*() {
|
||||
let cache = yield caches.open("plop");
|
||||
yield cache.add("404_cached_file.js");
|
||||
yield cache.add("browser_storage_basic.js");
|
||||
};
|
||||
|
||||
window.setup = function*() {
|
||||
yield idbGenerator();
|
||||
yield cacheGenerator();
|
||||
};
|
||||
|
||||
window.clear = function*() {
|
||||
document.cookie = "c1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/browser";
|
||||
document.cookie =
|
||||
@ -98,6 +122,8 @@ window.clear = function*() {
|
||||
yield deleteDB("idb1");
|
||||
yield deleteDB("idb2");
|
||||
|
||||
yield caches.delete("plop");
|
||||
|
||||
dump("removed cookies, localStorage, sessionStorage and indexedDB data " +
|
||||
"from " + document.location + "\n");
|
||||
};
|
||||
|
@ -17,41 +17,59 @@ function success(event) {
|
||||
setupIDB.next(event);
|
||||
}
|
||||
|
||||
window.idbGenerator = function*(callback) {
|
||||
let idbGenerator = function*() {
|
||||
let request = indexedDB.open("idb-s1", 1);
|
||||
request.onupgradeneeded = success;
|
||||
request.onsuccess = success;
|
||||
request.onerror = function() {
|
||||
throw new Error("error opening db connection");
|
||||
};
|
||||
let event = yield undefined;
|
||||
let db = yield new Promise(done => {
|
||||
request.onupgradeneeded = event => {
|
||||
let db = event.target.result;
|
||||
let store1 = db.createObjectStore("obj-s1", { keyPath: "id" });
|
||||
store1.transaction.oncomplete = () => {
|
||||
done(db);
|
||||
};
|
||||
};
|
||||
});
|
||||
yield new Promise(done => {
|
||||
request.onsuccess = done;
|
||||
});
|
||||
|
||||
store1.add({id: 6, name: "foo", email: "foo@bar.com"}).onsuccess = success;
|
||||
yield undefined;
|
||||
store1.add({id: 7, name: "foo2", email: "foo2@bar.com"}).onsuccess = success;
|
||||
yield undefined;
|
||||
let transaction = db.transaction(["obj-s1"], "readwrite");
|
||||
let store1 = transaction.objectStore("obj-s1");
|
||||
store1.add({id: 6, name: "foo", email: "foo@bar.com"});
|
||||
store1.add({id: 7, name: "foo2", email: "foo2@bar.com"});
|
||||
yield new Promise(success => {
|
||||
transaction.oncomplete = success;
|
||||
});
|
||||
|
||||
yield undefined;
|
||||
db.close();
|
||||
|
||||
request = indexedDB.open("idb-s2", 1);
|
||||
request.onupgradeneeded = success;
|
||||
request.onsuccess = success;
|
||||
event = yield undefined;
|
||||
|
||||
let db2 = yield new Promise(done => {
|
||||
request.onupgradeneeded = event => {
|
||||
let db2 = event.target.result;
|
||||
let store3 =
|
||||
db2.createObjectStore("obj-s2", { keyPath: "id3", autoIncrement: true });
|
||||
store3.createIndex("name2", "name2", { unique: true });
|
||||
store3.add({id3: 16, name2: "foo", email: "foo@bar.com"}).onsuccess = success;
|
||||
yield undefined;
|
||||
store3.transaction.oncomplete = () => {
|
||||
done(db2);
|
||||
};
|
||||
};
|
||||
});
|
||||
yield new Promise(done => {
|
||||
request.onsuccess = done;
|
||||
});
|
||||
|
||||
transaction = db2.transaction(["obj-s2"], "readwrite");
|
||||
let store3 = transaction.objectStore("obj-s2");
|
||||
store3.add({id3: 16, name2: "foo", email: "foo@bar.com"});
|
||||
yield new Promise(success => {
|
||||
transaction.oncomplete = success;
|
||||
});
|
||||
|
||||
yield undefined;
|
||||
db2.close();
|
||||
dump("added cookies and stuff from secured iframe\n");
|
||||
callback();
|
||||
};
|
||||
|
||||
function deleteDB(dbName) {
|
||||
@ -61,6 +79,10 @@ function deleteDB(dbName) {
|
||||
});
|
||||
}
|
||||
|
||||
window.setup = function*() {
|
||||
yield idbGenerator();
|
||||
};
|
||||
|
||||
window.clear = function*() {
|
||||
document.cookie = "sc1=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
||||
|
||||
|
@ -341,11 +341,16 @@ StorageUI.prototype = {
|
||||
populateStorageTree: function(storageTypes) {
|
||||
this.storageTypes = {};
|
||||
for (let type in storageTypes) {
|
||||
// Ignore `from` field, which is just a protocol.js implementation artifact
|
||||
if (type === "from") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let typeLabel = L10N.getStr("tree.labels." + type);
|
||||
let typeLabel = type;
|
||||
try {
|
||||
typeLabel = L10N.getStr("tree.labels." + type);
|
||||
} catch(e) {
|
||||
console.error("Unable to localize tree label type:" + type);
|
||||
}
|
||||
this.tree.add([{id: type, label: typeLabel, type: "store"}]);
|
||||
if (!storageTypes[type].hosts) {
|
||||
continue;
|
||||
@ -568,7 +573,12 @@ StorageUI.prototype = {
|
||||
if (!uniqueKey) {
|
||||
this.table.uniqueId = uniqueKey = key;
|
||||
}
|
||||
columns[key] = key;
|
||||
try {
|
||||
columns[key] = L10N.getStr("table.headers." + type + "." + key);
|
||||
} catch(e) {
|
||||
console.error("Unable to localize table header type:" + type + " key:" + key);
|
||||
}
|
||||
}
|
||||
this.table.setColumns(columns, null, HIDDEN_COLUMNS);
|
||||
this.shouldResetColumns = false;
|
||||
|
@ -115,6 +115,7 @@ body {
|
||||
#global-toolbar > *,
|
||||
#timeline-toolbar > * {
|
||||
min-height: var(--toolbar-height);
|
||||
border-color: var(--theme-splitter-color);
|
||||
border-width: 0 0 0 1px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -1089,7 +1089,7 @@ Messages.Extended = function(messagePieces, options = {})
|
||||
}
|
||||
|
||||
this._repeatID.quoteStrings = this._quoteStrings;
|
||||
this._repeatID.messagePieces = messagePieces + "";
|
||||
this._repeatID.messagePieces = JSON.stringify(messagePieces);
|
||||
this._repeatID.actors = new Set(); // using a set to avoid duplicates
|
||||
};
|
||||
|
||||
|
@ -163,28 +163,26 @@ skip-if = e10s # Bug 1042253 - webconsole e10s tests (intermittent Linux debug)
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_console_copy_entire_message_context_menu.js]
|
||||
[browser_console_error_source_click.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_console_filters.js]
|
||||
[browser_console_iframe_messages.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_console_keyboard_accessibility.js]
|
||||
[browser_console_log_inspectable_object.js]
|
||||
[browser_console_native_getters.js]
|
||||
[browser_console_navigation_marker.js]
|
||||
[browser_console_netlogging.js]
|
||||
[browser_console_nsiconsolemessage.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_console_optimized_out_vars.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_console_private_browsing.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_console_server_logging.js]
|
||||
[browser_console_variables_view.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_console_variables_view_filter.js]
|
||||
[browser_console_variables_view_dom_nodes.js]
|
||||
[browser_console_variables_view_dont_sort_non_sortable_classes_properties.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_console_variables_view_special_names.js]
|
||||
[browser_console_variables_view_while_debugging.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
@ -199,17 +197,14 @@ skip-if = e10s && debug && os == 'win'
|
||||
[browser_output_breaks_after_console_dir_uninspectable.js]
|
||||
[browser_output_longstring_expand.js]
|
||||
[browser_repeated_messages_accuracy.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_result_format_as_string.js]
|
||||
[browser_warn_user_about_replaced_api.js]
|
||||
[browser_webconsole_abbreviate_source_url.js]
|
||||
[browser_webconsole_allow_mixedcontent_securityerrors.js]
|
||||
tags = mcb
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_assert.js]
|
||||
[browser_webconsole_block_mixedcontent_securityerrors.js]
|
||||
tags = mcb
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_bug_579412_input_focus.js]
|
||||
[browser_webconsole_bug_580001_closing_after_completion.js]
|
||||
[browser_webconsole_bug_580030_errors_after_page_reload.js]
|
||||
@ -285,7 +280,6 @@ tags = mcb
|
||||
[browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js]
|
||||
skip-if = true # Bug 1110500 - mouse event failure in test
|
||||
[browser_webconsole_bug_764572_output_open_url.js]
|
||||
@ -294,12 +288,10 @@ skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_770099_violation.js]
|
||||
skip-if = e10s && os == 'win'
|
||||
[browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_bug_804845_ctrl_key_nav.js]
|
||||
skip-if = os != "mac"
|
||||
[browser_webconsole_bug_817834_add_edited_input_to_history.js]
|
||||
[browser_webconsole_bug_837351_securityerrors.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_filter_buttons_contextmenu.js]
|
||||
[browser_webconsole_bug_1006027_message_timestamps_incorrect.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug intermittent)
|
||||
@ -328,7 +320,7 @@ skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_history.js]
|
||||
[browser_webconsole_hpkp_invalid-headers.js]
|
||||
[browser_webconsole_hsts_invalid-headers.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_webconsole_input_field_focus_on_panel_select.js]
|
||||
[browser_webconsole_inspect-parsed-documents.js]
|
||||
[browser_webconsole_js_input_expansion.js]
|
||||
|
@ -29,6 +29,7 @@ add_task(function* () {
|
||||
yield testCSSRepeats(hud);
|
||||
yield testCSSRepeatsAfterReload(hud);
|
||||
yield testConsoleRepeats(hud);
|
||||
yield testConsoleFalsyValues(hud);
|
||||
|
||||
Services.prefs.clearUserPref(PREF);
|
||||
});
|
||||
@ -124,3 +125,52 @@ function testConsoleRepeats(hud) {
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function testConsoleFalsyValues(hud) {
|
||||
hud.jsterm.clearOutput(true);
|
||||
hud.jsterm.execute("testConsoleFalsyValues()");
|
||||
|
||||
info("wait for repeats of falsy values with the console API");
|
||||
|
||||
return waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [
|
||||
{
|
||||
name: "console.log 'NaN' repeated once",
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG,
|
||||
repeats: 1,
|
||||
},
|
||||
{
|
||||
name: "console.log 'undefined' repeated once",
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG,
|
||||
repeats: 1,
|
||||
},
|
||||
{
|
||||
name: "console.log 'null' repeated once",
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG,
|
||||
repeats: 1,
|
||||
},
|
||||
{
|
||||
name: "console.log 'NaN' repeated twice",
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG,
|
||||
repeats: 2,
|
||||
},
|
||||
{
|
||||
name: "console.log 'undefined' repeated twice",
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG,
|
||||
repeats: 2,
|
||||
},
|
||||
{
|
||||
name: "console.log 'null' repeated twice",
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG,
|
||||
repeats: 2,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
@ -2,14 +2,15 @@
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title>Test for bugs 720180, 800510 and 865288</title>
|
||||
<title>Test for bugs 720180, 800510, 865288 and 1218089</title>
|
||||
<script>
|
||||
function testConsole() {
|
||||
// same line and column number
|
||||
for(var i = 0; i < 2; i++) {
|
||||
console.log("foo repeat");
|
||||
}
|
||||
console.log("foo repeat"); console.error("foo repeat");
|
||||
console.log("foo repeat");
|
||||
console.error("foo repeat");
|
||||
}
|
||||
function testConsoleObjects() {
|
||||
for (var i = 0; i < 3; i++) {
|
||||
@ -17,6 +18,20 @@
|
||||
console.log("abba", o);
|
||||
}
|
||||
}
|
||||
function testConsoleFalsyValues(){
|
||||
[NaN, undefined, null].forEach(function(item, index){
|
||||
console.log(item);
|
||||
});
|
||||
[NaN, NaN].forEach(function(item, index){
|
||||
console.log(item);
|
||||
});
|
||||
[undefined, undefined].forEach(function(item, index){
|
||||
console.log(item);
|
||||
});
|
||||
[null, null].forEach(function(item, index){
|
||||
console.log(item);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
|
@ -138,13 +138,15 @@ var AnimationPlayerActor = ActorClass({
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the name associated with the player. This is used to match
|
||||
* up the player with values in the computed animation-name or
|
||||
* transition-property property.
|
||||
* Get the name of this animation. This can be either the animation.id
|
||||
* property if it was set, or the keyframe rule name or the transition
|
||||
* property.
|
||||
* @return {String}
|
||||
*/
|
||||
getName: function() {
|
||||
if (this.isAnimation()) {
|
||||
if (this.player.id) {
|
||||
return this.player.id;
|
||||
} else if (this.isAnimation()) {
|
||||
return this.player.animationName;
|
||||
} else if (this.isTransition()) {
|
||||
return this.player.transitionProperty;
|
||||
|
@ -922,6 +922,131 @@ StorageActors.createActor({
|
||||
storeObjectType: "storagestoreobject"
|
||||
}, getObjectForLocalOrSessionStorage("sessionStorage"));
|
||||
|
||||
|
||||
let CacheAttributes = [
|
||||
"url",
|
||||
"status",
|
||||
];
|
||||
types.addDictType("cacheobject", {
|
||||
"url": "string",
|
||||
"status": "string"
|
||||
});
|
||||
|
||||
// Array of Cache store objects
|
||||
types.addDictType("cachestoreobject", {
|
||||
total: "number",
|
||||
offset: "number",
|
||||
data: "array:nullable:cacheobject"
|
||||
});
|
||||
|
||||
StorageActors.createActor({
|
||||
typeName: "Cache",
|
||||
storeObjectType: "cachestoreobject"
|
||||
}, {
|
||||
getCachesForHost: Task.async(function*(host) {
|
||||
let uri = Services.io.newURI(host, null, null);
|
||||
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
|
||||
// The first argument tells if you want to get |content| cache or |chrome| cache.
|
||||
// The |content| cache is the cache explicitely named by the web content
|
||||
// (service worker or web page).
|
||||
// The |chrome| cache is the cache implicitely cached by the platform, hosting the
|
||||
// source file of the service worker.
|
||||
let { CacheStorage } = this.storageActor.window;
|
||||
let cache = new CacheStorage("content", principal);
|
||||
return cache;
|
||||
}),
|
||||
|
||||
preListStores: Task.async(function*() {
|
||||
for (let host of this.hosts) {
|
||||
yield this.populateStoresForHost(host);
|
||||
}
|
||||
}),
|
||||
|
||||
form: function(form, detail) {
|
||||
if (detail === "actorid") {
|
||||
return this.actorID;
|
||||
}
|
||||
|
||||
let hosts = {};
|
||||
for (let host of this.hosts) {
|
||||
hosts[host] = this.getNamesForHost(host);
|
||||
}
|
||||
|
||||
return {
|
||||
actor: this.actorID,
|
||||
hosts: hosts
|
||||
};
|
||||
},
|
||||
|
||||
getNamesForHost: function(host) {
|
||||
// UI code expect each name to be a JSON string of an array :/
|
||||
return [...this.hostVsStores.get(host).keys()].map(a => JSON.stringify([a]));
|
||||
},
|
||||
|
||||
getValuesForHost: Task.async(function*(host, name) {
|
||||
if (!name) return [];
|
||||
// UI is weird and expect a JSON stringified array... and pass it back :/
|
||||
name = JSON.parse(name)[0];
|
||||
|
||||
let cache = this.hostVsStores.get(host).get(name);
|
||||
let requests = yield cache.keys();
|
||||
let results = [];
|
||||
for(let request of requests) {
|
||||
let response = yield cache.match(request);
|
||||
// Unwrap the response to get access to all its properties if the
|
||||
// response happen to be 'opaque', when it is a Cross Origin Request.
|
||||
response = response.cloneUnfiltered();
|
||||
results.push(yield this.processEntry(request, response));
|
||||
}
|
||||
return results;
|
||||
}),
|
||||
|
||||
processEntry: Task.async(function*(request, response) {
|
||||
return {
|
||||
url: String(request.url),
|
||||
status: String(response.statusText),
|
||||
};
|
||||
}),
|
||||
|
||||
getHostName: function(location) {
|
||||
if (!location.host) {
|
||||
return location.href;
|
||||
}
|
||||
return location.protocol + "//" + location.host;
|
||||
},
|
||||
|
||||
populateStoresForHost: Task.async(function*(host, window) {
|
||||
let storeMap = new Map();
|
||||
let caches = yield this.getCachesForHost(host);
|
||||
for (let name of (yield caches.keys())) {
|
||||
storeMap.set(name, (yield caches.open(name)));
|
||||
}
|
||||
this.hostVsStores.set(host, storeMap);
|
||||
}),
|
||||
|
||||
/**
|
||||
* This method is overriden and left blank as for Cache Storage, this
|
||||
* operation cannot be performed synchronously. Thus, the preListStores
|
||||
* method exists to do the same task asynchronously.
|
||||
*/
|
||||
populateStoresForHosts: function() {
|
||||
this.hostVsStores = new Map();
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a url, correctly determine its protocol + hostname part.
|
||||
*/
|
||||
getSchemaAndHost: function(url) {
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
return uri.scheme + "://" + uri.hostPort;
|
||||
},
|
||||
|
||||
toStoreObject: function(item) {
|
||||
return item;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Code related to the Indexed DB actor and front
|
||||
*/
|
||||
|
@ -145,6 +145,19 @@
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.script-generated {
|
||||
display: inline-block;
|
||||
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background-color: black;
|
||||
background-image:
|
||||
repeating-linear-gradient(45deg, transparent 0, transparent 5px, #f06 5px, #f06 10px);
|
||||
border: 5px solid #f06;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
<div class="not-animated"></div>
|
||||
<div class="simple-animation"></div>
|
||||
@ -157,11 +170,26 @@
|
||||
<div class="delayed-multiple-animations"></div>
|
||||
<div class="multiple-animations-2"></div>
|
||||
<div class="all-transitions"></div>
|
||||
<div class="script-generated"></div>
|
||||
<script type="text/javascript">
|
||||
// Get the transitions started when the page loads
|
||||
var players;
|
||||
addEventListener("load", function() {
|
||||
document.querySelector(".transition").classList.add("get-round");
|
||||
document.querySelector(".delayed-transition").classList.add("get-round");
|
||||
|
||||
// Create a script-generated animation.
|
||||
var animation = document.querySelector(".script-generated").animate({
|
||||
backgroundColor: ["black", "gold"]
|
||||
}, {
|
||||
duration: 500,
|
||||
iterations: Infinity,
|
||||
direction: "alternate"
|
||||
});
|
||||
animation.id = "custom-animation-name";
|
||||
|
||||
// Add a custom animation id to an existing css animation.
|
||||
document.querySelector(".delayed-animation")
|
||||
.getAnimations()[0].id = "cssanimation-custom-name";
|
||||
});
|
||||
</script>
|
||||
|
@ -28,6 +28,7 @@ support-files =
|
||||
[browser_animation_getStateAfterFinished.js]
|
||||
[browser_animation_getSubTreeAnimations.js]
|
||||
[browser_animation_keepFinished.js]
|
||||
[browser_animation_name.js]
|
||||
[browser_animation_playerState.js]
|
||||
[browser_animation_playPauseIframe.js]
|
||||
[browser_animation_playPauseSeveral.js]
|
||||
|
51
devtools/server/tests/browser/browser_animation_name.js
Normal file
51
devtools/server/tests/browser/browser_animation_name.js
Normal file
@ -0,0 +1,51 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the AnimationPlayerActor provides the correct name for an
|
||||
// animation. Whether this animation is a CSSAnimation, CSSTransition or a
|
||||
// script-based animation that has been given an id, or even a CSSAnimation that
|
||||
// has been given an id.
|
||||
|
||||
const TEST_DATA = [{
|
||||
selector: ".simple-animation",
|
||||
animationIndex: 0,
|
||||
expectedName: "move"
|
||||
}, {
|
||||
selector: ".transition",
|
||||
animationIndex: 0,
|
||||
expectedName: "width"
|
||||
}, {
|
||||
selector: ".script-generated",
|
||||
animationIndex: 0,
|
||||
expectedName: "custom-animation-name"
|
||||
}, {
|
||||
selector: ".delayed-animation",
|
||||
animationIndex: 0,
|
||||
expectedName: "cssanimation-custom-name"
|
||||
}];
|
||||
|
||||
add_task(function*() {
|
||||
let {client, walker, animations} =
|
||||
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
for (let {selector, animationIndex, expectedName} of TEST_DATA) {
|
||||
let {name} = yield getAnimationStateForNode(walker, animations, selector,
|
||||
animationIndex);
|
||||
is(name, expectedName, "The animation has the expected name");
|
||||
}
|
||||
|
||||
yield closeDebuggerClient(client);
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function* getAnimationStateForNode(walker, animations, nodeSelector, index) {
|
||||
let node = yield walker.querySelector(walker.rootNode, nodeSelector);
|
||||
let players = yield animations.getAnimationPlayersForNode(node);
|
||||
let player = players[index];
|
||||
yield player.ready();
|
||||
let state = yield player.getCurrentState();
|
||||
return state;
|
||||
}
|
@ -265,10 +265,7 @@ AccessibleCaretManager::UpdateCaretsForCursorMode(UpdateCaretsHint aHint)
|
||||
case PositionChangedResult::Changed:
|
||||
switch (aHint) {
|
||||
case UpdateCaretsHint::Default:
|
||||
// On Fennec, always show accessiblecaret even if the input is empty
|
||||
// to make ActionBar visible.
|
||||
if (sCaretsExtendedVisibility ||
|
||||
HasNonEmptyTextContent(GetEditingHostForFrame(frame))) {
|
||||
if (HasNonEmptyTextContent(GetEditingHostForFrame(frame))) {
|
||||
mFirstCaret->SetAppearance(Appearance::Normal);
|
||||
} else {
|
||||
mFirstCaret->SetAppearance(Appearance::NormalNotShown);
|
||||
|
@ -252,8 +252,7 @@ protected:
|
||||
static bool sSelectionBarEnabled;
|
||||
|
||||
// Android specific visibility extensions correct compatibility issues
|
||||
// with caret-drag, tapping into empty inputs, and ActionBar visiblity
|
||||
// during page scroll.
|
||||
// with caret-drag and ActionBar visibility during page scroll.
|
||||
static bool sCaretsExtendedVisibility;
|
||||
|
||||
// By default, javascript content selection changes closes AccessibleCarets and
|
||||
|
@ -926,6 +926,10 @@ pref("layout.accessiblecaret.enabled", true);
|
||||
#else
|
||||
pref("layout.accessiblecaret.enabled", false);
|
||||
#endif
|
||||
|
||||
// Android need persistent carets and actionbar. Turn off the caret timeout.
|
||||
pref("layout.accessiblecaret.timeout_ms", 0);
|
||||
|
||||
// Android generates long tap (mouse) events.
|
||||
pref("layout.accessiblecaret.use_long_tap_injector", false);
|
||||
|
||||
|
@ -7,6 +7,7 @@ package org.mozilla.gecko;
|
||||
|
||||
import android.Manifest;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
import org.mozilla.gecko.adjust.AdjustHelperInterface;
|
||||
import org.mozilla.gecko.annotation.RobocopTarget;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
@ -1727,9 +1728,7 @@ public class BrowserApp extends GeckoApp
|
||||
final BrowserDB db = getProfile().getDB();
|
||||
final ContentResolver cr = getContentResolver();
|
||||
Telemetry.addToHistogram("PLACES_PAGES_COUNT", db.getCount(cr, "history"));
|
||||
Telemetry.addToHistogram("PLACES_BOOKMARKS_COUNT", db.getCount(cr, "bookmarks"));
|
||||
Telemetry.addToHistogram("FENNEC_FAVICONS_COUNT", db.getCount(cr, "favicons"));
|
||||
Telemetry.addToHistogram("FENNEC_THUMBNAILS_COUNT", db.getCount(cr, "thumbnails"));
|
||||
Telemetry.addToHistogram("FENNEC_BOOKMARKS_COUNT", db.getCount(cr, "bookmarks"));
|
||||
Telemetry.addToHistogram("FENNEC_READING_LIST_COUNT", db.getReadingListAccessor().getCount(cr));
|
||||
Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT", (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0));
|
||||
Telemetry.addToHistogram("FENNEC_TABQUEUE_ENABLED", (TabQueueHelper.isTabQueueEnabled(BrowserApp.this) ? 1 : 0));
|
||||
@ -2222,36 +2221,33 @@ public class BrowserApp extends GeckoApp
|
||||
}
|
||||
|
||||
String url = "";
|
||||
String telemetryMsg = "urlbar-empty";
|
||||
|
||||
final Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null) {
|
||||
final String userSearchTerm = tab.getUserRequested();
|
||||
final String tabURL = tab.getURL();
|
||||
|
||||
// Check to see if there's a user-entered search term,
|
||||
// which we save whenever the user performs a search.
|
||||
final String telemetryMsg;
|
||||
if (!TextUtils.isEmpty(userSearchTerm)) {
|
||||
url = userSearchTerm;
|
||||
telemetryMsg = "urlbar-userentered";
|
||||
} else {
|
||||
url = tab.getURL();
|
||||
telemetryMsg = url.isEmpty() ? "urlbar-empty" : "urlbar-url";
|
||||
} else if (!TextUtils.isEmpty(tabURL)) {
|
||||
url = tabURL;
|
||||
telemetryMsg = "urlbar-url";
|
||||
}
|
||||
}
|
||||
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.ACTIONBAR, telemetryMsg);
|
||||
}
|
||||
enterEditingMode(url);
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.ACTIONBAR, telemetryMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enters editing mode with the specified URL. If a null
|
||||
* url is given, the empty String will be used instead.
|
||||
*/
|
||||
private void enterEditingMode(String url) {
|
||||
if (url == null) {
|
||||
url = "";
|
||||
}
|
||||
|
||||
private void enterEditingMode(@NonNull String url) {
|
||||
if (mBrowserToolbar.isEditing() || mBrowserToolbar.isAnimating()) {
|
||||
return;
|
||||
}
|
||||
|
@ -181,8 +181,6 @@ public class LocalURLMetadata implements URLMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
Telemetry.addToHistogram("FENNEC_TILES_CACHE_HIT", data.size());
|
||||
|
||||
// If everything was in the cache, we're done!
|
||||
if (urlsToQuery.size() == 0) {
|
||||
return Collections.unmodifiableMap(data);
|
||||
|
@ -26,16 +26,11 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpEntity;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.HttpStatus;
|
||||
import ch.boye.httpclientandroidlib.client.HttpClient;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpGet;
|
||||
import ch.boye.httpclientandroidlib.impl.client.DefaultHttpRequestRetryHandler;
|
||||
import ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder;
|
||||
|
||||
/**
|
||||
* Download content that has been scheduled during "study" or "verify".
|
||||
*/
|
||||
@ -43,8 +38,12 @@ public class DownloadAction extends BaseAction {
|
||||
private static final String LOGTAG = "DLCDownloadAction";
|
||||
|
||||
private static final String CACHE_DIRECTORY = "downloadContent";
|
||||
|
||||
private static final String CDN_BASE_URL = "https://mobile.cdn.mozilla.net/";
|
||||
|
||||
private static final int STATUS_OK = 200;
|
||||
private static final int STATUS_PARTIAL_CONTENT = 206;
|
||||
|
||||
public interface Callback {
|
||||
void onContentDownloaded(DownloadContent content);
|
||||
}
|
||||
@ -70,8 +69,6 @@ public class DownloadAction extends BaseAction {
|
||||
return;
|
||||
}
|
||||
|
||||
final HttpClient client = buildHttpClient();
|
||||
|
||||
for (DownloadContent content : catalog.getScheduledDownloads()) {
|
||||
Log.d(LOGTAG, "Downloading: " + content);
|
||||
|
||||
@ -96,7 +93,7 @@ public class DownloadAction extends BaseAction {
|
||||
final String url = createDownloadURL(content);
|
||||
|
||||
if (!temporaryFile.exists() || temporaryFile.length() < content.getSize()) {
|
||||
download(client, url, temporaryFile);
|
||||
download(url, temporaryFile);
|
||||
}
|
||||
|
||||
if (!verify(temporaryFile, content.getDownloadChecksum())) {
|
||||
@ -146,22 +143,23 @@ public class DownloadAction extends BaseAction {
|
||||
Log.v(LOGTAG, "Done");
|
||||
}
|
||||
|
||||
protected void download(HttpClient client, String source, File temporaryFile)
|
||||
protected void download(String source, File temporaryFile)
|
||||
throws RecoverableDownloadContentException, UnrecoverableDownloadContentException {
|
||||
InputStream inputStream = null;
|
||||
OutputStream outputStream = null;
|
||||
|
||||
final HttpGet request = new HttpGet(source);
|
||||
HttpURLConnection connection = null;
|
||||
|
||||
try {
|
||||
connection = buildHttpURLConnection(source);
|
||||
|
||||
final long offset = temporaryFile.exists() ? temporaryFile.length() : 0;
|
||||
if (offset > 0) {
|
||||
request.setHeader("Range", "bytes=" + offset + "-");
|
||||
connection.setRequestProperty("Range", "bytes=" + offset + "-");
|
||||
}
|
||||
|
||||
try {
|
||||
final HttpResponse response = client.execute(request);
|
||||
final int status = response.getStatusLine().getStatusCode();
|
||||
if (status != HttpStatus.SC_OK && status != HttpStatus.SC_PARTIAL_CONTENT) {
|
||||
final int status = connection.getResponseCode();
|
||||
if (status != STATUS_OK && status != STATUS_PARTIAL_CONTENT) {
|
||||
// We are trying to be smart and only retry if this is an error that might resolve in the future.
|
||||
// TODO: This is guesstimating at best. We want to implement failure counters (Bug 1215106).
|
||||
if (status >= 500) {
|
||||
@ -172,6 +170,7 @@ public class DownloadAction extends BaseAction {
|
||||
// Unrecoverable: Client errors 4xx - Unlikely that this version of the client will ever succeed.
|
||||
throw new UnrecoverableDownloadContentException("(Unrecoverable) Download failed. Status code: " + status);
|
||||
} else {
|
||||
// HttpsUrlConnection: -1 (No valid response code)
|
||||
// Informational 1xx: They have no meaning to us.
|
||||
// Successful 2xx: We don't know how to handle anything but 200.
|
||||
// Redirection 3xx: HttpClient should have followed redirects if possible. We should not see those errors here.
|
||||
@ -179,14 +178,8 @@ public class DownloadAction extends BaseAction {
|
||||
}
|
||||
}
|
||||
|
||||
final HttpEntity entity = response.getEntity();
|
||||
if (entity == null) {
|
||||
// Recoverable: Should not happen for a valid asset
|
||||
throw new RecoverableDownloadContentException(RecoverableDownloadContentException.SERVER, "Null entity");
|
||||
}
|
||||
|
||||
inputStream = new BufferedInputStream(entity.getContent());
|
||||
outputStream = openFile(temporaryFile, status == HttpStatus.SC_PARTIAL_CONTENT);
|
||||
inputStream = new BufferedInputStream(connection.getInputStream());
|
||||
outputStream = openFile(temporaryFile, status == STATUS_PARTIAL_CONTENT);
|
||||
|
||||
IOUtils.copy(inputStream, outputStream);
|
||||
|
||||
@ -198,6 +191,10 @@ public class DownloadAction extends BaseAction {
|
||||
} finally {
|
||||
IOUtils.safeStreamClose(inputStream);
|
||||
IOUtils.safeStreamClose(outputStream);
|
||||
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,14 +255,21 @@ public class DownloadAction extends BaseAction {
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
|
||||
}
|
||||
|
||||
protected HttpClient buildHttpClient() {
|
||||
protected HttpURLConnection buildHttpURLConnection(String url)
|
||||
throws UnrecoverableDownloadContentException, IOException {
|
||||
// TODO: Implement proxy support (Bug 1209496)
|
||||
return HttpClientBuilder.create()
|
||||
.setUserAgent(HardwareUtils.isTablet() ?
|
||||
try {
|
||||
System.setProperty("http.keepAlive", "true");
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.setRequestProperty("User-Agent", HardwareUtils.isTablet() ?
|
||||
AppConstants.USER_AGENT_FENNEC_TABLET :
|
||||
AppConstants.USER_AGENT_FENNEC_MOBILE)
|
||||
.setRetryHandler(new DefaultHttpRequestRetryHandler())
|
||||
.build();
|
||||
AppConstants.USER_AGENT_FENNEC_MOBILE);
|
||||
connection.setRequestMethod("GET");
|
||||
return connection;
|
||||
} catch (MalformedURLException e) {
|
||||
throw new UnrecoverableDownloadContentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String createDownloadURL(DownloadContent content) {
|
||||
|
@ -80,6 +80,8 @@ class SearchEngineRow extends AnimatedHeightLayout {
|
||||
private int mMaxSavedSuggestions;
|
||||
private int mMaxSearchSuggestions;
|
||||
|
||||
private final List<Integer> mOccurrences = new ArrayList<Integer>();
|
||||
|
||||
public SearchEngineRow(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@ -164,19 +166,19 @@ class SearchEngineRow extends AnimatedHeightLayout {
|
||||
* @param pattern The pattern that is searched for
|
||||
* @param string The string where we search for the pattern
|
||||
*/
|
||||
private List<Integer> findAllOccurrencesOf(String pattern, String string) {
|
||||
List<Integer> occurrences = new ArrayList<>();
|
||||
private void refreshOccurrencesWith(String pattern, String string) {
|
||||
mOccurrences.clear();
|
||||
final int patternLength = pattern.length();
|
||||
int indexOfMatch = 0;
|
||||
int lastIndexOfMatch = 0;
|
||||
while(indexOfMatch != -1) {
|
||||
indexOfMatch = string.indexOf(pattern, lastIndexOfMatch);
|
||||
lastIndexOfMatch = indexOfMatch + patternLength;
|
||||
// Crash here?
|
||||
if(indexOfMatch != -1) {
|
||||
occurrences.add(indexOfMatch);
|
||||
mOccurrences.add(indexOfMatch);
|
||||
}
|
||||
}
|
||||
return occurrences;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,18 +203,19 @@ class SearchEngineRow extends AnimatedHeightLayout {
|
||||
final TextView suggestionText = (TextView) v.findViewById(R.id.suggestion_text);
|
||||
final String searchTerm = getSuggestionTextFromView(mUserEnteredView);
|
||||
final int searchTermLength = searchTerm.length();
|
||||
final List<Integer> occurrences = findAllOccurrencesOf(searchTerm, suggestion);
|
||||
if (occurrences.size() > 0) {
|
||||
refreshOccurrencesWith(searchTerm, suggestion);
|
||||
if (mOccurrences.size() > 0) {
|
||||
final SpannableStringBuilder sb = new SpannableStringBuilder(suggestion);
|
||||
int nextStartSpanIndex = 0;
|
||||
// Done to make sure that the stretch of text after the last occurrence, till the end of the suggestion, is made bold
|
||||
occurrences.add(suggestion.length());
|
||||
for(int occurrence : occurrences) {
|
||||
mOccurrences.add(suggestion.length());
|
||||
for(int occurrence : mOccurrences) {
|
||||
// Even though they're the same style, SpannableStringBuilder will interpret there as being only one Span present if we re-use a StyleSpan
|
||||
StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
|
||||
sb.setSpan(boldSpan, nextStartSpanIndex, occurrence, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
nextStartSpanIndex = occurrence + searchTermLength;
|
||||
}
|
||||
mOccurrences.clear();
|
||||
suggestionText.setText(sb);
|
||||
} else {
|
||||
suggestionText.setText(suggestion);
|
||||
|
@ -51,7 +51,7 @@ public class TelemetryPingGenerator {
|
||||
appName + '/' +
|
||||
appVersion + '/' +
|
||||
appUpdateChannel + '/' +
|
||||
appBuildId + '/';
|
||||
appBuildId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,6 @@ final class UnusedResourcesUtil {
|
||||
R.drawable.close,
|
||||
R.drawable.homepage_banner_firstrun,
|
||||
R.drawable.icon_openinapp,
|
||||
R.drawable.network_offline,
|
||||
R.drawable.pause,
|
||||
R.drawable.phone,
|
||||
R.drawable.play,
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB |
@ -35,6 +35,27 @@ var ActionBarHandler = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._selectionID && e.collapsed) {
|
||||
switch (e.reason) {
|
||||
case 'longpressonemptycontent':
|
||||
// case 'taponcaret':
|
||||
// Show ActionBar when long pressing on an empty input.
|
||||
// XXX: If we ever want to show ActionBar when single tapping on the
|
||||
// caret, uncomment the above case 'taponcaret'.
|
||||
this._init();
|
||||
break;
|
||||
|
||||
case 'updateposition':
|
||||
// Do not show ActionBar when single tapping on an non-empty editable
|
||||
// input.
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Open a closed ActionBar if carets actually visible.
|
||||
if (!this._selectionID && e.caretVisuallyVisible) {
|
||||
this._init();
|
||||
|
@ -4123,7 +4123,8 @@ Tab.prototype = {
|
||||
let list = this.sanitizeRelString(target.rel);
|
||||
if (list.indexOf("[icon]") != -1) {
|
||||
jsonMessage = this.makeFaviconMessage(target);
|
||||
} else if (list.indexOf("[apple-touch-icon]") != -1) {
|
||||
} else if (list.indexOf("[apple-touch-icon]") != -1 ||
|
||||
list.indexOf("[apple-touch-icon-precomposed]") != -1) {
|
||||
let message = this.makeFaviconMessage(target);
|
||||
this.addMetadata("touchIconList", message.href, message.size);
|
||||
} else if (list.indexOf("[alternate]") != -1 && aEvent.type == "DOMLinkAdded") {
|
||||
|
@ -5,33 +5,24 @@
|
||||
|
||||
package org.mozilla.gecko.dlc;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||
import org.mozilla.gecko.dlc.catalog.DownloadContent;
|
||||
import org.mozilla.gecko.dlc.catalog.DownloadContentCatalog;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpEntity;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.HttpStatus;
|
||||
import ch.boye.httpclientandroidlib.StatusLine;
|
||||
import ch.boye.httpclientandroidlib.client.HttpClient;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpGet;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
|
||||
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ -42,6 +33,9 @@ import static org.mockito.Mockito.*;
|
||||
public class TestDownloadAction {
|
||||
private static final String TEST_URL = "http://example.org";
|
||||
|
||||
private static final int STATUS_OK = 200;
|
||||
private static final int STATUS_PARTIAL_CONTENT = 206;
|
||||
|
||||
/**
|
||||
* Scenario: The current network is metered.
|
||||
*
|
||||
@ -55,8 +49,8 @@ public class TestDownloadAction {
|
||||
|
||||
action.perform(RuntimeEnvironment.application, null);
|
||||
|
||||
verify(action, never()).buildHttpClient();
|
||||
verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
|
||||
verify(action, never()).buildHttpURLConnection(anyString());
|
||||
verify(action, never()).download(anyString(), any(File.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,8 +67,8 @@ public class TestDownloadAction {
|
||||
action.perform(RuntimeEnvironment.application, null);
|
||||
|
||||
verify(action, never()).isActiveNetworkMetered(any(Context.class));
|
||||
verify(action, never()).buildHttpClient();
|
||||
verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
|
||||
verify(action, never()).buildHttpURLConnection(anyString());
|
||||
verify(action, never()).download(anyString(), any(File.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,7 +96,7 @@ public class TestDownloadAction {
|
||||
|
||||
action.perform(RuntimeEnvironment.application, catalog);
|
||||
|
||||
verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
|
||||
verify(action, never()).download(anyString(), any(File.class));
|
||||
verify(catalog).markAsDownloaded(content);
|
||||
}
|
||||
|
||||
@ -114,15 +108,16 @@ public class TestDownloadAction {
|
||||
*/
|
||||
@Test(expected=BaseAction.RecoverableDownloadContentException.class)
|
||||
public void testServerErrorsAreRecoverable() throws Exception {
|
||||
HttpClient client = mockHttpClient(500, "");
|
||||
HttpURLConnection connection = mockHttpURLConnection(500, "");
|
||||
|
||||
File temporaryFile = mock(File.class);
|
||||
doReturn(false).when(temporaryFile).exists();
|
||||
|
||||
DownloadAction action = spy(new DownloadAction(null));
|
||||
action.download(client, TEST_URL, temporaryFile);
|
||||
doReturn(connection).when(action).buildHttpURLConnection(anyString());
|
||||
action.download(TEST_URL, temporaryFile);
|
||||
|
||||
verify(client).execute(any(HttpUriRequest.class));
|
||||
verify(connection).getInputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,15 +128,16 @@ public class TestDownloadAction {
|
||||
*/
|
||||
@Test(expected=BaseAction.UnrecoverableDownloadContentException.class)
|
||||
public void testClientErrorsAreUnrecoverable() throws Exception {
|
||||
HttpClient client = mockHttpClient(404, "");
|
||||
HttpURLConnection connection = mockHttpURLConnection(404, "");
|
||||
|
||||
File temporaryFile = mock(File.class);
|
||||
doReturn(false).when(temporaryFile).exists();
|
||||
|
||||
DownloadAction action = spy(new DownloadAction(null));
|
||||
action.download(client, TEST_URL, temporaryFile);
|
||||
doReturn(connection).when(action).buildHttpURLConnection(anyString());
|
||||
action.download(TEST_URL, temporaryFile);
|
||||
|
||||
verify(client).execute(any(HttpUriRequest.class));
|
||||
verify(connection).getInputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,14 +165,13 @@ public class TestDownloadAction {
|
||||
doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||
|
||||
doReturn(false).when(action).verify(eq(file), anyString());
|
||||
doNothing().when(action).download(any(HttpClient.class), anyString(), eq(file));
|
||||
doNothing().when(action).download(anyString(), eq(file));
|
||||
doReturn(true).when(action).verify(eq(file), anyString());
|
||||
doNothing().when(action).extract(eq(file), eq(file), anyString());
|
||||
|
||||
action.perform(RuntimeEnvironment.application, catalog);
|
||||
|
||||
verify(action).buildHttpClient();
|
||||
verify(action).download(any(HttpClient.class), anyString(), eq(file));
|
||||
verify(action).download(anyString(), eq(file));
|
||||
verify(action).extract(eq(file), eq(file), anyString());
|
||||
verify(catalog).markAsDownloaded(content);
|
||||
}
|
||||
@ -209,8 +204,8 @@ public class TestDownloadAction {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
doReturn(outputStream).when(action).openFile(eq(temporaryFile), anyBoolean());
|
||||
|
||||
HttpClient client = mockHttpClient(HttpStatus.SC_PARTIAL_CONTENT, "HelloWorld");
|
||||
doReturn(client).when(action).buildHttpClient();
|
||||
HttpURLConnection connection = mockHttpURLConnection(STATUS_PARTIAL_CONTENT, "HelloWorld");
|
||||
doReturn(connection).when(action).buildHttpURLConnection(anyString());
|
||||
|
||||
File destinationFile = mockNotExistingFile();
|
||||
doReturn(destinationFile).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||
@ -220,12 +215,9 @@ public class TestDownloadAction {
|
||||
|
||||
action.perform(RuntimeEnvironment.application, catalog);
|
||||
|
||||
ArgumentCaptor<HttpGet> argument = ArgumentCaptor.forClass(HttpGet.class);
|
||||
verify(client).execute(argument.capture());
|
||||
verify(connection).getInputStream();
|
||||
verify(connection).setRequestProperty("Range", "bytes=1337-");
|
||||
|
||||
HttpGet request = argument.getValue();
|
||||
Assert.assertTrue(request.containsHeader("Range"));
|
||||
Assert.assertEquals("bytes=1337-", request.getFirstHeader("Range").getValue());
|
||||
Assert.assertEquals("HelloWorld", new String(outputStream.toByteArray(), "UTF-8"));
|
||||
|
||||
verify(action).openFile(eq(temporaryFile), eq(true));
|
||||
@ -261,8 +253,8 @@ public class TestDownloadAction {
|
||||
doReturn(outputStream).when(action).openFile(eq(temporaryFile), anyBoolean());
|
||||
doThrow(IOException.class).when(outputStream).write(any(byte[].class), anyInt(), anyInt());
|
||||
|
||||
HttpClient client = mockHttpClient(HttpStatus.SC_PARTIAL_CONTENT, "HelloWorld");
|
||||
doReturn(client).when(action).buildHttpClient();
|
||||
HttpURLConnection connection = mockHttpURLConnection(STATUS_PARTIAL_CONTENT, "HelloWorld");
|
||||
doReturn(connection).when(action).buildHttpURLConnection(anyString());
|
||||
|
||||
doReturn(mockNotExistingFile()).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||
|
||||
@ -306,7 +298,7 @@ public class TestDownloadAction {
|
||||
|
||||
action.perform(RuntimeEnvironment.application, catalog);
|
||||
|
||||
verify(action, never()).download(any(HttpClient.class), anyString(), eq(temporaryFile));
|
||||
verify(action, never()).download(anyString(), eq(temporaryFile));
|
||||
verify(action).verify(eq(temporaryFile), anyString());
|
||||
verify(action).extract(eq(temporaryFile), eq(destinationFile), anyString());
|
||||
verify(catalog).markAsDownloaded(content);
|
||||
@ -333,7 +325,7 @@ public class TestDownloadAction {
|
||||
|
||||
DownloadAction action = spy(new DownloadAction(null));
|
||||
doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
|
||||
doNothing().when(action).download(any(HttpClient.class), anyString(), any(File.class));
|
||||
doNothing().when(action).download(anyString(), any(File.class));
|
||||
doReturn(false).when(action).verify(any(File.class), anyString());
|
||||
|
||||
File temporaryFile = mockNotExistingFile();
|
||||
@ -372,8 +364,8 @@ public class TestDownloadAction {
|
||||
|
||||
doReturn(true).when(action).hasEnoughDiskSpace(content, destinationFile, temporaryFile);
|
||||
|
||||
verify(action, never()).buildHttpClient();
|
||||
verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
|
||||
verify(action, never()).buildHttpURLConnection(anyString());
|
||||
verify(action, never()).download(anyString(), any(File.class));
|
||||
verify(action, never()).verify(any(File.class), anyString());
|
||||
verify(catalog, never()).markAsDownloaded(content);
|
||||
}
|
||||
@ -444,9 +436,9 @@ public class TestDownloadAction {
|
||||
doReturn(mockNotExistingFile()).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||
doReturn(true).when(action).hasEnoughDiskSpace(eq(content), any(File.class), any(File.class));
|
||||
|
||||
HttpClient client = mock(HttpClient.class);
|
||||
doThrow(IOException.class).when(client).execute(any(HttpUriRequest.class));
|
||||
doReturn(client).when(action).buildHttpClient();
|
||||
HttpURLConnection connection = mockHttpURLConnection(STATUS_OK, "");
|
||||
doThrow(IOException.class).when(connection).getInputStream();
|
||||
doReturn(connection).when(action).buildHttpURLConnection(anyString());
|
||||
|
||||
action.perform(RuntimeEnvironment.application, catalog);
|
||||
|
||||
@ -476,7 +468,7 @@ public class TestDownloadAction {
|
||||
doReturn(mockNotExistingFile()).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
|
||||
doReturn(mockNotExistingFile()).when(action).getDestinationFile(RuntimeEnvironment.application, content);
|
||||
doReturn(true).when(action).hasEnoughDiskSpace(eq(content), any(File.class), any(File.class));
|
||||
doNothing().when(action).download(any(HttpClient.class), anyString(), any(File.class));
|
||||
doNothing().when(action).download(anyString(), any(File.class));
|
||||
doReturn(true).when(action).verify(any(File.class), anyString());
|
||||
|
||||
File destinationFile = mock(File.class);
|
||||
@ -541,20 +533,12 @@ public class TestDownloadAction {
|
||||
return file;
|
||||
}
|
||||
|
||||
private static HttpClient mockHttpClient(int statusCode, String content) throws Exception {
|
||||
StatusLine status = mock(StatusLine.class);
|
||||
doReturn(statusCode).when(status).getStatusCode();
|
||||
private static HttpURLConnection mockHttpURLConnection(int statusCode, String content) throws Exception {
|
||||
HttpURLConnection connection = mock(HttpURLConnection.class);
|
||||
|
||||
HttpEntity entity = mock(HttpEntity.class);
|
||||
doReturn(new ByteArrayInputStream(content.getBytes("UTF-8"))).when(entity).getContent();
|
||||
doReturn(statusCode).when(connection).getResponseCode();
|
||||
doReturn(new ByteArrayInputStream(content.getBytes("UTF-8"))).when(connection).getInputStream();
|
||||
|
||||
HttpResponse response = mock(HttpResponse.class);
|
||||
doReturn(status).when(response).getStatusLine();
|
||||
doReturn(entity).when(response).getEntity();
|
||||
|
||||
HttpClient client = mock(HttpClient.class);
|
||||
doReturn(response).when(client).execute(any(HttpUriRequest.class));
|
||||
|
||||
return client;
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-common/logmanager.js");
|
||||
Cu.import("resource://services-common/async.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Status",
|
||||
"resource://services-sync/status.js");
|
||||
@ -577,18 +578,22 @@ ErrorHandler.prototype = {
|
||||
this._log.debug(data + " failed to apply some records.");
|
||||
}
|
||||
break;
|
||||
case "weave:engine:sync:error":
|
||||
case "weave:engine:sync:error": {
|
||||
let exception = subject; // exception thrown by engine's sync() method
|
||||
let engine_name = data; // engine name that threw the exception
|
||||
|
||||
this.checkServerError(exception);
|
||||
|
||||
Status.engines = [engine_name, exception.failureCode || ENGINE_UNKNOWN_FAIL];
|
||||
if (Async.isShutdownException(exception)) {
|
||||
this._log.debug(engine_name + " was interrupted due to the application shutting down");
|
||||
} else {
|
||||
this._log.debug(engine_name + " failed", exception);
|
||||
|
||||
Services.telemetry.getKeyedHistogramById("WEAVE_ENGINE_SYNC_ERRORS")
|
||||
.add(engine_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "weave:service:login:error":
|
||||
this._log.error("Sync encountered a login error");
|
||||
this.resetFileLog();
|
||||
@ -601,12 +606,22 @@ ErrorHandler.prototype = {
|
||||
|
||||
this.dontIgnoreErrors = false;
|
||||
break;
|
||||
case "weave:service:sync:error":
|
||||
case "weave:service:sync:error": {
|
||||
if (Status.sync == CREDENTIALS_CHANGED) {
|
||||
this.service.logout();
|
||||
}
|
||||
|
||||
this._log.error("Sync encountered an error");
|
||||
let exception = subject;
|
||||
if (Async.isShutdownException(exception)) {
|
||||
// If we are shutting down we just log the fact, attempt to flush
|
||||
// the log file and get out of here!
|
||||
this._log.error("Sync was interrupted due to the application shutting down");
|
||||
this.resetFileLog();
|
||||
break;
|
||||
}
|
||||
|
||||
// Not a shutdown related exception...
|
||||
this._log.error("Sync encountered an error", exception);
|
||||
this.resetFileLog();
|
||||
|
||||
if (this.shouldReportError()) {
|
||||
@ -617,6 +632,7 @@ ErrorHandler.prototype = {
|
||||
|
||||
this.dontIgnoreErrors = false;
|
||||
break;
|
||||
}
|
||||
case "weave:service:sync:finish":
|
||||
this._log.trace("Status.service is " + Status.service);
|
||||
|
||||
|
@ -161,7 +161,7 @@ var HistoryEntry = {
|
||||
}
|
||||
|
||||
let all_items_found = true;
|
||||
for (let itemvisit in item.visits) {
|
||||
for (let itemvisit of item.visits) {
|
||||
all_items_found = all_items_found && "found" in itemvisit;
|
||||
Logger.logInfo("History entry for " + item.uri + ", type:" +
|
||||
itemvisit.type + ", date:" + itemvisit.date +
|
||||
|
@ -888,7 +888,6 @@ var TPS = {
|
||||
// that complete.
|
||||
if (this.fxaccounts_enabled) {
|
||||
this._triggeredSync = true;
|
||||
this.waitForEvent("weave:service:sync:start");
|
||||
this.waitForSyncFinished();
|
||||
}
|
||||
},
|
||||
@ -919,6 +918,7 @@ var TPS = {
|
||||
this._triggeredSync = true;
|
||||
this.StartAsyncOperation();
|
||||
Weave.Service.sync();
|
||||
Logger.logInfo("Sync is complete");
|
||||
},
|
||||
|
||||
WipeServer: function TPS__WipeServer() {
|
||||
|
@ -342,11 +342,11 @@ class TPSTestRunner(object):
|
||||
if self.mobile:
|
||||
self.preferences.update({'services.sync.client.type' : 'mobile'})
|
||||
|
||||
# Set a dummy username to force the correct authentication type. For the
|
||||
# old sync, the username is not allowed to contain a '@'.
|
||||
dummy = {'fx_account': 'dummy@somewhere', 'sync_account': 'dummy'}
|
||||
auth_type = self.config.get('auth_type', 'fx_account')
|
||||
self.preferences.update({'services.sync.username': dummy[auth_type]})
|
||||
# If we are using legacy Sync, then set a dummy username to force the
|
||||
# correct authentication type. Without this pref set to a value
|
||||
# without an '@' character, Sync will initialize for FxA.
|
||||
if self.config.get('auth_type', 'fx_account') != "fx_account":
|
||||
self.preferences.update({'services.sync.username': "dummy"})
|
||||
|
||||
if self.debug:
|
||||
self.preferences.update(self.debug_preferences)
|
||||
|
@ -2410,7 +2410,7 @@ var Microformats; // jshint ignore:line
|
||||
* @return {Object || undefined}
|
||||
*/
|
||||
getDOMParser: function () {
|
||||
if (typeof DOMParser === undefined) {
|
||||
if (typeof DOMParser === "undefined") {
|
||||
try {
|
||||
return Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
||||
.createInstance(Components.interfaces.nsIDOMParser);
|
||||
|
@ -3386,21 +3386,14 @@
|
||||
"description": "Time taken to download a specified distribution file (msec)",
|
||||
"cpp_guard": "ANDROID"
|
||||
},
|
||||
"FENNEC_FAVICONS_COUNT": {
|
||||
"expires_in_version": "40",
|
||||
"FENNEC_BOOKMARKS_COUNT": {
|
||||
"expires_in_version": "60",
|
||||
"kind": "exponential",
|
||||
"high": "2000",
|
||||
"n_buckets": 10,
|
||||
"cpp_guard": "ANDROID",
|
||||
"description": "Number of favicons stored in the browser DB *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***"
|
||||
},
|
||||
"FENNEC_THUMBNAILS_COUNT": {
|
||||
"expires_in_version": "40",
|
||||
"kind": "exponential",
|
||||
"high": "2000",
|
||||
"n_buckets": 10,
|
||||
"cpp_guard": "ANDROID",
|
||||
"description": "Number of thumbnails stored in the browser DB *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***"
|
||||
"high": "8000",
|
||||
"n_buckets": 20,
|
||||
"description": "Number of bookmarks stored in the browser DB",
|
||||
"alert_emails": ["mobile-frontend@mozilla.com"],
|
||||
"bug_numbers": [1244704]
|
||||
},
|
||||
"FENNEC_READING_LIST_COUNT": {
|
||||
"expires_in_version": "50",
|
||||
@ -8148,13 +8141,6 @@
|
||||
"kind": "flag",
|
||||
"description": "If a master-password is enabled for this profile"
|
||||
},
|
||||
"FENNEC_TILES_CACHE_HIT": {
|
||||
"expires_in_version": "40",
|
||||
"kind": "linear",
|
||||
"high": "13",
|
||||
"n_buckets": 12,
|
||||
"description": "Cache hits on the tile-info metadata database *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***"
|
||||
},
|
||||
"DISPLAY_SCALING_OSX" : {
|
||||
"expires_in_version": "never",
|
||||
"kind": "linear",
|
||||
|
@ -58,6 +58,8 @@ const PREF_UNIFIED = PREF_BRANCH + "unified";
|
||||
|
||||
const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
|
||||
const MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD = "Telemetry:GetChildPayload";
|
||||
const MESSAGE_TELEMETRY_THREAD_HANGS = "Telemetry:ChildThreadHangs";
|
||||
const MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS = "Telemetry:GetChildThreadHangs";
|
||||
const MESSAGE_TELEMETRY_USS = "Telemetry:USS";
|
||||
const MESSAGE_TELEMETRY_GET_CHILD_USS = "Telemetry:GetChildUSS";
|
||||
|
||||
@ -541,6 +543,17 @@ this.TelemetrySession = Object.freeze({
|
||||
requestChildPayloads: function() {
|
||||
return Impl.requestChildPayloads();
|
||||
},
|
||||
/**
|
||||
* Returns a promise that resolves to an array of thread hang stats from content processes, one entry per process.
|
||||
* The structure of each entry is identical to that of "threadHangStats" in nsITelemetry.
|
||||
* While thread hang stats are also part of the child payloads, this function is useful for cheaply getting this information,
|
||||
* which is useful for realtime hang monitoring.
|
||||
* Child processes that do not respond, or spawn/die during execution of this function are excluded from the result.
|
||||
* @returns Promise
|
||||
*/
|
||||
getChildThreadHangs: function() {
|
||||
return Impl.getChildThreadHangs();
|
||||
},
|
||||
/**
|
||||
* Save the session state to a pending file.
|
||||
* Used only for testing purposes.
|
||||
@ -646,6 +659,15 @@ var Impl = {
|
||||
// where source is a weak reference to the child process,
|
||||
// and payload is the telemetry payload from that child process.
|
||||
_childTelemetry: [],
|
||||
// Thread hangs from child processes.
|
||||
// Each element is in the format {source: <weak-ref>, payload: <object>},
|
||||
// where source is a weak reference to the child process,
|
||||
// and payload contains the thread hang stats from that child process.
|
||||
_childThreadHangs: [],
|
||||
// Array of the resolve functions of all the promises that are waiting for the child thread hang stats to arrive, used to resolve all those promises at once
|
||||
_childThreadHangsResolveFunctions: [],
|
||||
// Timeout function for child thread hang stats retrieval
|
||||
_childThreadHangsTimeout: null,
|
||||
// Unique id that identifies this session so the server can cope with duplicate
|
||||
// submissions, orphaning and other oddities. The id is shared across subsessions.
|
||||
_sessionId: null,
|
||||
@ -1372,6 +1394,7 @@ var Impl = {
|
||||
this._hasXulWindowVisibleObserver = true;
|
||||
|
||||
ppml.addMessageListener(MESSAGE_TELEMETRY_PAYLOAD, this);
|
||||
ppml.addMessageListener(MESSAGE_TELEMETRY_THREAD_HANGS, this);
|
||||
ppml.addMessageListener(MESSAGE_TELEMETRY_USS, this);
|
||||
|
||||
// Delay full telemetry initialization to give the browser time to
|
||||
@ -1438,6 +1461,7 @@ var Impl = {
|
||||
|
||||
Services.obs.addObserver(this, "content-child-shutdown", false);
|
||||
cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD, this);
|
||||
cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS, this);
|
||||
cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_USS, this);
|
||||
|
||||
this.gatherStartupHistograms();
|
||||
@ -1470,6 +1494,7 @@ var Impl = {
|
||||
switch (message.name) {
|
||||
case MESSAGE_TELEMETRY_PAYLOAD:
|
||||
{
|
||||
// In parent process, receive Telemetry payload from child
|
||||
let source = message.data.childUUID;
|
||||
delete message.data.childUUID;
|
||||
|
||||
@ -1495,11 +1520,42 @@ var Impl = {
|
||||
}
|
||||
case MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD:
|
||||
{
|
||||
// In child process, send the requested Telemetry payload
|
||||
this.sendContentProcessPing("saved-session");
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TELEMETRY_THREAD_HANGS:
|
||||
{
|
||||
// Accumulate child thread hang stats from this child
|
||||
this._childThreadHangs.push(message.data);
|
||||
|
||||
// Check if we've got data from all the children, accounting for child processes dying
|
||||
// if it happens before the last response is received and no new child processes are spawned at the exact same time
|
||||
// If that happens, we can resolve the promise earlier rather than having to wait for the timeout to expire
|
||||
// Basically, the number of replies is at most the number of messages sent out, this._childCount,
|
||||
// and also at most the number of child processes that currently exist
|
||||
if (this._childThreadHangs.length === Math.min(this._childCount, ppmm.childCount)) {
|
||||
clearTimeout(this._childThreadHangsTimeout);
|
||||
|
||||
// Resolve all the promises that are waiting on these thread hang stats
|
||||
// We resolve here instead of rejecting because
|
||||
for (let resolve of this._childThreadHangsResolveFunctions) {
|
||||
resolve(this._childThreadHangs);
|
||||
}
|
||||
this._childThreadHangsResolveFunctions = [];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS:
|
||||
{
|
||||
// In child process, send the requested child thread hangs
|
||||
this.sendContentProcessThreadHangs();
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TELEMETRY_USS:
|
||||
{
|
||||
// In parent process, receive the USS report from the child
|
||||
if (this._totalMemoryTimeout && this._childrenToHearFrom.delete(message.data.id)) {
|
||||
this._totalMemory += message.data.bytes;
|
||||
if (this._childrenToHearFrom.size == 0) {
|
||||
@ -1517,6 +1573,7 @@ var Impl = {
|
||||
}
|
||||
case MESSAGE_TELEMETRY_GET_CHILD_USS:
|
||||
{
|
||||
// In child process, send the requested USS report
|
||||
this.sendContentProcessUSS(message.data.id);
|
||||
break
|
||||
}
|
||||
@ -1553,6 +1610,15 @@ var Impl = {
|
||||
cpmm.sendAsyncMessage(MESSAGE_TELEMETRY_PAYLOAD, payload);
|
||||
},
|
||||
|
||||
sendContentProcessThreadHangs: function sendContentProcessThreadHangs() {
|
||||
this._log.trace("sendContentProcessThreadHangs");
|
||||
let payload = {
|
||||
childUUID: this._processUUID,
|
||||
hangs: Telemetry.threadHangStats,
|
||||
};
|
||||
cpmm.sendAsyncMessage(MESSAGE_TELEMETRY_THREAD_HANGS, payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Save both the "saved-session" and the "shutdown" pings to disk.
|
||||
* This needs to be called after TelemetrySend shuts down otherwise pings
|
||||
@ -1642,6 +1708,50 @@ var Impl = {
|
||||
ppmm.broadcastAsyncMessage(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD, {});
|
||||
},
|
||||
|
||||
getChildThreadHangs: function getChildThreadHangs() {
|
||||
return new Promise((resolve) => {
|
||||
// Return immediately if there are no child processes to get stats from
|
||||
if (ppmm.childCount === 0) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Register our promise so it will be resolved when we receive the child thread hang stats on the parent process
|
||||
// The resolve functions will all be called from "receiveMessage" when a MESSAGE_TELEMETRY_THREAD_HANGS message comes in
|
||||
this._childThreadHangsResolveFunctions.push((threadHangStats) => {
|
||||
let hangs = threadHangStats.map(child => child.hangs);
|
||||
return resolve(hangs);
|
||||
});
|
||||
|
||||
// If we (the parent) are not currently in the process of requesting child thread hangs, request them
|
||||
// If we are, then the resolve function we registered above will receive the results without needing to request them again
|
||||
if (this._childThreadHangsResolveFunctions.length === 1) {
|
||||
// We have to cache the number of children we send messages to, in case the child count changes while waiting for messages to arrive
|
||||
// This handles the case where the child count increases later on, in which case the new processes won't respond since we never sent messages to them
|
||||
this._childCount = ppmm.childCount;
|
||||
|
||||
this._childThreadHangs = []; // Clear the child hangs
|
||||
for (let i = 0; i < this._childCount; i++) {
|
||||
// If a child dies at exactly while we're running this loop, the message sending will fail but we won't get an exception
|
||||
// In this case, since we won't know this has happened, we will simply rely on the timeout to handle it
|
||||
ppmm.getChildAt(i).sendAsyncMessage(MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS);
|
||||
}
|
||||
|
||||
// Set up a timeout in case one or more of the content processes never responds
|
||||
this._childThreadHangsTimeout = setTimeout(() => {
|
||||
// Resolve all the promises that are waiting on these thread hang stats
|
||||
// We resolve here instead of rejecting because the purpose of this function is
|
||||
// to retrieve the BHR stats from all processes that will give us stats
|
||||
// As a result, one process failing simply means it doesn't get included in the result.
|
||||
for (let resolve of this._childThreadHangsResolveFunctions) {
|
||||
resolve(this._childThreadHangs);
|
||||
}
|
||||
this._childThreadHangsResolveFunctions = [];
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
gatherStartup: function gatherStartup() {
|
||||
this._log.trace("gatherStartup");
|
||||
let counters = processInfo.getCounters();
|
||||
|
@ -8,6 +8,7 @@
|
||||
.main-content {
|
||||
padding-top: 0;
|
||||
-moz-padding-end: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
#nav-header {
|
||||
|
Loading…
Reference in New Issue
Block a user