mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound
This commit is contained in:
commit
04440648e9
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 950298 requires clobber because it changes where nsinstall is picked and it was previously installed there with wrong permissions. Also, the directory where js is built changed.
|
||||
Bug 897735 requires a clobber due to mass mochitest bustage otherwise
|
||||
|
@ -1080,9 +1080,9 @@ let RemoteDebugger = {
|
||||
DebuggerServer.init(this.prompt.bind(this));
|
||||
DebuggerServer.chromeWindowType = "navigator:browser";
|
||||
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
|
||||
// Until we implement unix domain socket, we enable content actors
|
||||
// only on development devices
|
||||
if (Services.prefs.getBoolPref("devtools.debugger.enable-content-actors")) {
|
||||
// Prevent tab actors to be loaded in parent process,
|
||||
// unless we enable certified apps debugging
|
||||
if (!Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps")) {
|
||||
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js");
|
||||
DebuggerServer.addGlobalActor(DebuggerServer.ChromeDebuggerActor, "chromeDebugger");
|
||||
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
|
||||
@ -1093,7 +1093,6 @@ let RemoteDebugger = {
|
||||
DebuggerServer.registerModule("devtools/server/actors/inspector");
|
||||
DebuggerServer.registerModule("devtools/server/actors/styleeditor");
|
||||
DebuggerServer.registerModule("devtools/server/actors/stylesheets");
|
||||
DebuggerServer.enableWebappsContentActor = true;
|
||||
}
|
||||
DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
|
||||
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "c8716b0dcb8cc8946903355c3533481f967b52d3",
|
||||
"revision": "5fed274e799a9f111cdf6476ec53c9328c55c7b9",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"size": 55073212,
|
||||
"digest": "10560463c0804186fc59a84ecab4d5ccd1ffdf298336d3b91065c38888698f4cfab4bad743d6170e73f9bc0538f47a96baf5f1381e8b4ab484d8721301bea31e",
|
||||
"size": 55276112,
|
||||
"digest": "035efc2064a88bdb6f60fd126f17ee0e35de708f4666837cbeb0f3381024044a6ec3a05c244c553f9c2d852d94706f5263edde3d7dbff33872bd05db0215f5e6",
|
||||
"algorithm": "sha512",
|
||||
"filename": "backup-hamachi.tar.xz"
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"size": 55073212,
|
||||
"digest": "10560463c0804186fc59a84ecab4d5ccd1ffdf298336d3b91065c38888698f4cfab4bad743d6170e73f9bc0538f47a96baf5f1381e8b4ab484d8721301bea31e",
|
||||
"size": 55276112,
|
||||
"digest": "035efc2064a88bdb6f60fd126f17ee0e35de708f4666837cbeb0f3381024044a6ec3a05c244c553f9c2d852d94706f5263edde3d7dbff33872bd05db0215f5e6",
|
||||
"algorithm": "sha512",
|
||||
"filename": "backup-hamachi.tar.xz"
|
||||
},
|
||||
|
@ -463,7 +463,7 @@ appUpdater.prototype =
|
||||
* See XPIProvider.jsm
|
||||
*/
|
||||
onUpdateAvailable: function(aAddon, aInstall) {
|
||||
if (!Services.blocklist.isAddonBlocklisted(aAddon.id, aInstall.version,
|
||||
if (!Services.blocklist.isAddonBlocklisted(aAddon,
|
||||
this.update.appVersion,
|
||||
this.update.platformVersion)) {
|
||||
// Compatibility or new version updates mean the same thing here.
|
||||
|
@ -26,7 +26,7 @@ let tests = [
|
||||
sessionToken: "dead",
|
||||
kA: "beef",
|
||||
kB: "cafe",
|
||||
isVerified: true
|
||||
verified: true
|
||||
},
|
||||
payloadType: "message",
|
||||
validateResponse: function(payload) {
|
||||
|
@ -36,10 +36,10 @@ var tests = {
|
||||
testSimpleBlocklist: function(next) {
|
||||
// this really just tests adding and clearing our blocklist for later tests
|
||||
setAndUpdateBlocklist(blocklistURL, function() {
|
||||
ok(Services.blocklist.isAddonBlocklisted("test1.example.com@services.mozilla.org", "0", "0", "0"), "blocking 'blocked'");
|
||||
ok(!Services.blocklist.isAddonBlocklisted("example.com@services.mozilla.org", "0", "0", "0"), "not blocking 'good'");
|
||||
ok(Services.blocklist.isAddonBlocklisted(SocialService.createWrapper(manifest_bad)), "blocking 'blocked'");
|
||||
ok(!Services.blocklist.isAddonBlocklisted(SocialService.createWrapper(manifest)), "not blocking 'good'");
|
||||
resetBlocklist(function() {
|
||||
ok(!Services.blocklist.isAddonBlocklisted("test1.example.com@services.mozilla.org", "0", "0", "0"), "blocklist cleared");
|
||||
ok(!Services.blocklist.isAddonBlocklisted(SocialService.createWrapper(manifest_bad)), "blocklist cleared");
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
@ -22,8 +22,11 @@ Cu.import("resource:///modules/CustomizableUI.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DragPositionManager",
|
||||
"resource:///modules/DragPositionManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
|
||||
"resource:///modules/BrowserUITelemetry.jsm");
|
||||
|
||||
let gModuleName = "[CustomizeMode]";
|
||||
#include logging.js
|
||||
@ -731,6 +734,7 @@ CustomizeMode.prototype = {
|
||||
this.resetting = true;
|
||||
// Disable the reset button temporarily while resetting:
|
||||
let btn = this.document.getElementById("customization-reset-button");
|
||||
BrowserUITelemetry.countCustomizationEvent("reset");
|
||||
btn.disabled = true;
|
||||
return Task.spawn(function() {
|
||||
this._removePanelCustomizationPlaceholders();
|
||||
@ -1106,6 +1110,7 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
|
||||
CustomizableUI.removeWidgetFromArea(aDraggedItemId);
|
||||
BrowserUITelemetry.countCustomizationEvent("remove");
|
||||
// Special widgets are removed outright, we can return here:
|
||||
if (CustomizableUI.isSpecialWidget(aDraggedItemId)) {
|
||||
return;
|
||||
@ -1143,6 +1148,7 @@ CustomizeMode.prototype = {
|
||||
this.wrapToolbarItem(aTargetNode, place);
|
||||
}
|
||||
this.wrapToolbarItem(draggedItem, place);
|
||||
BrowserUITelemetry.countCustomizationEvent("move");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1150,6 +1156,12 @@ CustomizeMode.prototype = {
|
||||
// widget to the end of the area.
|
||||
if (aTargetNode == aTargetArea.customizationTarget) {
|
||||
CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id);
|
||||
// For the purposes of BrowserUITelemetry, we consider both moving a widget
|
||||
// within the same area, and adding a widget from one area to another area
|
||||
// as a "move". An "add" is only when we move an item from the palette into
|
||||
// an area.
|
||||
let custEventType = aOriginArea.id == kPaletteId ? "add" : "move";
|
||||
BrowserUITelemetry.countCustomizationEvent(custEventType);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1177,7 +1189,6 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
let position = placement ? placement.position : null;
|
||||
|
||||
|
||||
// Is the target area the same as the origin? Since we've already handled
|
||||
// the possibility that the target is the customization palette, we know
|
||||
// that the widget is moving within a customizable area.
|
||||
@ -1186,6 +1197,12 @@ CustomizeMode.prototype = {
|
||||
} else {
|
||||
CustomizableUI.addWidgetToArea(aDraggedItemId, aTargetArea.id, position);
|
||||
}
|
||||
|
||||
// For BrowserUITelemetry, an "add" is only when we move an item from the palette
|
||||
// into an area. Otherwise, it's a move.
|
||||
let custEventType = aOriginArea.id == kPaletteId ? "add" : "move";
|
||||
BrowserUITelemetry.countCustomizationEvent(custEventType);
|
||||
|
||||
// If we dropped onto a skipintoolbarset item, manually correct the drop location:
|
||||
if (aTargetNode != itemForPlacement) {
|
||||
let draggedWrapper = draggedItem.parentNode;
|
||||
|
@ -177,7 +177,7 @@
|
||||
<menuitem id="auto-pretty-print"
|
||||
type="checkbox"
|
||||
label="&debuggerUI.autoPrettyPrint;"
|
||||
accesskey="&debuggerUI.autoPrettyPrint.key;"
|
||||
accesskey="&debuggerUI.autoPrettyPrint.accesskey;"
|
||||
command="toggleAutoPrettyPrint"/>
|
||||
<menuitem id="pause-on-exceptions"
|
||||
type="checkbox"
|
||||
|
@ -61,6 +61,12 @@
|
||||
tooltiptext="&options.timestampMessages.tooltip;"
|
||||
data-pref="devtools.webconsole.timestampMessages"/>
|
||||
</vbox>
|
||||
<label value="&options.styleeditor.label;"/>
|
||||
<vbox id="styleeditor-options" class="options-groupbox">
|
||||
<checkbox label="&options.stylesheetSourceMaps.label;"
|
||||
tooltiptext="&options.stylesheetSourceMaps.tooltip;"
|
||||
data-pref="devtools.styleeditor.source-maps-enabled"/>
|
||||
</vbox>
|
||||
<label value="&options.profiler.label;"/>
|
||||
<vbox id="profiler-options" class="options-groupbox">
|
||||
<checkbox label="&options.showPlatformData.label;"
|
||||
|
@ -20,13 +20,12 @@ Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
|
||||
Cu.import("resource:///modules/devtools/SplitView.jsm");
|
||||
Cu.import("resource:///modules/devtools/StyleSheetEditor.jsm");
|
||||
|
||||
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
|
||||
const { PrefObserver, PREF_ORIG_SOURCES } = require("devtools/styleeditor/utils");
|
||||
|
||||
const LOAD_ERROR = "error-load";
|
||||
|
||||
const STYLE_EDITOR_TEMPLATE = "stylesheet";
|
||||
|
||||
const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
|
||||
|
||||
/**
|
||||
* StyleEditorUI is controls and builds the UI of the Style Editor, including
|
||||
* maintaining a list of editors for each stylesheet on a debuggee.
|
||||
@ -55,6 +54,7 @@ function StyleEditorUI(debuggee, target, panelDoc) {
|
||||
this.editors = [];
|
||||
this.selectedEditor = null;
|
||||
|
||||
this._updateSourcesLabel = this._updateSourcesLabel.bind(this);
|
||||
this._onStyleSheetCreated = this._onStyleSheetCreated.bind(this);
|
||||
this._onNewDocument = this._onNewDocument.bind(this);
|
||||
this._clear = this._clear.bind(this);
|
||||
@ -67,7 +67,10 @@ function StyleEditorUI(debuggee, target, panelDoc) {
|
||||
|
||||
this._target.on("will-navigate", this._clear);
|
||||
this._target.on("navigate", this._onNewDocument);
|
||||
})
|
||||
});
|
||||
|
||||
this._prefObserver = new PrefObserver("devtools.styleeditor.");
|
||||
this._prefObserver.on(PREF_ORIG_SOURCES, this._onNewDocument);
|
||||
}
|
||||
|
||||
StyleEditorUI.prototype = {
|
||||
@ -116,6 +119,27 @@ StyleEditorUI.prototype = {
|
||||
wire(this._view.rootElement, ".style-editor-importButton", function onImport() {
|
||||
this._importFromFile(this._mockImportFile || null, this._window);
|
||||
}.bind(this));
|
||||
|
||||
this._contextMenu = this._panelDoc.getElementById("sidebar-context");
|
||||
this._contextMenu.addEventListener("popupshowing",
|
||||
this._updateSourcesLabel);
|
||||
|
||||
this._sourcesItem = this._panelDoc.getElementById("context-origsources");
|
||||
this._sourcesItem.addEventListener("command",
|
||||
this._toggleOrigSources);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update text of context menu option to reflect whether we're showing
|
||||
* original sources (e.g. Sass files) or not.
|
||||
*/
|
||||
_updateSourcesLabel: function() {
|
||||
let string = "showOriginalSources";
|
||||
if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
|
||||
string = "showCSSSources";
|
||||
}
|
||||
this._sourcesItem.setAttribute("label", _(string + ".label"));
|
||||
this._sourcesItem.setAttribute("accesskey", _(string + ".accesskey"));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -282,6 +306,14 @@ StyleEditorUI.prototype = {
|
||||
this.emit("error", errorCode, message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the original sources pref.
|
||||
*/
|
||||
_toggleOrigSources: function() {
|
||||
let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
|
||||
Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a particular stylesheet editor from the UI
|
||||
*
|
||||
@ -474,31 +506,12 @@ StyleEditorUI.prototype = {
|
||||
*/
|
||||
selectStyleSheet: function(href, line, col)
|
||||
{
|
||||
let alreadyCalled = !!this._styleSheetToSelect;
|
||||
let originalHref;
|
||||
|
||||
if (alreadyCalled) {
|
||||
originalHref = this._styleSheetToSelect.href;
|
||||
}
|
||||
|
||||
this._styleSheetToSelect = {
|
||||
href: href,
|
||||
line: line,
|
||||
col: col,
|
||||
};
|
||||
|
||||
if (alreadyCalled) {
|
||||
// Just switch to the correct line and columns if the editor is already
|
||||
// selected for the requested stylesheet.
|
||||
for each (let editor in this.editors) {
|
||||
if (editor.styleSheet.href == originalHref) {
|
||||
editor.sourceEditor.setCursor({line: line, ch: col})
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Switch to the editor for this sheet, if it exists yet.
|
||||
Otherwise each editor will be checked when it's created. */
|
||||
this.switchToSelectedSheet();
|
||||
@ -554,5 +567,8 @@ StyleEditorUI.prototype = {
|
||||
|
||||
destroy: function() {
|
||||
this._clearStyleSheetEditors();
|
||||
|
||||
this._prefObserver.off(PREF_ORIG_SOURCES, this._onNewDocument);
|
||||
this._prefObserver.destroy();
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,9 @@
|
||||
key="key_gotoLine"
|
||||
command="cmd_gotoLine"/>
|
||||
</xul:menupopup>
|
||||
<xul:menupopup id="sidebar-context">
|
||||
<xul:menuitem id="context-origsources"/>
|
||||
</xul:menupopup>
|
||||
</xul:popupset>
|
||||
|
||||
<xul:commandset id="editMenuCommands"/>
|
||||
@ -69,7 +72,8 @@
|
||||
|
||||
<xul:keyset id="sourceEditorKeys"/>
|
||||
|
||||
<xul:box id="style-editor-chrome" class="splitview-root loading">
|
||||
<xul:box id="style-editor-chrome" class="splitview-root loading"
|
||||
context="sidebar-context">
|
||||
<xul:box class="splitview-controller">
|
||||
<xul:box class="splitview-main">
|
||||
<xul:toolbar class="devtools-toolbar">
|
||||
|
@ -16,7 +16,7 @@ function test()
|
||||
addTabAndOpenStyleEditor(function(panel) {
|
||||
let UI = panel.UI;
|
||||
UI.on("editor-added", (event, editor) => {
|
||||
if (++count >= 3) {
|
||||
if (++count == 3) {
|
||||
// wait for 3 editors - 1 for first style sheet, 1 for the
|
||||
// generated style sheet, and 1 for original source after it
|
||||
// loads and replaces the generated style sheet.
|
||||
@ -43,10 +43,37 @@ function runTests(UI)
|
||||
ScssEditor.getSourceEditor().then(() => {
|
||||
testScssEditor(ScssEditor);
|
||||
|
||||
finishUp();
|
||||
togglePref(UI);
|
||||
});
|
||||
}
|
||||
|
||||
function togglePref(UI) {
|
||||
let count = 0;
|
||||
|
||||
UI.on("editor-added", (event, editor) => {
|
||||
if (++count == 2) {
|
||||
testTogglingPref(UI);
|
||||
}
|
||||
})
|
||||
|
||||
Services.prefs.setBoolPref(PREF, false);
|
||||
}
|
||||
|
||||
function testTogglingPref(UI) {
|
||||
is(UI.editors.length, 2, "correct number of editors after pref toggled");
|
||||
|
||||
let CSSEditor = UI.editors[1];
|
||||
|
||||
let link = getStylesheetNameLinkFor(CSSEditor);
|
||||
link.click();
|
||||
|
||||
CSSEditor.getSourceEditor().then(() => {
|
||||
testCSSEditor(CSSEditor);
|
||||
|
||||
finishUp();
|
||||
})
|
||||
}
|
||||
|
||||
function testFirstEditor(editor) {
|
||||
let name = getStylesheetNameFor(editor);
|
||||
is(name, "simple.css", "First style sheet display name is correct");
|
||||
@ -70,6 +97,21 @@ span {\n\
|
||||
}", "Original source text is correct");
|
||||
}
|
||||
|
||||
function testCSSEditor(editor) {
|
||||
let name = getStylesheetNameFor(editor);
|
||||
is(name, "sourcemaps.css", "CSS source display name is correct");
|
||||
|
||||
let text = editor.sourceEditor.getText();
|
||||
|
||||
is(text, "div {\n\
|
||||
color: #ff0066; }\n\
|
||||
\n\
|
||||
span {\n\
|
||||
background-color: #EEE; }\n\
|
||||
\n\
|
||||
/*# sourceMappingURL=sourcemaps.css.map */", "CSS text is correct");
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
function getStylesheetNameLinkFor(editor) {
|
||||
return editor.summary.querySelector(".stylesheet-name");
|
||||
|
40
browser/devtools/styleeditor/utils.js
Normal file
40
browser/devtools/styleeditor/utils.js
Normal file
@ -0,0 +1,40 @@
|
||||
/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const {Cc, Ci, Cu, Cr} = require("chrome");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
|
||||
|
||||
exports.PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
|
||||
|
||||
/**
|
||||
* A PreferenceObserver observes a pref branch for pref changes.
|
||||
* It emits an event for each preference change.
|
||||
*/
|
||||
function PrefObserver(branchName) {
|
||||
this.branchName = branchName;
|
||||
this.branch = Services.prefs.getBranch(branchName);
|
||||
this.branch.addObserver("", this, false);
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
exports.PrefObserver = PrefObserver;
|
||||
|
||||
PrefObserver.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
if (topic == "nsPref:changed") {
|
||||
this.emit(this.branchName + data);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.branch) {
|
||||
this.branch.removeObserver('', this);
|
||||
}
|
||||
}
|
||||
};
|
@ -13,6 +13,7 @@ let promise = require("sdk/core/promise");
|
||||
let {EventEmitter} = require("devtools/shared/event-emitter");
|
||||
const {OutputParser} = require("devtools/output-parser");
|
||||
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
|
||||
const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PluralForm.jsm");
|
||||
@ -26,7 +27,6 @@ const FILTER_CHANGED_TIMEOUT = 300;
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
|
||||
|
||||
/**
|
||||
* Helper for long-running processes that should yield occasionally to
|
||||
@ -170,6 +170,11 @@ function CssHtmlTree(aStyleInspector, aPageStyle)
|
||||
this._handlePrefChange = this._handlePrefChange.bind(this);
|
||||
gDevTools.on("pref-changed", this._handlePrefChange);
|
||||
|
||||
// Refresh panel when pref for showing original sources changes
|
||||
this._updateSourceLinks = this._updateSourceLinks.bind(this);
|
||||
this._prefObserver = new PrefObserver("devtools.");
|
||||
this._prefObserver.on(PREF_ORIG_SOURCES, this._updateSourceLinks);
|
||||
|
||||
CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
|
||||
|
||||
// The element that we're inspecting, and the document that it comes from.
|
||||
@ -270,7 +275,8 @@ CssHtmlTree.prototype = {
|
||||
},
|
||||
|
||||
_handlePrefChange: function(event, data) {
|
||||
if (data.pref == "devtools.defaultColorUnit" && this._computed) {
|
||||
if (this._computed && (data.pref == "devtools.defaultColorUnit" ||
|
||||
data.pref == PREF_ORIG_SOURCES)) {
|
||||
this.refreshPanel();
|
||||
}
|
||||
},
|
||||
@ -448,6 +454,13 @@ CssHtmlTree.prototype = {
|
||||
CssLogic.FILTER.USER;
|
||||
},
|
||||
|
||||
_updateSourceLinks: function CssHtmlTree__updateSourceLinks()
|
||||
{
|
||||
for (let propView of this.propertyViews) {
|
||||
propView.updateSourceLinks();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The CSS as displayed by the UI.
|
||||
*/
|
||||
@ -554,6 +567,13 @@ CssHtmlTree.prototype = {
|
||||
command: this._onCopy
|
||||
});
|
||||
|
||||
// Show Original Sources
|
||||
this.menuitemSources= createMenuItem(this._contextmenu, {
|
||||
label: "ruleView.contextmenu.showOrigSources",
|
||||
accesskey: "ruleView.contextmenu.showOrigSources.accessKey",
|
||||
command: this._onToggleOrigSources
|
||||
});
|
||||
|
||||
let popupset = doc.documentElement.querySelector("popupset");
|
||||
if (!popupset) {
|
||||
popupset = doc.createElementNS(XUL_NS, "popupset");
|
||||
@ -571,6 +591,17 @@ CssHtmlTree.prototype = {
|
||||
let win = this.styleDocument.defaultView;
|
||||
let disable = win.getSelection().isCollapsed;
|
||||
this.menuitemCopy.disabled = disable;
|
||||
|
||||
let label = "ruleView.contextmenu.showOrigSources";
|
||||
if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
|
||||
label = "ruleView.contextmenu.showCSSSources";
|
||||
}
|
||||
this.menuitemSources.setAttribute("label",
|
||||
CssHtmlTree.l10n(label));
|
||||
|
||||
let accessKey = label + ".accessKey";
|
||||
this.menuitemSources.setAttribute("accesskey",
|
||||
CssHtmlTree.l10n(accessKey));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -657,6 +688,15 @@ CssHtmlTree.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the original sources pref.
|
||||
*/
|
||||
_onToggleOrigSources: function()
|
||||
{
|
||||
let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
|
||||
Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
|
||||
},
|
||||
|
||||
/**
|
||||
* Destructor for CssHtmlTree.
|
||||
*/
|
||||
@ -671,6 +711,9 @@ CssHtmlTree.prototype = {
|
||||
this.searchField.removeEventListener("command", this.filterChanged);
|
||||
gDevTools.off("pref-changed", this._handlePrefChange);
|
||||
|
||||
this._prefObserver.off(PREF_ORIG_SOURCES, this._updateSourceLinks);
|
||||
this._prefObserver.destroy();
|
||||
|
||||
// Cancel tree construction
|
||||
if (this._createViewsProcess) {
|
||||
this._createViewsProcess.cancel();
|
||||
@ -1042,6 +1085,20 @@ PropertyView.prototype = {
|
||||
return this._matchedSelectorViews;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update all the selector source links to reflect whether we're linking to
|
||||
* original sources (e.g. Sass files).
|
||||
*/
|
||||
updateSourceLinks: function PropertyView_updateSourceLinks()
|
||||
{
|
||||
if (!this._matchedSelectorViews) {
|
||||
return;
|
||||
}
|
||||
for (let view of this._matchedSelectorViews) {
|
||||
view.updateSourceLink();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The action when a user expands matched selectors.
|
||||
*
|
||||
@ -1099,33 +1156,7 @@ function SelectorView(aTree, aSelectorInfo)
|
||||
this.selectorInfo = aSelectorInfo;
|
||||
this._cacheStatusNames();
|
||||
|
||||
let rule = this.selectorInfo.rule;
|
||||
if (rule && rule.parentStyleSheet) {
|
||||
this.sheet = rule.parentStyleSheet;
|
||||
this.source = CssLogic.shortSource(this.sheet) + ":" + rule.line;
|
||||
|
||||
let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
|
||||
if (showOrig && rule.type != ELEMENT_STYLE) {
|
||||
rule.getOriginalLocation().then(({href, line, column}) => {
|
||||
let newSource = CssLogic.shortSource({href: href}) + ":" + line;
|
||||
|
||||
// Really hacky. Setting the 'source' property won't change the
|
||||
// link's text if the link's already been loaded via template, so we
|
||||
// have to retroactively mutate the DOM.
|
||||
if (newSource != this.source && this.tree.propertyContainer) {
|
||||
let selector = '[sourcelocation="' + this.source + '"]';
|
||||
let link = this.tree.propertyContainer.querySelector(selector);
|
||||
if (link) {
|
||||
link.textContent = newSource;
|
||||
}
|
||||
}
|
||||
this.source = newSource;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.source = CssLogic.l10n("rule.sourceElement");
|
||||
this.href = "#";
|
||||
}
|
||||
this.updateSourceLink();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1221,6 +1252,64 @@ SelectorView.prototype = {
|
||||
return frag;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the text of the source link to reflect whether we're showing
|
||||
* original sources or not.
|
||||
*/
|
||||
updateSourceLink: function()
|
||||
{
|
||||
this.updateSource().then((oldSource) => {
|
||||
if (oldSource != this.source && this.tree.propertyContainer) {
|
||||
let selector = '[sourcelocation="' + oldSource + '"]';
|
||||
let link = this.tree.propertyContainer.querySelector(selector);
|
||||
if (link) {
|
||||
link.textContent = this.source;
|
||||
link.setAttribute("sourcelocation", this.source);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the 'source' store based on our original sources preference.
|
||||
*/
|
||||
updateSource: function()
|
||||
{
|
||||
let rule = this.selectorInfo.rule;
|
||||
this.sheet = rule.parentStyleSheet;
|
||||
|
||||
if (!rule || !this.sheet) {
|
||||
let oldSource = this.source;
|
||||
this.source = CssLogic.l10n("rule.sourceElement");
|
||||
this.href = "#";
|
||||
return promise.resolve(oldSource);
|
||||
}
|
||||
|
||||
let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
|
||||
|
||||
if (showOrig && rule.type != ELEMENT_STYLE) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
// set as this first so we show something while we're fetching
|
||||
this.source = CssLogic.shortSource(this.sheet) + ":" + rule.line;
|
||||
|
||||
rule.getOriginalLocation().then(({href, line, column}) => {
|
||||
let oldSource = this.source;
|
||||
this.source = CssLogic.shortSource({href: href}) + ":" + line;
|
||||
deferred.resolve(oldSource);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
let oldSource = this.source;
|
||||
this.source = CssLogic.shortSource(this.sheet) + ":" + rule.line;
|
||||
return promise.resolve(oldSource);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the style editor if the RETURN key was pressed.
|
||||
*/
|
||||
maybeOpenStyleEditor: function(aEvent)
|
||||
{
|
||||
let keyEvent = Ci.nsIDOMKeyEvent;
|
||||
|
@ -22,7 +22,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
|
||||
const { PrefObserver, PREF_ORIG_SOURCES } = require("devtools/styleeditor/utils");
|
||||
|
||||
/**
|
||||
* These regular expressions are adapted from firebug's css.js, and are
|
||||
@ -1087,12 +1087,17 @@ function CssRuleView(aInspector, aDoc, aStore, aPageStyle)
|
||||
this._contextMenuUpdate = this._contextMenuUpdate.bind(this);
|
||||
this._onSelectAll = this._onSelectAll.bind(this);
|
||||
this._onCopy = this._onCopy.bind(this);
|
||||
this._onToggleOrigSources = this._onToggleOrigSources.bind(this);
|
||||
|
||||
this.element.addEventListener("copy", this._onCopy);
|
||||
|
||||
this._handlePrefChange = this._handlePrefChange.bind(this);
|
||||
gDevTools.on("pref-changed", this._handlePrefChange);
|
||||
|
||||
this._onSourcePrefChanged = this._onSourcePrefChanged.bind(this);
|
||||
this._prefObserver = new PrefObserver("devtools.");
|
||||
this._prefObserver.on(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
|
||||
|
||||
let options = {
|
||||
fixedWidth: true,
|
||||
autoSelect: true,
|
||||
@ -1139,6 +1144,11 @@ CssRuleView.prototype = {
|
||||
accesskey: "ruleView.contextmenu.copy.accessKey",
|
||||
command: this._onCopy
|
||||
});
|
||||
this.menuitemSources= createMenuItem(this._contextmenu, {
|
||||
label: "ruleView.contextmenu.showOrigSources",
|
||||
accesskey: "ruleView.contextmenu.showOrigSources.accessKey",
|
||||
command: this._onToggleOrigSources
|
||||
});
|
||||
|
||||
let popupset = doc.documentElement.querySelector("popupset");
|
||||
if (!popupset) {
|
||||
@ -1210,6 +1220,17 @@ CssRuleView.prototype = {
|
||||
}
|
||||
|
||||
this.menuitemCopy.disabled = !copy;
|
||||
|
||||
let label = "ruleView.contextmenu.showOrigSources";
|
||||
if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
|
||||
label = "ruleView.contextmenu.showCSSSources";
|
||||
}
|
||||
this.menuitemSources.setAttribute("label",
|
||||
_strings.GetStringFromName(label));
|
||||
|
||||
let accessKey = label + ".accessKey";
|
||||
this.menuitemSources.setAttribute("accesskey",
|
||||
_strings.GetStringFromName(accessKey));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1266,6 +1287,15 @@ CssRuleView.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the original sources pref.
|
||||
*/
|
||||
_onToggleOrigSources: function()
|
||||
{
|
||||
let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
|
||||
Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
|
||||
},
|
||||
|
||||
setPageStyle: function(aPageStyle) {
|
||||
this.pageStyle = aPageStyle;
|
||||
},
|
||||
@ -1286,6 +1316,21 @@ CssRuleView.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_onSourcePrefChanged: function()
|
||||
{
|
||||
if (this.menuitemSources) {
|
||||
let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
|
||||
this.menuitemSources.setAttribute("checked", isEnabled);
|
||||
}
|
||||
|
||||
// update text of source links
|
||||
for (let rule of this._elementStyle.rules) {
|
||||
if (rule.editor) {
|
||||
rule.editor.updateSourceLink();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function CssRuleView_destroy()
|
||||
{
|
||||
this.clear();
|
||||
@ -1293,6 +1338,9 @@ CssRuleView.prototype = {
|
||||
gDummyPromise = null;
|
||||
gDevTools.off("pref-changed", this._handlePrefChange);
|
||||
|
||||
this._prefObserver.off(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
|
||||
this._prefObserver.destroy();
|
||||
|
||||
this.element.removeEventListener("copy", this._onCopy);
|
||||
delete this._onCopy;
|
||||
|
||||
@ -1308,6 +1356,9 @@ CssRuleView.prototype = {
|
||||
this.menuitemCopy.removeEventListener("command", this._onCopy);
|
||||
this.menuitemCopy = null;
|
||||
|
||||
this.menuitemSources.removeEventListener("command", this._onToggleOrigSources);
|
||||
this.menuitemSources = null;
|
||||
|
||||
// Destroy the context menu.
|
||||
this._contextmenu.removeEventListener("popupshowing", this._contextMenuUpdate);
|
||||
this._contextmenu.parentNode.removeChild(this._contextmenu);
|
||||
@ -1619,17 +1670,10 @@ RuleEditor.prototype = {
|
||||
}.bind(this));
|
||||
let sourceLabel = this.doc.createElementNS(XUL_NS, "label");
|
||||
sourceLabel.setAttribute("crop", "center");
|
||||
sourceLabel.setAttribute("value", this.rule.title);
|
||||
sourceLabel.setAttribute("tooltiptext", this.rule.title);
|
||||
sourceLabel.classList.add("source-link-label");
|
||||
source.appendChild(sourceLabel);
|
||||
|
||||
let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
|
||||
if (showOrig && this.rule.domRule.type != ELEMENT_STYLE) {
|
||||
this.rule.getOriginalSourceString().then((string) => {
|
||||
sourceLabel.setAttribute("value", string);
|
||||
sourceLabel.setAttribute("tooltiptext", string);
|
||||
})
|
||||
}
|
||||
this.updateSourceLink();
|
||||
|
||||
let code = createChild(this.element, "div", {
|
||||
class: "ruleview-code"
|
||||
@ -1691,6 +1735,21 @@ RuleEditor.prototype = {
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
updateSourceLink: function RuleEditor_updateSourceLink()
|
||||
{
|
||||
let sourceLabel = this.element.querySelector(".source-link-label");
|
||||
sourceLabel.setAttribute("value", this.rule.title);
|
||||
sourceLabel.setAttribute("tooltiptext", this.rule.title);
|
||||
|
||||
let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
|
||||
if (showOrig && this.rule.domRule.type != ELEMENT_STYLE) {
|
||||
this.rule.getOriginalSourceString().then((string) => {
|
||||
sourceLabel.setAttribute("value", string);
|
||||
sourceLabel.setAttribute("tooltiptext", string);
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the rule editor with the contents of the rule.
|
||||
*/
|
||||
|
@ -17,7 +17,7 @@ loader.lazyGetter(this, "ComputedView", () => require("devtools/styleinspector/c
|
||||
loader.lazyGetter(this, "_strings", () => Services.strings
|
||||
.createBundle("chrome://global/locale/devtools/styleinspector.properties"));
|
||||
|
||||
const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
|
||||
const { PREF_ORIG_SOURCES } = require("devtools/styleeditor/utils");
|
||||
|
||||
// This module doesn't currently export any symbols directly, it only
|
||||
// registers inspector tools.
|
||||
|
@ -11,6 +11,9 @@ let toolbox;
|
||||
const TESTCASE_URI = TEST_BASE_HTTPS + "sourcemaps.html";
|
||||
const PREF = "devtools.styleeditor.source-maps-enabled";
|
||||
|
||||
const SCSS_LOC = "sourcemaps.scss:4";
|
||||
const CSS_LOC = "sourcemaps.css:1";
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
@ -46,19 +49,20 @@ function highlightNode(aInspector, aComputedView)
|
||||
}
|
||||
|
||||
function testComputedViewLink() {
|
||||
let link = getLinkByIndex(0);
|
||||
waitForSuccess({
|
||||
name: "link text changed to display original source location",
|
||||
validatorFn: function()
|
||||
{
|
||||
return link.textContent == "sourcemaps.scss:4";
|
||||
},
|
||||
successFn: linkChanged,
|
||||
failureFn: linkChanged,
|
||||
});
|
||||
verifyLinkText(SCSS_LOC, testTogglePref);
|
||||
}
|
||||
|
||||
function linkChanged() {
|
||||
function testTogglePref() {
|
||||
Services.prefs.setBoolPref(PREF, false);
|
||||
|
||||
verifyLinkText(CSS_LOC, () => {
|
||||
Services.prefs.setBoolPref(PREF, true);
|
||||
|
||||
testClickingLink();
|
||||
})
|
||||
}
|
||||
|
||||
function testClickingLink() {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
let toolbox = gDevTools.getToolbox(target);
|
||||
|
||||
@ -110,6 +114,20 @@ function getLinkByIndex(aIndex)
|
||||
return links[aIndex];
|
||||
}
|
||||
|
||||
function verifyLinkText(text, callback) {
|
||||
let link = getLinkByIndex(0);
|
||||
|
||||
waitForSuccess({
|
||||
name: "link text changed to display correct location: " + text,
|
||||
validatorFn: function()
|
||||
{
|
||||
return link.textContent == text;
|
||||
},
|
||||
successFn: callback,
|
||||
failureFn: callback,
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp()
|
||||
{
|
||||
gBrowser.removeCurrentTab();
|
||||
|
@ -11,6 +11,9 @@ let toolbox;
|
||||
const TESTCASE_URI = TEST_BASE_HTTPS + "sourcemaps.html";
|
||||
const PREF = "devtools.styleeditor.source-maps-enabled";
|
||||
|
||||
const SCSS_LOC = "sourcemaps.scss:4";
|
||||
const CSS_LOC = "sourcemaps.css:1";
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
@ -52,20 +55,20 @@ function highlightNode()
|
||||
}
|
||||
|
||||
function testRuleViewLink() {
|
||||
let label = getLinkByIndex(1).querySelector("label");
|
||||
|
||||
waitForSuccess({
|
||||
name: "link text changed to display original source location",
|
||||
validatorFn: function()
|
||||
{
|
||||
return label.getAttribute("value") == "sourcemaps.scss:4";
|
||||
},
|
||||
successFn: linkChanged,
|
||||
failureFn: linkChanged,
|
||||
});
|
||||
verifyLinkText(SCSS_LOC, testTogglePref);
|
||||
}
|
||||
|
||||
function linkChanged() {
|
||||
function testTogglePref() {
|
||||
Services.prefs.setBoolPref(PREF, false);
|
||||
|
||||
verifyLinkText(CSS_LOC, () => {
|
||||
Services.prefs.setBoolPref(PREF, true);
|
||||
|
||||
testClickingLink();
|
||||
})
|
||||
}
|
||||
|
||||
function testClickingLink() {
|
||||
toolbox.once("styleeditor-ready", function(id, aToolbox) {
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
panel.UI.on("editor-selected", (event, editor) => {
|
||||
@ -95,6 +98,22 @@ function editorSelected(editor) {
|
||||
finishUp();
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
|
||||
function verifyLinkText(text, callback) {
|
||||
let label = getLinkByIndex(1).querySelector("label");
|
||||
|
||||
waitForSuccess({
|
||||
name: "link text changed to display correct location: " + text,
|
||||
validatorFn: function()
|
||||
{
|
||||
return label.getAttribute("value") == text;
|
||||
},
|
||||
successFn: callback,
|
||||
failureFn: callback,
|
||||
});
|
||||
}
|
||||
|
||||
function getLinkByIndex(aIndex)
|
||||
{
|
||||
let contentDoc = ruleView().doc;
|
||||
|
@ -44,7 +44,7 @@
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.autoPrettyPrint): This is the label for the
|
||||
- checkbox that toggles auto pretty print. -->
|
||||
<!ENTITY debuggerUI.autoPrettyPrint "Auto Prettify Minified Sources">
|
||||
<!ENTITY debuggerUI.autoPrettyPrint.key "P">
|
||||
<!ENTITY debuggerUI.autoPrettyPrint.accesskey "P">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.sources.toggleBreakpoints): This is the tooltip for the
|
||||
- button that toggles all breakpoints for all sources. -->
|
||||
|
@ -64,6 +64,30 @@ open.accesskey=y
|
||||
# conjunction with accel (Command on Mac or Ctrl on other platforms) to Save
|
||||
saveStyleSheet.commandkey=S
|
||||
|
||||
# LOCALIZATION NOTE (saveStyleSheet.title): This is the file picker title,
|
||||
# when you save a style sheet from the Style Editor.
|
||||
saveStyleSheet.title=Save style sheet
|
||||
|
||||
# LOCALIZATION NOTE (showOriginalSources.label): This is the label on the context
|
||||
# menu item to toggle showing original sources in the editor.
|
||||
showOriginalSources.label=Show original sources
|
||||
|
||||
# LOCALIZATION NOTE (showOriginalSources.accesskey): This is the access key for
|
||||
# the menu item to toggle showing original sources in the editor.
|
||||
showOriginalSources.accesskey=O
|
||||
|
||||
# LOCALIZATION NOTE (showCSSSources.label): This is the label on the context
|
||||
# menu item to toggle back to showing only CSS sources in the editor.
|
||||
showCSSSources.label=Show CSS sources
|
||||
|
||||
# LOCALIZATION NOTE (showCSSSources.accesskey): This is the access key for the
|
||||
# menu item to toggle back to showing only CSS sources in the editor.
|
||||
showCSSSources.accesskey=C
|
||||
|
||||
# LOCALIZATION NOTE (saveStyleSheet.title): This is the file picker title,
|
||||
# when you save a style sheet from the Style Editor.
|
||||
saveStyleSheet.title=Save style sheet
|
||||
|
||||
# LOCALIZATION NOTE (ToolboxStyleEditor.label):
|
||||
# This string is displayed in the title of the tab when the style editor is
|
||||
# displayed inside the developer tools window and in the Developer Tools Menu.
|
||||
|
@ -120,6 +120,16 @@
|
||||
<!ENTITY options.timestampMessages.label "Enable timestamps">
|
||||
<!ENTITY options.timestampMessages.tooltip "If you enable this option commands and output in the Web Console will display a timestamp">
|
||||
|
||||
<!-- LOCALIZATION NOTE (options.styleeditor.label): This is the label for the
|
||||
- heading of the group of Style Editor preferences in the options
|
||||
- panel. -->
|
||||
<!ENTITY options.styleeditor.label "Style Editor">
|
||||
|
||||
<!-- LOCALIZATION NOTE (options.stylesheetSourceMaps.label): This is the
|
||||
- label for the checkbox that toggles showing original sources in the Style Editor -->
|
||||
<!ENTITY options.stylesheetSourceMaps.label "Show original sources">
|
||||
<!ENTITY options.stylesheetSourceMaps.tooltip "Show original sources (e.g. Sass files) in the Style Editor and Inspector">
|
||||
|
||||
<!-- LOCALIZATION NOTE (options.profiler.label): This is the label for the
|
||||
- heading of the group of JavaScript Profiler preferences in the options
|
||||
- panel. -->
|
||||
|
@ -61,10 +61,6 @@ var ContentAreaObserver = {
|
||||
return this._getContentHeightForWindow(this.height);
|
||||
},
|
||||
|
||||
get contentTop () {
|
||||
return Elements.toolbar.getBoundingClientRect().bottom;
|
||||
},
|
||||
|
||||
get viewableHeight() {
|
||||
return this._getViewableHeightForContent(this.contentHeight);
|
||||
},
|
||||
@ -136,12 +132,6 @@ var ContentAreaObserver = {
|
||||
},
|
||||
|
||||
updateContentArea: function cao_updateContentArea (width, height) {
|
||||
let oldHeight = parseInt(this.styles["content-height"].height);
|
||||
let oldWidth = parseInt(this.styles["content-width"].width);
|
||||
|
||||
let newWidth = width || this.width;
|
||||
let newHeight = height || this.contentHeight;
|
||||
|
||||
if (Browser.selectedBrowser) {
|
||||
let notificationBox = Browser.getNotificationBox();
|
||||
|
||||
@ -149,10 +139,19 @@ var ContentAreaObserver = {
|
||||
// make the notification appear above the navbar.
|
||||
if (ContextUI.navbarVisible && !notificationBox.notificationsHidden &&
|
||||
notificationBox.allNotifications.length != 0) {
|
||||
newHeight -= Elements.navbar.getBoundingClientRect().height;
|
||||
let navbarHeight = Elements.navbar.getBoundingClientRect().height;
|
||||
notificationBox.style.paddingBottom = navbarHeight + "px";
|
||||
} else {
|
||||
notificationBox.style.paddingBottom = "";
|
||||
}
|
||||
}
|
||||
|
||||
let oldHeight = parseInt(this.styles["content-height"].height);
|
||||
let oldWidth = parseInt(this.styles["content-width"].width);
|
||||
|
||||
let newWidth = width || this.width;
|
||||
let newHeight = height || this.contentHeight;
|
||||
|
||||
if (newHeight == oldHeight && newWidth == oldWidth)
|
||||
return;
|
||||
|
||||
|
@ -112,7 +112,7 @@ var ContextUI = {
|
||||
}
|
||||
|
||||
if (shown) {
|
||||
ContentAreaObserver.update(window.innerWidth, window.innerHeight);
|
||||
ContentAreaObserver.updateContentArea();
|
||||
}
|
||||
|
||||
return shown;
|
||||
@ -144,7 +144,7 @@ var ContextUI = {
|
||||
}
|
||||
|
||||
if (dismissed) {
|
||||
ContentAreaObserver.update(window.innerWidth, window.innerHeight);
|
||||
ContentAreaObserver.updateContentArea();
|
||||
}
|
||||
|
||||
return dismissed;
|
||||
|
@ -1498,12 +1498,14 @@ Tab.prototype = {
|
||||
let browser = this._browser;
|
||||
|
||||
if (aActive) {
|
||||
notification.classList.add("active-tab-notificationbox");
|
||||
browser.setAttribute("type", "content-primary");
|
||||
Elements.browsers.selectedPanel = notification;
|
||||
browser.active = true;
|
||||
Elements.tabList.selectedTab = this._chromeTab;
|
||||
browser.focus();
|
||||
} else {
|
||||
notification.classList.remove("active-tab-notificationbox");
|
||||
browser.messageManager.sendAsyncMessage("Browser:Blur", { });
|
||||
browser.setAttribute("type", "content");
|
||||
browser.active = false;
|
||||
|
@ -454,7 +454,7 @@ appUpdater.prototype =
|
||||
* See XPIProvider.jsm
|
||||
*/
|
||||
onUpdateAvailable: function(aAddon, aInstall) {
|
||||
if (!Services.blocklist.isAddonBlocklisted(aAddon.id, aInstall.version,
|
||||
if (!Services.blocklist.isAddonBlocklisted(aAddon,
|
||||
this.update.appVersion,
|
||||
this.update.platformVersion)) {
|
||||
// Compatibility or new version updates mean the same thing here.
|
||||
|
@ -216,7 +216,7 @@ documenttab[selected] .documenttab-selection {
|
||||
|
||||
/* Start UI ----------------------------------------------------------------- */
|
||||
|
||||
#content-viewport[startpage] browser {
|
||||
#content-viewport[startpage] .active-tab-notificationbox {
|
||||
padding-bottom: @toolbar_height@;
|
||||
}
|
||||
|
||||
|
@ -126,12 +126,20 @@ const OTHER_MOUSEUP_MONITORED_ITEMS = [
|
||||
"PlacesToolbarItems",
|
||||
];
|
||||
|
||||
// Weakly maps browser windows to objects whose keys are relative
|
||||
// timestamps for when some kind of session started. For example,
|
||||
// when a customization session started. That way, when the window
|
||||
// exits customization mode, we can determine how long the session
|
||||
// lasted.
|
||||
const WINDOW_DURATION_MAP = new WeakMap();
|
||||
|
||||
this.BrowserUITelemetry = {
|
||||
init: function() {
|
||||
UITelemetry.addSimpleMeasureFunction("toolbars",
|
||||
this.getToolbarMeasures.bind(this));
|
||||
Services.obs.addObserver(this, "sessionstore-windows-restored", false);
|
||||
Services.obs.addObserver(this, "browser-delayed-startup-finished", false);
|
||||
CustomizableUI.addListener(this);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
@ -195,13 +203,17 @@ this.BrowserUITelemetry = {
|
||||
},
|
||||
|
||||
_countableEvents: {},
|
||||
_countEvent: function(aKeyArray) {
|
||||
let countObject = this._ensureObjectChain(aKeyArray, 0);
|
||||
let lastItemKey = aKeyArray[aKeyArray.length - 1];
|
||||
countObject[lastItemKey]++;
|
||||
},
|
||||
|
||||
_countMouseUpEvent: function(aCategory, aAction, aButton) {
|
||||
const BUTTONS = ["left", "middle", "right"];
|
||||
let buttonKey = BUTTONS[aButton];
|
||||
if (buttonKey) {
|
||||
let countObject =
|
||||
this._ensureObjectChain([aCategory, aAction, buttonKey], 0);
|
||||
countObject[buttonKey]++;
|
||||
this._countEvent([aCategory, aAction, buttonKey]);
|
||||
}
|
||||
},
|
||||
|
||||
@ -239,6 +251,8 @@ this.BrowserUITelemetry = {
|
||||
item.addEventListener("mouseup", this);
|
||||
}
|
||||
}
|
||||
|
||||
WINDOW_DURATION_MAP.set(aWindow, {});
|
||||
},
|
||||
|
||||
_unregisterWindow: function(aWindow) {
|
||||
@ -411,14 +425,57 @@ this.BrowserUITelemetry = {
|
||||
result.nondefaultAdded = nondefaultAdded;
|
||||
result.defaultRemoved = defaultRemoved;
|
||||
|
||||
// Find out how many open tabs we have in each window
|
||||
let winEnumerator = Services.wm.getEnumerator("navigator:browser");
|
||||
let visibleTabs = [];
|
||||
let hiddenTabs = [];
|
||||
while (winEnumerator.hasMoreElements()) {
|
||||
let someWin = winEnumerator.getNext();
|
||||
if (someWin.gBrowser) {
|
||||
let visibleTabsNum = someWin.gBrowser.visibleTabs.length;
|
||||
visibleTabs.push(visibleTabsNum);
|
||||
hiddenTabs.push(someWin.gBrowser.tabs.length - visibleTabsNum);
|
||||
}
|
||||
}
|
||||
result.visibleTabs = visibleTabs;
|
||||
result.hiddenTabs = hiddenTabs;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
getToolbarMeasures: function() {
|
||||
let result = this._firstWindowMeasurements || {};
|
||||
result.countableEvents = this._countableEvents;
|
||||
result.durations = this._durations;
|
||||
return result;
|
||||
},
|
||||
|
||||
countCustomizationEvent: function(aEventType) {
|
||||
this._countEvent(["customize", aEventType]);
|
||||
},
|
||||
|
||||
_durations: {
|
||||
customization: [],
|
||||
},
|
||||
|
||||
onCustomizeStart: function(aWindow) {
|
||||
this._countEvent(["customize", "start"]);
|
||||
let durationMap = WINDOW_DURATION_MAP.get(aWindow);
|
||||
if (!durationMap) {
|
||||
durationMap = {};
|
||||
WINDOW_DURATION_MAP.set(aWindow, durationMap);
|
||||
}
|
||||
durationMap.customization = aWindow.performance.now();
|
||||
},
|
||||
|
||||
onCustomizeEnd: function(aWindow) {
|
||||
let durationMap = WINDOW_DURATION_MAP.get(aWindow);
|
||||
if (durationMap && "customization" in durationMap) {
|
||||
let duration = aWindow.performance.now() - durationMap.customization;
|
||||
this._durations.customization.push(duration);
|
||||
delete durationMap.customization;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -456,7 +456,7 @@ MovieExtendsBox::~MovieExtendsBox()
|
||||
nsresult
|
||||
ChunkOffsetBox::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
// fragmented, we don't need time to sample table
|
||||
// We don't need time to sample table in fragmented mp4.
|
||||
entry_count = 0;
|
||||
size += sizeof(entry_count);
|
||||
*aBoxSize = size;
|
||||
@ -486,7 +486,7 @@ ChunkOffsetBox::~ChunkOffsetBox()
|
||||
nsresult
|
||||
SampleToChunkBox::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
// fragmented, we don't need time to sample table
|
||||
// We don't need time to sample table in fragmented mp4
|
||||
entry_count = 0;
|
||||
size += sizeof(entry_count);
|
||||
*aBoxSize = size;
|
||||
@ -516,7 +516,7 @@ SampleToChunkBox::~SampleToChunkBox()
|
||||
nsresult
|
||||
TimeToSampleBox::Generate(uint32_t* aBoxSize)
|
||||
{
|
||||
// fragmented, we don't need time to sample table
|
||||
// We don't need time to sample table in fragmented mp4.
|
||||
entry_count = 0;
|
||||
size += sizeof(entry_count);
|
||||
*aBoxSize = size;
|
||||
|
@ -20,6 +20,12 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* track type from spec 8.4.3.3
|
||||
*/
|
||||
#define Audio_Track 0x01
|
||||
#define Video_Track 0x02
|
||||
|
||||
class AACTrackMetadata;
|
||||
class AVCTrackMetadata;
|
||||
class ES_Descriptor;
|
||||
|
@ -28,7 +28,6 @@ ISOMediaWriter::ISOMediaWriter(uint32_t aType)
|
||||
, mBlobReady(false)
|
||||
, mType(0)
|
||||
{
|
||||
// TODO: replace Audio_Track/Video_Track with HAS_AUDIO/HAS_VIDEO
|
||||
if (aType & HAS_AUDIO) {
|
||||
mType |= Audio_Track;
|
||||
}
|
||||
@ -176,7 +175,7 @@ ISOMediaWriter::ReadyToRunState(bool& aEOS)
|
||||
if (!mVideoFragmentBuffer->HasEnoughData()) {
|
||||
bReadyToMux = false;
|
||||
}
|
||||
if (mAudioFragmentBuffer->EOS()) {
|
||||
if (mVideoFragmentBuffer->EOS()) {
|
||||
aEOS = true;
|
||||
bReadyToMux = true;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ private:
|
||||
// A flag to indicate the output buffer is ready to blob out.
|
||||
bool mBlobReady;
|
||||
|
||||
// Combination of HAS_AUDIO or HAS_VIDEO.
|
||||
// Combination of Audio_Track or Video_Track.
|
||||
uint32_t mType;
|
||||
};
|
||||
|
||||
|
@ -10,12 +10,6 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* track type from spec 8.4.3.3
|
||||
*/
|
||||
#define Audio_Track 0x01
|
||||
#define Video_Track 0x02
|
||||
|
||||
class AACTrackMetadata : public TrackMetadataBase {
|
||||
public:
|
||||
uint32_t SampleRate; // From 14496-3 table 1.16, it could be 7350 ~ 96000.
|
||||
|
@ -53,8 +53,14 @@ function handleResponse() {
|
||||
function handleRequest(request, response) {
|
||||
var query = getQuery(request);
|
||||
|
||||
// Default status when responding.
|
||||
var version = "1.1";
|
||||
var statusCode = 200;
|
||||
var description = "OK";
|
||||
|
||||
// Default values for content type, size and rate.
|
||||
var contentType = "text/plain";
|
||||
var contentRange = null;
|
||||
var size = 1024;
|
||||
var rate = 0;
|
||||
|
||||
@ -68,6 +74,39 @@ function handleRequest(request, response) {
|
||||
size = parseInt(query["size"]);
|
||||
}
|
||||
|
||||
// optional range request check.
|
||||
if (request.hasHeader("range")) {
|
||||
version = "1.1";
|
||||
statusCode = 206;
|
||||
description = "Partial Content";
|
||||
|
||||
// We'll only support simple range byte style requests.
|
||||
var [offset, total] = request.getHeader("range").slice("bytes=".length).split("-");
|
||||
// Enforce valid Number values.
|
||||
offset = parseInt(offset);
|
||||
offset = isNaN(offset) ? 0 : offset;
|
||||
// Same.
|
||||
total = parseInt(total);
|
||||
total = isNaN(total) ? 0 : total;
|
||||
|
||||
// We'll need to original total size as part of the Content-Range header
|
||||
// value in our response.
|
||||
var originalSize = size;
|
||||
|
||||
// If we have a total size requested, we must make sure to send that number
|
||||
// of bytes only (minus the start offset).
|
||||
if (total && total < size) {
|
||||
size = total - offset;
|
||||
} else if (offset) {
|
||||
// Looks like we just have a byte offset to deal with.
|
||||
size = size - offset;
|
||||
}
|
||||
|
||||
// We specifically need to add a Content-Range header to all responses for
|
||||
// requests that include a range request header.
|
||||
contentRange = "bytes " + offset + "-" + (size - 1) + "/" + originalSize;
|
||||
}
|
||||
|
||||
// optional rate (in bytes/s) at which to send the file.
|
||||
if ("rate" in query) {
|
||||
rate = parseInt(query["rate"]);
|
||||
@ -96,7 +135,11 @@ function handleRequest(request, response) {
|
||||
response.processAsync();
|
||||
|
||||
// generate the content.
|
||||
response.setStatusLine(version, statusCode, description);
|
||||
response.setHeader("Content-Type", contentType, false);
|
||||
if (contentRange) {
|
||||
response.setHeader("Content-Range", contentRange, false);
|
||||
}
|
||||
response.setHeader("Content-Length", size.toString(), false);
|
||||
|
||||
// initialize the timer and start writing out the response.
|
||||
|
@ -15,7 +15,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=938023
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<a href="serve_file.sjs?contentType=application/octet-stream&size=102400&rate=1024" download="test.bin" id="download1">Large Download</a>
|
||||
<a href="serve_file.sjs?contentType=application/octet-stream&size=102400&rate=10240" download="test.bin" id="download1">Large Download</a>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
@ -52,10 +52,10 @@ function checkDownloadList(downloads) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function checkResumedFailed(download) {
|
||||
ok(download.state == "stopped", "Download fails to resume.");
|
||||
function checkResumeSucceeded(download) {
|
||||
ok(download.state == "succeeded", "Download resumed successfully.");
|
||||
navigator.mozDownloadManager.clearAllDone()
|
||||
.then(checkDownloadList, error);
|
||||
.then(checkDownloadList, error);
|
||||
}
|
||||
|
||||
function downloadChange(evt) {
|
||||
@ -67,9 +67,8 @@ function downloadChange(evt) {
|
||||
} else if (download.state == "stopped" && !resuming) {
|
||||
resuming = true;
|
||||
ok(pausing, "Download stopped by pause()");
|
||||
// serve_file.sjs does not support resuming, so that should fail.
|
||||
download.resume()
|
||||
.then(error, function() { checkResumedFailed(download); });
|
||||
.then(function() { checkResumeSucceeded(download); }, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,9 +24,6 @@
|
||||
#define CHANNEL_WIDTH_100KHZ 100
|
||||
#define CHANNEL_WIDTH_50KHZ 50
|
||||
|
||||
#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
|
||||
#define SETTING_KEY_RIL_RADIO_DISABLED "ril.radio.disabled"
|
||||
|
||||
using namespace mozilla::hal;
|
||||
using mozilla::Preferences;
|
||||
|
||||
@ -93,12 +90,6 @@ FMRadioService::FMRadioService()
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
|
||||
if (NS_FAILED(obs->AddObserver(this,
|
||||
MOZSETTINGS_CHANGED_ID,
|
||||
/* useWeak */ false))) {
|
||||
NS_WARNING("Failed to add settings change observer!");
|
||||
}
|
||||
|
||||
RegisterFMRadioObserver(this);
|
||||
}
|
||||
|
||||
@ -446,7 +437,7 @@ FMRadioService::Enable(double aFrequencyInMHz,
|
||||
nsRefPtr<ReadRilSettingTask> callback =
|
||||
new ReadRilSettingTask(mPendingRequest);
|
||||
|
||||
rv = settingsLock->Get(SETTING_KEY_RIL_RADIO_DISABLED, callback);
|
||||
rv = settingsLock->Get("ril.radio.disabled", callback);
|
||||
if (NS_FAILED(rv)) {
|
||||
TransitionState(ErrorResponse(
|
||||
NS_LITERAL_STRING("Can't get settings lock")), Disabled);
|
||||
@ -463,25 +454,18 @@ FMRadioService::Enable(double aFrequencyInMHz,
|
||||
void
|
||||
FMRadioService::Disable(FMRadioReplyRunnable* aReplyRunnable)
|
||||
{
|
||||
// When airplane-mode is enabled, we will call this function from
|
||||
// FMRadioService::Observe without passing a FMRadioReplyRunnable,
|
||||
// so we have to check if |aReplyRunnable| is null before we dispatch it.
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
switch (mState) {
|
||||
case Disabling:
|
||||
if (aReplyRunnable) {
|
||||
aReplyRunnable->SetReply(
|
||||
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
}
|
||||
aReplyRunnable->SetReply(
|
||||
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
return;
|
||||
case Disabled:
|
||||
if (aReplyRunnable) {
|
||||
aReplyRunnable->SetReply(
|
||||
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
}
|
||||
aReplyRunnable->SetReply(
|
||||
ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
return;
|
||||
case Enabled:
|
||||
case Enabling:
|
||||
@ -515,10 +499,8 @@ FMRadioService::Disable(FMRadioReplyRunnable* aReplyRunnable)
|
||||
if (!mHasReadRilSetting) {
|
||||
SetState(Disabled);
|
||||
|
||||
if (aReplyRunnable) {
|
||||
aReplyRunnable->SetReply(SuccessResponse());
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
}
|
||||
aReplyRunnable->SetReply(SuccessResponse());
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -649,65 +631,6 @@ FMRadioService::CancelSeek(FMRadioReplyRunnable* aReplyRunnable)
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FMRadioService::Observe(nsISupports * aSubject,
|
||||
const char * aTopic,
|
||||
const char16_t * aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(sFMRadioService);
|
||||
|
||||
if (strcmp(aTopic, MOZSETTINGS_CHANGED_ID) != 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The string that we're interested in will be a JSON string looks like:
|
||||
// {"key":"ril.radio.disabled","value":true}
|
||||
AutoSafeJSContext cx;
|
||||
const nsDependentString dataStr(aData);
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
|
||||
!val.isObject()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObject& obj(val.toObject());
|
||||
JS::Rooted<JS::Value> key(cx);
|
||||
if (!JS_GetProperty(cx, &obj, "key", &key) ||
|
||||
!key.isString()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JS::Rooted<JSString*> jsKey(cx, key.toString());
|
||||
nsDependentJSString keyStr;
|
||||
if (!keyStr.init(cx, jsKey)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> value(cx);
|
||||
if (!JS_GetProperty(cx, &obj, "value", &value)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (keyStr.EqualsLiteral(SETTING_KEY_RIL_RADIO_DISABLED)) {
|
||||
if (!value.isBoolean()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mRilDisabled = value.toBoolean();
|
||||
mHasReadRilSetting = true;
|
||||
|
||||
// Disable the FM radio HW if Airplane mode is enabled.
|
||||
if (mRilDisabled) {
|
||||
Disable(nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioService::NotifyFMRadioEvent(FMRadioEventType aType)
|
||||
{
|
||||
@ -809,7 +732,7 @@ FMRadioService::Singleton()
|
||||
return sFMRadioService;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(FMRadioService, nsIObserver)
|
||||
NS_IMPL_ISUPPORTS0(FMRadioService)
|
||||
|
||||
END_FMRADIO_NAMESPACE
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
BEGIN_FMRADIO_NAMESPACE
|
||||
@ -137,8 +136,8 @@ enum FMRadioState
|
||||
};
|
||||
|
||||
class FMRadioService MOZ_FINAL : public IFMRadioService
|
||||
, public nsISupports
|
||||
, public hal::FMRadioObserver
|
||||
, public nsIObserver
|
||||
{
|
||||
friend class ReadRilSettingTask;
|
||||
friend class SetFrequencyRunnable;
|
||||
@ -172,8 +171,6 @@ public:
|
||||
/* FMRadioObserver */
|
||||
void Notify(const hal::FMRadioOperationInformation& aInfo) MOZ_OVERRIDE;
|
||||
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
protected:
|
||||
FMRadioService();
|
||||
|
||||
|
@ -10,5 +10,4 @@ qemu = false
|
||||
[test_one_seek_at_once.js]
|
||||
[test_seek_up_and_down.js]
|
||||
[test_bug862672.js]
|
||||
[test_bug876597.js]
|
||||
|
||||
|
@ -1,90 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 10000;
|
||||
|
||||
SpecialPowers.addPermission("fmradio", true, document);
|
||||
SpecialPowers.addPermission("settings-read", true, document);
|
||||
SpecialPowers.addPermission("settings-write", true, document);
|
||||
|
||||
let FMRadio = window.navigator.mozFMRadio;
|
||||
let mozSettings = window.navigator.mozSettings;
|
||||
let KEY = "ril.radio.disabled";
|
||||
|
||||
function verifyInitialState() {
|
||||
log("Verifying initial state.");
|
||||
ok(FMRadio);
|
||||
is(FMRadio.enabled, false);
|
||||
ok(mozSettings);
|
||||
|
||||
checkRilSettings();
|
||||
}
|
||||
|
||||
function checkRilSettings() {
|
||||
log("Checking airplane mode settings");
|
||||
let req = mozSettings.createLock().get(KEY);
|
||||
req.onsuccess = function(event) {
|
||||
ok(!req.result[KEY], "Airplane mode is disabled.");
|
||||
enableFMRadio();
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
ok(false, "Error occurs when reading settings value.");
|
||||
finish();
|
||||
};
|
||||
}
|
||||
|
||||
function enableFMRadio() {
|
||||
log("Enable FM radio");
|
||||
let frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
|
||||
let req = FMRadio.enable(frequency);
|
||||
|
||||
req.onsuccess = function() {
|
||||
enableAirplaneMode();
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
ok(false, "Failed to enable FM radio.");
|
||||
};
|
||||
}
|
||||
|
||||
function enableAirplaneMode() {
|
||||
log("Enable airplane mode");
|
||||
FMRadio.ondisabled = function() {
|
||||
FMRadio.ondisabled = null;
|
||||
enableFMRadioWithAirplaneModeEnabled();
|
||||
};
|
||||
|
||||
let settings = {};
|
||||
settings[KEY] = true;
|
||||
mozSettings.createLock().set(settings);
|
||||
}
|
||||
|
||||
function enableFMRadioWithAirplaneModeEnabled() {
|
||||
log("Enable FM radio with airplane mode enabled");
|
||||
let frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
|
||||
let req = FMRadio.enable(frequency);
|
||||
req.onerror = cleanUp();
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(false, "FMRadio could be enabled when airplane mode is enabled.");
|
||||
};
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
let settings = {};
|
||||
settings[KEY] = false;
|
||||
let req = mozSettings.createLock().set(settings);
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(!FMRadio.enabled);
|
||||
finish();
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
ok(false, "Error occurs when setting value");
|
||||
};
|
||||
}
|
||||
|
||||
verifyInitialState();
|
||||
|
@ -1238,12 +1238,14 @@ ContentParent::ContentParent(mozIApplication* aApp,
|
||||
bool aIsNuwaProcess /* = false */)
|
||||
: mOSPrivileges(aOSPrivileges)
|
||||
, mIsForBrowser(aIsForBrowser)
|
||||
, mIsNuwaProcess(aIsNuwaProcess)
|
||||
{
|
||||
InitializeMembers(); // Perform common initialization.
|
||||
|
||||
// No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated should
|
||||
// be true.
|
||||
MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1);
|
||||
// No more than one of !!aApp, aIsForBrowser, aIsForPreallocated, and
|
||||
// aIsNuwaProcess should be true.
|
||||
MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated + aIsNuwaProcess <=
|
||||
1);
|
||||
|
||||
// Insert ourselves into the global linked list of ContentParent objects.
|
||||
if (!sContentParents) {
|
||||
@ -1317,6 +1319,7 @@ ContentParent::ContentParent(ContentParent* aTemplate,
|
||||
: mOSPrivileges(aOSPrivileges)
|
||||
, mAppManifestURL(aAppManifestURL)
|
||||
, mIsForBrowser(false)
|
||||
, mIsNuwaProcess(false)
|
||||
{
|
||||
InitializeMembers(); // Perform common initialization.
|
||||
|
||||
@ -1497,6 +1500,14 @@ ContentParent::IsForApp()
|
||||
return !mAppManifestURL.IsEmpty();
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
bool
|
||||
ContentParent::IsNuwaProcess()
|
||||
{
|
||||
return mIsNuwaProcess;
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t
|
||||
ContentParent::Pid()
|
||||
{
|
||||
@ -1882,7 +1893,10 @@ ContentParent::Observe(nsISupports* aSubject,
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
else if (!strcmp(aTopic, "child-memory-reporter-request")) {
|
||||
unused << SendPMemoryReportRequestConstructor((uint32_t)(uintptr_t)aData);
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (!IsNuwaProcess())
|
||||
#endif
|
||||
unused << SendPMemoryReportRequestConstructor((uint32_t)(uintptr_t)aData);
|
||||
}
|
||||
else if (!strcmp(aTopic, "child-gc-request")){
|
||||
unused << SendGarbageCollect();
|
||||
|
@ -145,6 +145,9 @@ public:
|
||||
|
||||
bool IsAlive();
|
||||
bool IsForApp();
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
bool IsNuwaProcess();
|
||||
#endif
|
||||
|
||||
GeckoChildProcessHost* Process() {
|
||||
return mSubprocess;
|
||||
@ -560,6 +563,7 @@ private:
|
||||
|
||||
bool mSendPermissionUpdates;
|
||||
bool mIsForBrowser;
|
||||
bool mIsNuwaProcess;
|
||||
|
||||
// These variables track whether we've called Close(), CloseWithError()
|
||||
// and KillHard() on our channel.
|
||||
|
@ -1623,8 +1623,9 @@ TabChild::RecvHandleLongTap(const CSSIntPoint& aPoint)
|
||||
return true;
|
||||
}
|
||||
|
||||
DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"), aPoint, 2, 1, 0, false,
|
||||
nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
|
||||
mContextMenuHandled =
|
||||
DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"), aPoint, 2, 1, 0, false,
|
||||
nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -367,7 +367,7 @@ NfcWorker[NFC_NOTIFICATION_INITIALIZED] = function NFC_NOTIFICATION_INITIALIZED
|
||||
|
||||
NfcWorker[NFC_NOTIFICATION_TECH_DISCOVERED] = function NFC_NOTIFICATION_TECH_DISCOVERED() {
|
||||
debug("NFC_NOTIFICATION_TECH_DISCOVERED");
|
||||
let techs = [];
|
||||
let techList = [];
|
||||
let ndefMsgs = [];
|
||||
|
||||
let sessionId = Buf.readInt32();
|
||||
@ -375,7 +375,7 @@ NfcWorker[NFC_NOTIFICATION_TECH_DISCOVERED] = function NFC_NOTIFICATION_TECH_DIS
|
||||
for (let count = 0; count < techCount; count++) {
|
||||
let tech = NFC_TECHS[Buf.readUint8()];
|
||||
if (tech) {
|
||||
techs.push(tech);
|
||||
techList.push(tech);
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,7 +390,7 @@ NfcWorker[NFC_NOTIFICATION_TECH_DISCOVERED] = function NFC_NOTIFICATION_TECH_DIS
|
||||
}
|
||||
this.sendDOMMessage({type: "techDiscovered",
|
||||
sessionId: sessionId,
|
||||
tech: techs,
|
||||
techList: techList,
|
||||
ndef: ndefMsgs
|
||||
});
|
||||
};
|
||||
|
@ -96,9 +96,6 @@ protected:
|
||||
nsAutoTArray<FrameChildList,4> mLists;
|
||||
};
|
||||
|
||||
} // namespace layout
|
||||
} // namespace mozilla
|
||||
|
||||
inline mozilla::layout::FrameChildListIDs
|
||||
operator|(mozilla::layout::FrameChildListID aLeftOp,
|
||||
mozilla::layout::FrameChildListID aRightOp)
|
||||
@ -114,6 +111,9 @@ operator|(mozilla::layout::FrameChildListID aLeftOp,
|
||||
return mozilla::layout::FrameChildListIDs(aLeftOp) | aRightOp;
|
||||
}
|
||||
|
||||
} // namespace layout
|
||||
} // namespace mozilla
|
||||
|
||||
inline void nsFrameList::AppendIfNonempty(
|
||||
nsTArray<mozilla::layout::FrameChildList>* aLists,
|
||||
mozilla::layout::FrameChildListID aListID) const
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package org.mozilla.gecko.tests.helpers;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.mozilla.gecko.Assert;
|
||||
import org.mozilla.gecko.tests.UITestContext;
|
||||
|
||||
@ -22,11 +24,41 @@ public final class AssertionHelper {
|
||||
sAsserter = context.getAsserter();
|
||||
}
|
||||
|
||||
public static void assertEquals(final String message, final Object expected, final Object actual) {
|
||||
public static void assertArrayEquals(final String message, final byte[] expecteds, final byte[] actuals) {
|
||||
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
|
||||
}
|
||||
|
||||
public static void assertArrayEquals(final String message, final char[] expecteds, final char[] actuals) {
|
||||
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
|
||||
}
|
||||
|
||||
public static void assertArrayEquals(final String message, final short[] expecteds, final short[] actuals) {
|
||||
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
|
||||
}
|
||||
|
||||
public static void assertArrayEquals(final String message, final int[] expecteds, final int[] actuals) {
|
||||
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
|
||||
}
|
||||
|
||||
public static void assertArrayEquals(final String message, final long[] expecteds, final long[] actuals) {
|
||||
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
|
||||
}
|
||||
|
||||
public static void assertArrayEquals(final String message, final Object[] expecteds, final Object[] actuals) {
|
||||
sAsserter.ok(Arrays.equals(expecteds, actuals), message, DIAG_STRING);
|
||||
}
|
||||
|
||||
public static void assertEquals(final String message, final double expected, final double actual, final double delta) {
|
||||
if (Double.compare(expected, actual) != 0) {
|
||||
sAsserter.ok(Math.abs(expected - actual) <= delta, message, DIAG_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertEquals(final String message, final long expected, final long actual) {
|
||||
sAsserter.is(actual, expected, message);
|
||||
}
|
||||
|
||||
public static void assertEquals(final String message, final int expected, final int actual) {
|
||||
public static void assertEquals(final String message, final Object expected, final Object actual) {
|
||||
sAsserter.is(actual, expected, message);
|
||||
}
|
||||
|
||||
@ -38,10 +70,18 @@ public final class AssertionHelper {
|
||||
sAsserter.isnot(actual, null, message);
|
||||
}
|
||||
|
||||
public static void assertNotSame(final String message, final Object unexpected, final Object actual) {
|
||||
sAsserter.ok(unexpected != actual, message, DIAG_STRING);
|
||||
}
|
||||
|
||||
public static void assertNull(final String message, final Object actual) {
|
||||
sAsserter.is(actual, null, message);
|
||||
}
|
||||
|
||||
public static void assertSame(final String message, final Object expected, final Object actual) {
|
||||
sAsserter.ok(expected == actual, message, DIAG_STRING);
|
||||
}
|
||||
|
||||
public static void assertTrue(final String message, final boolean actual) {
|
||||
sAsserter.ok(actual, message, DIAG_STRING);
|
||||
}
|
||||
|
@ -28,6 +28,10 @@ var ContextMenus = {
|
||||
|
||||
init: function() {
|
||||
document.addEventListener("contextmenu", this, false);
|
||||
|
||||
document.getElementById("contextmenu-enable").addEventListener("click", ContextMenus.enable.bind(this), false);
|
||||
document.getElementById("contextmenu-disable").addEventListener("click", ContextMenus.disable.bind(this), false);
|
||||
document.getElementById("contextmenu-uninstall").addEventListener("click", ContextMenus.uninstall.bind(this), false);
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
@ -82,20 +86,24 @@ function init() {
|
||||
|
||||
AddonManager.addInstallListener(Addons);
|
||||
AddonManager.addAddonListener(Addons);
|
||||
Addons.getAddons();
|
||||
Addons.init();
|
||||
showList();
|
||||
ContextMenus.init();
|
||||
|
||||
document.getElementById("header-button").addEventListener("click", openLink, false);
|
||||
}
|
||||
|
||||
|
||||
function uninit() {
|
||||
AddonManager.removeInstallListener(Addons);
|
||||
AddonManager.removeAddonListener(Addons);
|
||||
}
|
||||
|
||||
function openLink(aElement) {
|
||||
function openLink(aEvent) {
|
||||
try {
|
||||
let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
|
||||
let url = formatter.formatURLPref(aElement.getAttribute("pref"));
|
||||
|
||||
let url = formatter.formatURLPref(aEvent.currentTarget.getAttribute("pref"));
|
||||
let BrowserApp = gChromeWin.BrowserApp;
|
||||
BrowserApp.addTab(url, { selected: true, parentId: BrowserApp.selectedTab.id });
|
||||
} catch (ex) {}
|
||||
@ -179,9 +187,8 @@ var Addons = {
|
||||
let outer = document.createElement("div");
|
||||
outer.className = "addon-item list-item";
|
||||
outer.setAttribute("role", "button");
|
||||
outer.addEventListener("click", function() {
|
||||
openLink(document.getElementById("header-button"));
|
||||
}.bind(this), true);
|
||||
outer.setAttribute("pref", "extensions.getAddons.browseAddons");
|
||||
outer.addEventListener("click", openLink, true);
|
||||
|
||||
let img = document.createElement("img");
|
||||
img.className = "icon";
|
||||
@ -194,7 +201,7 @@ var Addons = {
|
||||
let title = document.createElement("div");
|
||||
title.id = "browse-title";
|
||||
title.className = "title";
|
||||
title.textContent = gStringBundle.GetStringFromName("addons.browseAll");;
|
||||
title.textContent = gStringBundle.GetStringFromName("addons.browseAll");
|
||||
inner.appendChild(title);
|
||||
|
||||
outer.appendChild(inner);
|
||||
@ -238,7 +245,7 @@ var Addons = {
|
||||
return element;
|
||||
},
|
||||
|
||||
getAddons: function getAddons() {
|
||||
init: function init() {
|
||||
let self = this;
|
||||
AddonManager.getAddonsByTypes(["extension", "theme", "locale"], function(aAddons) {
|
||||
// Clear all content before filling the addons
|
||||
@ -254,6 +261,11 @@ var Addons = {
|
||||
let browseItem = self._createBrowseItem();
|
||||
list.appendChild(browseItem);
|
||||
});
|
||||
|
||||
document.getElementById("uninstall-btn").addEventListener("click", Addons.uninstallCurrent.bind(this), false);
|
||||
document.getElementById("cancel-btn").addEventListener("click", Addons.cancelUninstall.bind(this), false);
|
||||
document.getElementById("disable-btn").addEventListener("click", Addons.disable.bind(this), false);
|
||||
document.getElementById("enable-btn").addEventListener("click", Addons.enable.bind(this), false);
|
||||
},
|
||||
|
||||
_getOpTypeForOperations: function _getOpTypeForOperations(aOperations) {
|
||||
@ -421,24 +433,33 @@ var Addons = {
|
||||
this.setEnabled(false);
|
||||
},
|
||||
|
||||
uninstall: function uninstall(aAddon) {
|
||||
let list = document.getElementById("addons-list");
|
||||
uninstallCurrent: function uninstallCurrent() {
|
||||
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||
|
||||
let addon = aAddon || detailItem.addon;
|
||||
let addon = detailItem.addon;
|
||||
if (!addon)
|
||||
return;
|
||||
|
||||
let listItem = this._getElementForAddon(addon.id);
|
||||
this.uninstall(addon);
|
||||
},
|
||||
|
||||
addon.uninstall();
|
||||
if (addon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
|
||||
uninstall: function uninstall(aAddon) {
|
||||
let list = document.getElementById("addons-list");
|
||||
|
||||
if (!aAddon) {
|
||||
return;
|
||||
}
|
||||
|
||||
let listItem = this._getElementForAddon(aAddon.id);
|
||||
|
||||
aAddon.uninstall();
|
||||
if (aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
|
||||
this.showRestart();
|
||||
|
||||
// A disabled addon doesn't need a restart so it has no pending ops and
|
||||
// can't be cancelled
|
||||
let opType = this._getOpTypeForOperations(addon.pendingOperations);
|
||||
if (!addon.isActive && opType == "")
|
||||
let opType = this._getOpTypeForOperations(aAddon.pendingOperations);
|
||||
if (!aAddon.isActive && opType == "")
|
||||
opType = "needs-uninstall";
|
||||
|
||||
detailItem.setAttribute("opType", opType);
|
||||
@ -518,3 +539,6 @@ var Addons = {
|
||||
onDownloadCancelled: function(aInstall) {
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", init, false);
|
||||
window.addEventListener("unload", uninit, false);
|
||||
|
@ -21,52 +21,23 @@
|
||||
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
|
||||
<link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://browser/skin/aboutAddons.css" type="text/css"/>
|
||||
<style>
|
||||
.hide-on-enable,
|
||||
.show-on-error,
|
||||
.show-on-uninstall,
|
||||
.show-on-install,
|
||||
.show-on-restart,
|
||||
div[isDisabled="true"] .hide-on-disable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div[error] .show-on-error,
|
||||
div[opType="needs-restart"] .show-on-restart,
|
||||
div[opType="needs-uninstall"] .show-on-uninstall,
|
||||
div[opType="needs-install"] .show-on-install,
|
||||
div[opType="needs-enable"] .show-on-enable,
|
||||
div[opType="needs-disable"] .show-on-disable,
|
||||
div[isDisabled="true"] .show-on-disable {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
div[opType="needs-restart"] .hide-on-restart,
|
||||
div[opType="needs-uninstall"] .hide-on-uninstall,
|
||||
div[isDisabled="true"][opType="needs-uninstall"],
|
||||
div[opType="needs-install"] .hide-on-install,
|
||||
div[opType="needs-enable"] .hide-on-enable,
|
||||
div[opType="needs-disable"] .hide-on-disable {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;" onload="init();" onunload="uninit();">
|
||||
<body dir="&locale.dir;">
|
||||
<menu type="context" id="addonmenu">
|
||||
<menuitem id="contextmenu-enable" label="&addonAction.enable;" onclick="ContextMenus.enable();"></menuitem>
|
||||
<menuitem id="contextmenu-disable" label="&addonAction.disable;" onclick="ContextMenus.disable();"></menuitem>
|
||||
<menuitem id="contextmenu-uninstall" label="&addonAction.uninstall;" onclick="ContextMenus.uninstall();"></menuitem>
|
||||
<menuitem id="contextmenu-enable" label="&addonAction.enable;"></menuitem>
|
||||
<menuitem id="contextmenu-disable" label="&addonAction.disable;" ></menuitem>
|
||||
<menuitem id="contextmenu-uninstall" label="&addonAction.uninstall;" ></menuitem>
|
||||
</menu>
|
||||
|
||||
<div id="addons-header" class="header">
|
||||
<div>&aboutAddons.header2;</div>
|
||||
<div id="header-button" role="button" aria-label="&aboutAddons.browseAll;" pref="extensions.getAddons.browseAddons" onclick="openLink(this);"/>
|
||||
<div id="header-button" role="button" aria-label="&aboutAddons.browseAll;" pref="extensions.getAddons.browseAddons" />
|
||||
</div>
|
||||
<div id="addons-list" class="list" style="display: none;">
|
||||
<div id="addons-list" class="list">
|
||||
</div>
|
||||
|
||||
<div id="addons-details" class="list" style="display: none">
|
||||
<div id="addons-details" class="list">
|
||||
<div class="addon-item list-item">
|
||||
<img class="icon"/>
|
||||
<div class="inner">
|
||||
@ -79,10 +50,10 @@
|
||||
</div>
|
||||
<div class="status status-uninstalled show-on-uninstall"></div>
|
||||
<div class="buttons">
|
||||
<button id="enable-btn" class="show-on-disable hide-on-enable hide-on-uninstall" onclick="Addons.enable();">&addonAction.enable;</button>
|
||||
<button id="disable-btn" class="show-on-enable hide-on-disable hide-on-uninstall" onclick="Addons.disable();">&addonAction.disable;</button>
|
||||
<button id="uninstall-btn" class="hide-on-uninstall" onclick="Addons.uninstall();">&addonAction.uninstall;</button>
|
||||
<button id="cancel-btn" class="show-on-uninstall" onclick="Addons.cancelUninstall();">&addonAction.undo;</button>
|
||||
<button id="enable-btn" class="show-on-disable hide-on-enable hide-on-uninstall" >&addonAction.enable;</button>
|
||||
<button id="disable-btn" class="show-on-enable hide-on-disable hide-on-uninstall" >&addonAction.disable;</button>
|
||||
<button id="uninstall-btn" class="hide-on-uninstall" >&addonAction.uninstall;</button>
|
||||
<button id="cancel-btn" class="show-on-uninstall" >&addonAction.undo;</button>
|
||||
</div>
|
||||
<div class="options-header">&aboutAddons.options;</div>
|
||||
<div class="options-box"></div>
|
||||
|
@ -9,6 +9,7 @@ let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
document.addEventListener("DOMContentLoaded", init, false);
|
||||
|
||||
function dump(a) {
|
||||
Services.console.logStringMessage(a);
|
||||
@ -19,6 +20,25 @@ function sendMessageToJava(aMessage) {
|
||||
}
|
||||
|
||||
function init() {
|
||||
let anchors = document.querySelectorAll(".maybe-later");
|
||||
for(let anchor of anchors) {
|
||||
anchor.addEventListener("click", maybeLater, false);
|
||||
}
|
||||
document.getElementById("happy-link").addEventListener("click", function(evt) {
|
||||
switchSection("happy");
|
||||
}, false);
|
||||
document.getElementById("sad-link").addEventListener("click", function(evt) {
|
||||
switchSection("sad");
|
||||
}, false);
|
||||
|
||||
window.addEventListener("unload", uninit, false);
|
||||
|
||||
document.getElementById("open-play-store").addEventListener("click", openPlayStore, false);
|
||||
document.forms[0].addEventListener("submit", sendFeedback, false);
|
||||
document.getElementById("no-thanks").addEventListener("click", function(evt) {
|
||||
window.close();
|
||||
}, false);
|
||||
|
||||
let sumoLink = Services.urlFormatter.formatURLPref("app.support.baseURL");
|
||||
document.getElementById("sumo-link").href = sumoLink;
|
||||
|
||||
|
@ -22,19 +22,19 @@
|
||||
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;" onload="init();" onunload="uninit();">
|
||||
<body dir="&locale.dir;">
|
||||
|
||||
<section id="intro" active="true">
|
||||
<h1 class="header">&intro.header;</h1>
|
||||
<div class="message">&intro.message;</div>
|
||||
<div class="link-box" onclick="switchSection('happy');">
|
||||
<div id="happy-link" class="link-box">
|
||||
<a>&intro.happyLink;</a>
|
||||
</div>
|
||||
<div class="link-box-bottom" onclick="switchSection('sad');">
|
||||
<div id="sad-link" class="link-box-bottom">
|
||||
<a>&intro.sadLink;</a>
|
||||
</div>
|
||||
<div class="bottom-links">
|
||||
<a onclick="maybeLater();">&intro.maybeLater;</a>
|
||||
<a class="maybe-later">&intro.maybeLater;</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -44,18 +44,18 @@
|
||||
<div class="message">&happy.message;</div>
|
||||
<div class="fine-print">&happy.finePrint;</div>
|
||||
</div>
|
||||
<div class="link-box-bottom" onclick="openPlayStore();">
|
||||
<div id="open-play-store" class="link-box-bottom">
|
||||
<div class="stars"/>
|
||||
<a>&happy.ratingLink;</a>
|
||||
</div>
|
||||
<div class="bottom-links">
|
||||
<a onclick="maybeLater();">&happy.maybeLater2;</a>
|
||||
<a onclick="window.close();">&happy.noThanks;</a>
|
||||
<a class="maybe-later">&happy.maybeLater2;</a>
|
||||
<a id="no-thanks">&happy.noThanks;</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="sad">
|
||||
<form onsubmit="sendFeedback(event);">
|
||||
<form>
|
||||
<div class="message">&sad.message;</div>
|
||||
<textarea class="description" placeholder="&sad.placeholder;" rows="8" required="true"/>
|
||||
<div class="message">&sad.lastSite;</div>
|
||||
|
@ -239,3 +239,35 @@ setting[type="menulist"] {
|
||||
display: -moz-box;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-multi");
|
||||
}
|
||||
|
||||
.hide-on-enable,
|
||||
.show-on-error,
|
||||
.show-on-uninstall,
|
||||
.show-on-install,
|
||||
.show-on-restart,
|
||||
div[isDisabled="true"] .hide-on-disable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div[error] .show-on-error,
|
||||
div[opType="needs-restart"] .show-on-restart,
|
||||
div[opType="needs-uninstall"] .show-on-uninstall,
|
||||
div[opType="needs-install"] .show-on-install,
|
||||
div[opType="needs-enable"] .show-on-enable,
|
||||
div[opType="needs-disable"] .show-on-disable,
|
||||
div[isDisabled="true"] .show-on-disable {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
div[opType="needs-restart"] .hide-on-restart,
|
||||
div[opType="needs-uninstall"] .hide-on-uninstall,
|
||||
div[isDisabled="true"][opType="needs-uninstall"],
|
||||
div[opType="needs-install"] .hide-on-install,
|
||||
div[opType="needs-enable"] .hide-on-enable,
|
||||
div[opType="needs-disable"] .hide-on-disable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#addons-list, #addons-details {
|
||||
display: none;
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ InternalMethods.prototype = {
|
||||
* sessionToken: Session for the FxA server
|
||||
* kA: An encryption key from the FxA server
|
||||
* kB: An encryption key derived from the user's FxA password
|
||||
* isVerified: email verification status
|
||||
* verified: email verification status
|
||||
* }
|
||||
* or null if no user is signed in
|
||||
*/
|
||||
@ -305,7 +305,7 @@ InternalMethods.prototype = {
|
||||
},
|
||||
|
||||
isUserEmailVerified: function isUserEmailVerified(data) {
|
||||
return !!(data && data.isVerified);
|
||||
return !!(data && data.verified);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -334,7 +334,7 @@ InternalMethods.prototype = {
|
||||
},
|
||||
|
||||
whenVerified: function(data) {
|
||||
if (data.isVerified) {
|
||||
if (data.verified) {
|
||||
log.debug("already verified");
|
||||
return Promise.resolve(data);
|
||||
}
|
||||
@ -388,7 +388,7 @@ InternalMethods.prototype = {
|
||||
// off or stop polling altogether
|
||||
this.getUserAccountData()
|
||||
.then((data) => {
|
||||
data.isVerified = true;
|
||||
data.verified = true;
|
||||
return this.setUserAccountData(data);
|
||||
})
|
||||
.then((data) => {
|
||||
@ -456,7 +456,7 @@ this.FxAccounts.prototype = Object.freeze({
|
||||
|
||||
// set() makes sure that polling is happening, if necessary.
|
||||
// get() does not wait for verification, and returns an object even if
|
||||
// unverified. The caller of get() must check .isVerified .
|
||||
// unverified. The caller of get() must check .verified .
|
||||
// The "fxaccounts:onlogin" event will fire only when the verified state
|
||||
// goes from false to true, so callers must register their observer
|
||||
// and then call get(). In particular, it will not fire when the account
|
||||
@ -464,7 +464,7 @@ this.FxAccounts.prototype = Object.freeze({
|
||||
// the account is verified, the event will never fire. So callers must do:
|
||||
// register notification observer (go)
|
||||
// userdata = get()
|
||||
// if (userdata.isVerified()) {go()}
|
||||
// if (userdata.verified()) {go()}
|
||||
|
||||
/**
|
||||
* Set the current user signed in to Firefox Accounts.
|
||||
@ -477,7 +477,7 @@ this.FxAccounts.prototype = Object.freeze({
|
||||
* uid: The user's unique id
|
||||
* sessionToken: Session for the FxA server
|
||||
* keyFetchToken: an unused keyFetchToken
|
||||
* isVerified: true/false
|
||||
* verified: true/false
|
||||
* }
|
||||
* @return Promise
|
||||
* The promise resolves to null when the data is saved
|
||||
@ -512,7 +512,7 @@ this.FxAccounts.prototype = Object.freeze({
|
||||
* sessionToken: Session for the FxA server
|
||||
* kA: An encryption key from the FxA server
|
||||
* kB: An encryption key derived from the user's FxA password
|
||||
* isVerified: email verification status
|
||||
* verified: email verification status
|
||||
* }
|
||||
* or null if no user is signed in.
|
||||
*/
|
||||
|
@ -88,7 +88,7 @@ this.FxAccountsClient.prototype = {
|
||||
* {
|
||||
* uid: the user's unique ID
|
||||
* sessionToken: a session token
|
||||
* isVerified: flag indicating verification status of the email
|
||||
* verified: flag indicating verification status of the email
|
||||
* }
|
||||
*/
|
||||
signIn: function signIn(email, password) {
|
||||
|
@ -138,7 +138,7 @@ add_task(function test_get_signed_in_user_initially_unset() {
|
||||
sessionToken: "dead",
|
||||
kA: "beef",
|
||||
kB: "cafe",
|
||||
isVerified: true
|
||||
verified: true
|
||||
};
|
||||
|
||||
let result = yield account.getSignedInUser();
|
||||
@ -203,7 +203,7 @@ add_test(function test_verification_poll() {
|
||||
// Once email verification is complete, we will observe onlogin
|
||||
fxa.internal.getUserAccountData().then(user => {
|
||||
// And confirm that the user's state has changed
|
||||
do_check_eq(user.isVerified, true);
|
||||
do_check_eq(user.verified, true);
|
||||
do_check_eq(user.email, test_user.email);
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
@ -213,7 +213,7 @@ add_test(function test_verification_poll() {
|
||||
fxa.setSignedInUser(test_user).then(() => {
|
||||
fxa.internal.getUserAccountData().then(user => {
|
||||
// The user is signing in, but email has not been verified yet
|
||||
do_check_eq(user.isVerified, false);
|
||||
do_check_eq(user.verified, false);
|
||||
do_timeout(200, function() {
|
||||
// Mock email verification ...
|
||||
fxa.internal.fxAccountsClient._email = test_user.email;
|
||||
@ -268,7 +268,7 @@ add_test(function test_getKeys() {
|
||||
let user = getTestUser("eusebius");
|
||||
|
||||
// Once email has been verified, we will be able to get keys
|
||||
user.isVerified = true;
|
||||
user.verified = true;
|
||||
|
||||
fxa.setSignedInUser(user).then(() => {
|
||||
fxa.getSignedInUser().then((user) => {
|
||||
@ -282,7 +282,7 @@ add_test(function test_getKeys() {
|
||||
fxa.getSignedInUser().then((user) => {
|
||||
// Now we should have keys
|
||||
do_check_eq(fxa.internal.isUserEmailVerified(user), true);
|
||||
do_check_eq(!!user.isVerified, true);
|
||||
do_check_eq(!!user.verified, true);
|
||||
do_check_eq(user.kA, expandHex("11"));
|
||||
do_check_eq(user.kB, expandHex("66"));
|
||||
do_check_eq(user.keyFetchToken, undefined);
|
||||
@ -334,7 +334,7 @@ add_test(function test_overlapping_signins() {
|
||||
// Once email verification is complete, we will observe onlogin
|
||||
fxa.internal.getUserAccountData().then(user => {
|
||||
do_check_eq(user.email, bob.email);
|
||||
do_check_eq(user.isVerified, true);
|
||||
do_check_eq(user.verified, true);
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
});
|
||||
@ -345,7 +345,7 @@ add_test(function test_overlapping_signins() {
|
||||
log.debug("Alice signing in ...");
|
||||
fxa.internal.getUserAccountData().then(user => {
|
||||
do_check_eq(user.email, alice.email);
|
||||
do_check_eq(user.isVerified, false);
|
||||
do_check_eq(user.verified, false);
|
||||
log.debug("Alice has not verified her email ...");
|
||||
|
||||
// Now Bob signs in instead and actually verifies his email
|
||||
@ -372,9 +372,9 @@ add_task(function test_getAssertion() {
|
||||
sessionToken: "sessionToken",
|
||||
kA: expandHex("11"),
|
||||
kB: expandHex("66"),
|
||||
isVerified: true
|
||||
verified: true
|
||||
};
|
||||
// By putting kA/kB/isVerified in "creds", we skip ahead
|
||||
// By putting kA/kB/verified in "creds", we skip ahead
|
||||
// to the "we're ready" stage.
|
||||
yield fxa.setSignedInUser(creds);
|
||||
|
||||
@ -471,7 +471,7 @@ function getTestUser(name) {
|
||||
sessionToken: name + "'s session token",
|
||||
keyFetchToken: name + "'s keyfetch token",
|
||||
unwrapBKey: expandHex("44"),
|
||||
isVerified: false
|
||||
verified: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ this.makeIdentityConfig = function(overrides) {
|
||||
kB: 'kB',
|
||||
sessionToken: 'sessionToken',
|
||||
uid: 'user_uid',
|
||||
isVerified: true,
|
||||
verified: true,
|
||||
},
|
||||
token: {
|
||||
endpoint: Svc.Prefs.get("tokenServerURI"),
|
||||
|
@ -192,11 +192,13 @@
|
||||
"content/html/content/test/test_iframe_sandbox_popups.html":"multiple concurrent window.open()s fail on B2G",
|
||||
"content/html/content/test/test_iframe_sandbox_popups_inheritance.html":"multiple concurrent window.open()s fail on B2G",
|
||||
"content/html/content/test/test_iframe_sandbox_modal.html":"modal tests fail on B2G",
|
||||
"content/html/content/test/test_mozaudiochannel.html":"Perma-orange on debug emulator",
|
||||
|
||||
"content/html/content/test/test_iframe_sandbox_plugins.html":"plugins not supported",
|
||||
"content/html/content/test/test_object_plugin_nav.html":"plugins not supported",
|
||||
"content/html/document/test/test_bug741266.html":"needs control of popup window size",
|
||||
"docshell/test/navigation/test_popup-navigates-children.html":"Needs multiple window.open support, also uses docshelltreenode",
|
||||
"docshell/test/navigation/test_sessionhistory.html":"Perma-orange on debug emulator builds",
|
||||
"docshell/test/test_bug590573.html":"queryinterfaces into webnavigation, might suffer from something similar as bug 823022",
|
||||
"dom/devicestorage/ipc/test_ipc.html":"nested ipc not working",
|
||||
|
||||
|
@ -585,21 +585,30 @@ this.SocialService = {
|
||||
},
|
||||
|
||||
installProvider: function(aDOMDocument, data, installCallback) {
|
||||
let manifest;
|
||||
let installOrigin = aDOMDocument.nodePrincipal.origin;
|
||||
|
||||
let id = getAddonIDFromOrigin(installOrigin);
|
||||
let version = data && data.version ? data.version : "0";
|
||||
if (Services.blocklist.getAddonBlocklistState(id, version) == Ci.nsIBlocklistService.STATE_BLOCKED)
|
||||
throw new Error("installProvider: provider with origin [" +
|
||||
installOrigin + "] is blocklisted");
|
||||
if (data) {
|
||||
let installType = getOriginActivationType(installOrigin);
|
||||
// if we get data, we MUST have a valid manifest generated from the data
|
||||
manifest = this._manifestFromData(installType, data, aDOMDocument.nodePrincipal);
|
||||
if (!manifest)
|
||||
throw new Error("SocialService.installProvider: service configuration is invalid from " + aDOMDocument.location.href);
|
||||
|
||||
let addon = new AddonWrapper(manifest);
|
||||
if (addon && addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
|
||||
throw new Error("installProvider: provider with origin [" +
|
||||
installOrigin + "] is blocklisted");
|
||||
}
|
||||
|
||||
let id = getAddonIDFromOrigin(installOrigin);
|
||||
AddonManager.getAddonByID(id, function(aAddon) {
|
||||
if (aAddon && aAddon.userDisabled) {
|
||||
aAddon.cancelUninstall();
|
||||
aAddon.userDisabled = false;
|
||||
}
|
||||
schedule(function () {
|
||||
this._installProvider(aDOMDocument, data, aManifest => {
|
||||
this._installProvider(aDOMDocument, manifest, aManifest => {
|
||||
this._notifyProviderListeners("provider-installed", aManifest.origin);
|
||||
installCallback(aManifest);
|
||||
});
|
||||
@ -607,18 +616,11 @@ this.SocialService = {
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_installProvider: function(aDOMDocument, data, installCallback) {
|
||||
_installProvider: function(aDOMDocument, manifest, installCallback) {
|
||||
let sourceURI = aDOMDocument.location.href;
|
||||
let installOrigin = aDOMDocument.nodePrincipal.origin;
|
||||
|
||||
let installType = getOriginActivationType(installOrigin);
|
||||
let manifest;
|
||||
if (data) {
|
||||
// if we get data, we MUST have a valid manifest generated from the data
|
||||
manifest = this._manifestFromData(installType, data, aDOMDocument.nodePrincipal);
|
||||
if (!manifest)
|
||||
throw new Error("SocialService.installProvider: service configuration is invalid from " + sourceURI);
|
||||
}
|
||||
let installer;
|
||||
switch(installType) {
|
||||
case "foreign":
|
||||
@ -662,6 +664,10 @@ this.SocialService = {
|
||||
}
|
||||
},
|
||||
|
||||
createWrapper: function(manifest) {
|
||||
return new AddonWrapper(manifest);
|
||||
},
|
||||
|
||||
/**
|
||||
* updateProvider is used from the worker to self-update. Since we do not
|
||||
* have knowledge of the currently selected provider here, we will notify
|
||||
@ -716,8 +722,8 @@ function SocialProvider(input) {
|
||||
if (!input.origin)
|
||||
throw new Error("SocialProvider must be passed an origin");
|
||||
|
||||
let id = getAddonIDFromOrigin(input.origin);
|
||||
if (Services.blocklist.getAddonBlocklistState(id, input.version || "0") == Ci.nsIBlocklistService.STATE_BLOCKED)
|
||||
let addon = new AddonWrapper(input);
|
||||
if (addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
|
||||
throw new Error("SocialProvider: provider with origin [" +
|
||||
input.origin + "] is blocklisted");
|
||||
|
||||
@ -1011,8 +1017,8 @@ var SocialAddonProvider = {
|
||||
for (let manifest of SocialServiceInternal.manifests) {
|
||||
try {
|
||||
if (ActiveProviders.has(manifest.origin)) {
|
||||
let id = getAddonIDFromOrigin(manifest.origin);
|
||||
if (Services.blocklist.getAddonBlocklistState(id, manifest.version || "0") != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
|
||||
let addon = new AddonWrapper(manifest);
|
||||
if (addon.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
|
||||
SocialService.removeProvider(manifest.origin);
|
||||
}
|
||||
}
|
||||
@ -1100,11 +1106,11 @@ AddonWrapper.prototype = {
|
||||
},
|
||||
|
||||
get blocklistState() {
|
||||
return Services.blocklist.getAddonBlocklistState(this.id, this.version || "0");
|
||||
return Services.blocklist.getAddonBlocklistState(this);
|
||||
},
|
||||
|
||||
get blocklistURL() {
|
||||
return Services.blocklist.getAddonBlocklistURL(this.id, this.version || "0");
|
||||
return Services.blocklist.getAddonBlocklistURL(this);
|
||||
},
|
||||
|
||||
get screenshots() {
|
||||
|
File diff suppressed because it is too large
Load Diff
1113
toolkit/components/telemetry/TelemetryPing.jsm
Normal file
1113
toolkit/components/telemetry/TelemetryPing.jsm
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,2 @@
|
||||
component {55d6a5fa-130e-4ee6-a158-0133af3b86ba} TelemetryPing.js
|
||||
contract @mozilla.org/base/telemetry-ping;1 {55d6a5fa-130e-4ee6-a158-0133af3b86ba}
|
||||
category profile-after-change TelemetryPing @mozilla.org/base/telemetry-ping;1
|
||||
|
28
toolkit/components/telemetry/TelemetryStartup.js
Normal file
28
toolkit/components/telemetry/TelemetryStartup.js
Normal file
@ -0,0 +1,28 @@
|
||||
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm")
|
||||
|
||||
/**
|
||||
* TelemetryStartup is needed to forward the "profile-after-change" notification
|
||||
* to TelemetryPing.jsm.
|
||||
*/
|
||||
function TelemetryStartup() {
|
||||
}
|
||||
|
||||
TelemetryStartup.prototype.classID = Components.ID("{117b219f-92fe-4bd2-a21b-95a342a9d474}");
|
||||
TelemetryStartup.prototype.QueryInterface = XPCOMUtils.generateQI([Components.interfaces.nsIObserver])
|
||||
TelemetryStartup.prototype.observe = function(aSubject, aTopic, aData){
|
||||
if (aTopic == "profile-after-change") {
|
||||
TelemetryPing.observe(null, "profile-after-change", null);
|
||||
}
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelemetryStartup]);
|
3
toolkit/components/telemetry/TelemetryStartup.manifest
Normal file
3
toolkit/components/telemetry/TelemetryStartup.manifest
Normal file
@ -0,0 +1,3 @@
|
||||
component {117b219f-92fe-4bd2-a21b-95a342a9d474} TelemetryStartup.js
|
||||
contract @mozilla.org/base/telemetry-startup;1 {117b219f-92fe-4bd2-a21b-95a342a9d474}
|
||||
category profile-after-change TelemetryStartup @mozilla.org/base/telemetry-startup;1
|
@ -24,11 +24,10 @@ SOURCES += [
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'TelemetryPing.manifest',
|
||||
]
|
||||
|
||||
EXTRA_PP_COMPONENTS += [
|
||||
'TelemetryPing.js',
|
||||
'TelemetryPing.manifest',
|
||||
'TelemetryStartup.js',
|
||||
'TelemetryStartup.manifest'
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
@ -38,6 +37,7 @@ EXTRA_JS_MODULES += [
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'TelemetryPing.jsm',
|
||||
'UITelemetry.jsm',
|
||||
]
|
||||
|
||||
|
@ -17,6 +17,7 @@ Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
|
||||
const IGNORE_HISTOGRAM = "test::ignore_me";
|
||||
const IGNORE_HISTOGRAM_TO_CLONE = "MEMORY_HEAP_ALLOCATED";
|
||||
@ -38,7 +39,6 @@ const NUMBER_OF_THREADS_TO_LAUNCH = 30;
|
||||
var gNumberOfThreadsLaunched = 0;
|
||||
|
||||
const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
|
||||
var httpserver = new HttpServer();
|
||||
var serverStarted = false;
|
||||
|
@ -13,13 +13,13 @@
|
||||
* -> previousBuildID in telemetry, new value set in prefs.
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
const Cu = Components.utils;
|
||||
|
||||
// Get the TelemetryPing definitions directly so we can test it without going through xpcom.
|
||||
Services.scriptloader.loadSubScript("resource://gre/components/TelemetryPing.js");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
|
||||
// Force the Telemetry enabled preference so that TelemetryPing.setup() doesn't exit early.
|
||||
Services.prefs.setBoolPref(PREF_ENABLED, true);
|
||||
// Force the Telemetry enabled preference so that TelemetryPing.reset() doesn't exit early.
|
||||
Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true);
|
||||
|
||||
// Set up our dummy AppInfo object so we can control the appBuildID.
|
||||
Cu.import("resource://testing-common/AppInfo.jsm");
|
||||
@ -28,25 +28,22 @@ updateAppInfo();
|
||||
// Check that when run with no previous build ID stored, we update the pref but do not
|
||||
// put anything into the metadata.
|
||||
function testFirstRun() {
|
||||
let ping = new TelemetryPing();
|
||||
ping.setup();
|
||||
let metadata = ping.getMetadata();
|
||||
TelemetryPing.reset();
|
||||
let metadata = TelemetryPing.getMetadata();
|
||||
do_check_false("previousBuildID" in metadata);
|
||||
let appBuildID = getAppInfo().appBuildID;
|
||||
let buildIDPref = Services.prefs.getCharPref(PREF_PREVIOUS_BUILDID);
|
||||
let buildIDPref = Services.prefs.getCharPref(TelemetryPing.Constants.PREF_PREVIOUS_BUILDID);
|
||||
do_check_eq(appBuildID, buildIDPref);
|
||||
}
|
||||
|
||||
// Check that a subsequent run with the same build ID does not put prev build ID in
|
||||
// metadata. Assumes testFirstRun() has already been called to set the previousBuildID pref.
|
||||
function testSecondRun() {
|
||||
let ping = new TelemetryPing();
|
||||
ping.setup();
|
||||
let metadata = ping.getMetadata();
|
||||
TelemetryPing.reset();
|
||||
let metadata = TelemetryPing.getMetadata();
|
||||
do_check_false("previousBuildID" in metadata);
|
||||
}
|
||||
|
||||
|
||||
// Set up telemetry with a different app build ID and check that the old build ID
|
||||
// is returned in the metadata and the pref is updated to the new build ID.
|
||||
// Assumes testFirstRun() has been called to set the previousBuildID pref.
|
||||
@ -55,11 +52,10 @@ function testNewBuild() {
|
||||
let info = getAppInfo();
|
||||
let oldBuildID = info.appBuildID;
|
||||
info.appBuildID = NEW_BUILD_ID;
|
||||
let ping = new TelemetryPing();
|
||||
ping.setup();
|
||||
let metadata = ping.getMetadata();
|
||||
TelemetryPing.reset();
|
||||
let metadata = TelemetryPing.getMetadata();
|
||||
do_check_eq(metadata.previousBuildID, oldBuildID);
|
||||
let buildIDPref = Services.prefs.getCharPref(PREF_PREVIOUS_BUILDID);
|
||||
let buildIDPref = Services.prefs.getCharPref(TelemetryPing.Constants.PREF_PREVIOUS_BUILDID);
|
||||
do_check_eq(NEW_BUILD_ID, buildIDPref);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Check that TelemetryPing notifies correctly on idle.
|
||||
// Check that TelemetryPing notifies correctly on idle-daily.
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
@ -13,7 +16,5 @@ function run_test() {
|
||||
do_test_finished();
|
||||
}, "gather-telemetry", false);
|
||||
|
||||
Components.classes["@mozilla.org/base/telemetry-ping;1"]
|
||||
.getService(Components.interfaces.nsITelemetryPing)
|
||||
.observe(null, "idle-daily", null);
|
||||
TelemetryPing.observe(null, "idle-daily", null);
|
||||
}
|
||||
|
@ -10,16 +10,16 @@
|
||||
* overdue and recent pings.
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Get the TelemetryPing definitions directly so we can test it without going through xpcom.
|
||||
// That gives us Cc, Ci, Cr and Cu, as well as a number of consts like PREF_ENABLED,
|
||||
// and PREF_SERVER.
|
||||
Services.scriptloader.loadSubScript("resource://gre/components/TelemetryPing.js");
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryFile.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
|
||||
// We increment TelemetryFile's MAX_PING_FILE_AGE and
|
||||
// OVERDUE_PING_FILE_AGE by 1ms so that our test pings exceed
|
||||
@ -54,11 +54,10 @@ function createSavedPings(aNum, aAge) {
|
||||
// Create a TelemetryPing service that we can generate payloads from.
|
||||
// Luckily, the TelemetryPing constructor does nothing that we need to
|
||||
// clean up.
|
||||
let pingService = new TelemetryPing();
|
||||
let pings = [];
|
||||
let age = Date.now() - aAge;
|
||||
for (let i = 0; i < aNum; ++i) {
|
||||
let payload = pingService.getPayload();
|
||||
let payload = TelemetryPing.getPayload();
|
||||
let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload };
|
||||
TelemetryFile.savePing(ping);
|
||||
if (aAge) {
|
||||
@ -190,8 +189,8 @@ function stopHttpServer() {
|
||||
* Teardown a TelemetryPing instance and clear out any pending
|
||||
* pings to put as back in the starting state.
|
||||
*/
|
||||
function resetTelemetry(aPingService) {
|
||||
aPingService.uninstall();
|
||||
function resetTelemetry() {
|
||||
TelemetryPing.uninstall();
|
||||
// Quick and dirty way to clear TelemetryFile's pendingPings
|
||||
// collection, and put it back in its initial state.
|
||||
let gen = TelemetryFile.popPendingPings();
|
||||
@ -203,17 +202,15 @@ function resetTelemetry(aPingService) {
|
||||
* mode.
|
||||
*/
|
||||
function startTelemetry() {
|
||||
let service = new TelemetryPing();
|
||||
service.setup(true);
|
||||
return service;
|
||||
TelemetryPing.setup();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler);
|
||||
gHttpServer.start(-1);
|
||||
do_get_profile();
|
||||
Services.prefs.setBoolPref(PREF_ENABLED, true);
|
||||
Services.prefs.setCharPref(PREF_SERVER,
|
||||
Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true);
|
||||
Services.prefs.setCharPref(TelemetryPing.Constants.PREF_SERVER,
|
||||
"http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
run_next_test();
|
||||
}
|
||||
@ -224,20 +221,20 @@ function run_test() {
|
||||
*/
|
||||
add_task(function test_expired_pings_are_deleted() {
|
||||
let expiredPings = createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
|
||||
let pingService = startTelemetry();
|
||||
startTelemetry();
|
||||
yield assertReceivedNoPings();
|
||||
assertNotSaved(expiredPings);
|
||||
resetTelemetry(pingService);
|
||||
})
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that really recent pings are not sent on Telemetry initialization.
|
||||
*/
|
||||
add_task(function test_recent_pings_not_sent() {
|
||||
let recentPings = createSavedPings(RECENT_PINGS);
|
||||
let pingService = startTelemetry();
|
||||
startTelemetry();
|
||||
yield assertReceivedNoPings();
|
||||
resetTelemetry(pingService);
|
||||
resetTelemetry();
|
||||
clearPings(recentPings);
|
||||
});
|
||||
|
||||
@ -251,14 +248,14 @@ add_task(function test_overdue_pings_trigger_send() {
|
||||
let expiredPings = createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
|
||||
let overduePings = createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE);
|
||||
|
||||
let pingService = startTelemetry();
|
||||
startTelemetry();
|
||||
yield assertReceivedPings(TOTAL_EXPECTED_PINGS);
|
||||
|
||||
assertNotSaved(recentPings);
|
||||
assertNotSaved(expiredPings);
|
||||
assertNotSaved(overduePings);
|
||||
resetTelemetry(pingService);
|
||||
})
|
||||
resetTelemetry();
|
||||
});
|
||||
|
||||
add_task(function teardown() {
|
||||
yield stopHttpServer();
|
||||
|
@ -9,6 +9,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/ThirdPartyCookieProbe.jsm");
|
||||
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
|
||||
let TOPIC_ACCEPTED = "third-party-cookie-accepted";
|
||||
let TOPIC_REJECTED = "third-party-cookie-rejected";
|
||||
@ -19,8 +20,6 @@ const NUMBER_OF_REJECTS = 30;
|
||||
const NUMBER_OF_ACCEPTS = 17;
|
||||
const NUMBER_OF_REPEATS = 5;
|
||||
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
|
||||
let gCookieService;
|
||||
let gThirdPartyCookieProbe;
|
||||
|
||||
|
@ -10,14 +10,13 @@ const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryTimestamps.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm");
|
||||
|
||||
const Telemetry = Services.telemetry;
|
||||
const bundle = Services.strings.createBundle(
|
||||
"chrome://global/locale/aboutTelemetry.properties");
|
||||
const brandBundle = Services.strings.createBundle(
|
||||
"chrome://branding/locale/brand.properties");
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].
|
||||
getService(Ci.nsITelemetryPing);
|
||||
|
||||
// Maximum height of a histogram bar (in em for html, in chars for text)
|
||||
const MAX_BAR_HEIGHT = 18;
|
||||
|
@ -69,13 +69,17 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
|
||||
|
||||
var version = util.getArg(sourceMap, 'version');
|
||||
var sources = util.getArg(sourceMap, 'sources');
|
||||
var names = util.getArg(sourceMap, 'names');
|
||||
// Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
|
||||
// requires the array) to play nice here.
|
||||
var names = util.getArg(sourceMap, 'names', []);
|
||||
var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
|
||||
var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
|
||||
var mappings = util.getArg(sourceMap, 'mappings');
|
||||
var file = util.getArg(sourceMap, 'file', null);
|
||||
|
||||
if (version !== this._version) {
|
||||
// Once again, Sass deviates from the spec and supplies the version as a
|
||||
// string rather than a number, so we use loose equality checking here.
|
||||
if (version != this._version) {
|
||||
throw new Error('Unsupported version: ' + version);
|
||||
}
|
||||
|
||||
@ -1293,7 +1297,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
|
||||
throw new Error('Invalid mapping: ' + JSON.stringify({
|
||||
generated: aGenerated,
|
||||
source: aSource,
|
||||
orginal: aOriginal,
|
||||
original: aOriginal,
|
||||
name: aName
|
||||
}));
|
||||
}
|
||||
|
@ -74,6 +74,22 @@ ruleView.contextmenu.copy=Copy
|
||||
# the rule view context menu "Select all" entry.
|
||||
ruleView.contextmenu.copy.accessKey=C
|
||||
|
||||
# LOCALIZATION NOTE (ruleView.contextmenu.showOrigSources): Text displayed in the rule view
|
||||
# context menu.
|
||||
ruleView.contextmenu.showOrigSources=Show original sources
|
||||
|
||||
# LOCALIZATION NOTE (ruleView.contextmenu.showOrigSources.accessKey): Access key for
|
||||
# the rule view context menu "Show original sources" entry.
|
||||
ruleView.contextmenu.showOrigSources.accessKey=O
|
||||
|
||||
# LOCALIZATION NOTE (ruleView.contextmenu.showCSSSources): Text displayed in the rule view
|
||||
# context menu.
|
||||
ruleView.contextmenu.showCSSSources=Show CSS sources
|
||||
|
||||
# LOCALIZATION NOTE (ruleView.contextmenu.showCSSSources.accessKey): Access key for
|
||||
# the rule view context menu "Show CSS sources" entry.
|
||||
ruleView.contextmenu.showCSSSources.accessKey=C
|
||||
|
||||
# LOCALIZATION NOTE (computedView.contextmenu.selectAll): Text displayed in the
|
||||
# computed view context menu.
|
||||
computedView.contextmenu.selectAll=Select all
|
||||
|
@ -13,10 +13,8 @@ Cu.import("resource://testing-common/AppInfo.jsm");
|
||||
updateAppInfo();
|
||||
|
||||
function getSimpleMeasurementsFromTelemetryPing() {
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
let ping = TelemetryPing.getPayload();
|
||||
|
||||
return ping.simpleMeasurements;
|
||||
return Cu.import("resource://gre/modules/TelemetryPing.jsm", {}).
|
||||
TelemetryPing.getPayload().simpleMeasurements;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
|
@ -721,8 +721,7 @@ this.AddonUpdateChecker = {
|
||||
for (let update of aUpdates) {
|
||||
if (!update.updateURL)
|
||||
continue;
|
||||
let state = blocklist.getAddonBlocklistState(update.id, update.version,
|
||||
aAppVersion, aPlatformVersion);
|
||||
let state = blocklist.getAddonBlocklistState(update, aAppVersion, aPlatformVersion);
|
||||
if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
|
||||
continue;
|
||||
if ((newest == null || (Services.vc.compare(newest.version, update.version) < 0)) &&
|
||||
|
@ -550,12 +550,10 @@ function applyBlocklistChanges(aOldAddon, aNewAddon, aOldAppVersion,
|
||||
let bs = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsIBlocklistService);
|
||||
|
||||
let oldBlocklistState = bs.getAddonBlocklistState(aOldAddon.id,
|
||||
aOldAddon.version,
|
||||
let oldBlocklistState = bs.getAddonBlocklistState(createWrapper(aOldAddon),
|
||||
aOldAppVersion,
|
||||
aOldPlatformVersion);
|
||||
let newBlocklistState = bs.getAddonBlocklistState(aNewAddon.id,
|
||||
aNewAddon.version);
|
||||
let newBlocklistState = bs.getAddonBlocklistState(createWrapper(aNewAddon));
|
||||
|
||||
// If the blocklist state hasn't changed then the properties don't need to
|
||||
// change
|
||||
@ -2266,8 +2264,7 @@ var XPIProvider = {
|
||||
}
|
||||
catch (e) { }
|
||||
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
TelemetryPing.setAddOns(data);
|
||||
Cu.import("resource://gre/modules/TelemetryPing.jsm", {}).TelemetryPing.setAddOns(data);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -6213,7 +6210,7 @@ AddonInternal.prototype = {
|
||||
|
||||
let bs = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsIBlocklistService);
|
||||
return bs.getAddonBlocklistState(this.id, this.version);
|
||||
return bs.getAddonBlocklistState(createWrapper(this));
|
||||
},
|
||||
|
||||
get blocklistURL() {
|
||||
@ -6225,7 +6222,7 @@ AddonInternal.prototype = {
|
||||
|
||||
let bs = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsIBlocklistService);
|
||||
return bs.getAddonBlocklistURL(this.id, this.version);
|
||||
return bs.getAddonBlocklistURL(createWrapper(this));
|
||||
},
|
||||
|
||||
applyCompatibilityUpdate: function AddonInternal_applyCompatibilityUpdate(aUpdate, aSyncCompatibility) {
|
||||
@ -6343,7 +6340,7 @@ function AddonWrapper(aAddon) {
|
||||
["id", "syncGUID", "version", "type", "isCompatible", "isPlatformCompatible",
|
||||
"providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
|
||||
"softDisabled", "skinnable", "size", "foreignInstall", "hasBinaryComponents",
|
||||
"strictCompatibility", "compatibilityOverrides"].forEach(function(aProp) {
|
||||
"strictCompatibility", "compatibilityOverrides", "updateURL"].forEach(function(aProp) {
|
||||
this.__defineGetter__(aProp, function AddonWrapper_propertyGetter() aAddon[aProp]);
|
||||
}, this);
|
||||
|
||||
|
@ -49,6 +49,8 @@ const VULNERABILITYSTATUS_NONE = 0;
|
||||
const VULNERABILITYSTATUS_UPDATE_AVAILABLE = 1;
|
||||
const VULNERABILITYSTATUS_NO_UPDATE = 2;
|
||||
|
||||
const EXTENSION_BLOCK_FILTERS = ["id", "name", "creator", "homepageURL", "updateURL"];
|
||||
|
||||
var gLoggingEnabled = null;
|
||||
var gBlocklistEnabled = true;
|
||||
var gBlocklistLevel = DEFAULT_LEVEL;
|
||||
@ -317,16 +319,16 @@ Blocklist.prototype = {
|
||||
},
|
||||
|
||||
/* See nsIBlocklistService */
|
||||
isAddonBlocklisted: function Blocklist_isAddonBlocklisted(id, version, appVersion, toolkitVersion) {
|
||||
return this.getAddonBlocklistState(id, version, appVersion, toolkitVersion) ==
|
||||
isAddonBlocklisted: function Blocklist_isAddonBlocklisted(addon, appVersion, toolkitVersion) {
|
||||
return this.getAddonBlocklistState(addon, appVersion, toolkitVersion) ==
|
||||
Ci.nsIBlocklistService.STATE_BLOCKED;
|
||||
},
|
||||
|
||||
/* See nsIBlocklistService */
|
||||
getAddonBlocklistState: function Blocklist_getAddonBlocklistState(id, version, appVersion, toolkitVersion) {
|
||||
getAddonBlocklistState: function Blocklist_getAddonBlocklistState(addon, appVersion, toolkitVersion) {
|
||||
if (!this._addonEntries)
|
||||
this._loadBlocklist();
|
||||
return this._getAddonBlocklistState(id, version, this._addonEntries,
|
||||
return this._getAddonBlocklistState(addon, this._addonEntries,
|
||||
appVersion, toolkitVersion);
|
||||
},
|
||||
|
||||
@ -349,8 +351,8 @@ Blocklist.prototype = {
|
||||
* @returns The blocklist state for the item, one of the STATE constants as
|
||||
* defined in nsIBlocklistService.
|
||||
*/
|
||||
_getAddonBlocklistState: function Blocklist_getAddonBlocklistStateCall(id,
|
||||
version, addonEntries, appVersion, toolkitVersion) {
|
||||
_getAddonBlocklistState: function Blocklist_getAddonBlocklistStateCall(addon,
|
||||
addonEntries, appVersion, toolkitVersion) {
|
||||
if (!gBlocklistEnabled)
|
||||
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
|
||||
|
||||
@ -359,12 +361,12 @@ Blocklist.prototype = {
|
||||
if (!toolkitVersion)
|
||||
toolkitVersion = gApp.platformVersion;
|
||||
|
||||
var blItem = this._findMatchingAddonEntry(addonEntries, id);
|
||||
var blItem = this._findMatchingAddonEntry(addonEntries, addon);
|
||||
if (!blItem)
|
||||
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
|
||||
|
||||
for (let currentblItem of blItem.versions) {
|
||||
if (currentblItem.includesItem(version, appVersion, toolkitVersion))
|
||||
if (currentblItem.includesItem(addon.version, appVersion, toolkitVersion))
|
||||
return currentblItem.severity >= gBlocklistLevel ? Ci.nsIBlocklistService.STATE_BLOCKED :
|
||||
Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
|
||||
}
|
||||
@ -374,36 +376,63 @@ Blocklist.prototype = {
|
||||
/**
|
||||
* Returns the set of prefs of the add-on stored in the blocklist file
|
||||
* (probably to revert them on disabling).
|
||||
* @param id
|
||||
* ID of the add-on.
|
||||
* @param addon
|
||||
* The add-on whose to-be-reset prefs are to be found.
|
||||
*/
|
||||
_getAddonPrefs: function Blocklist_getAddonPrefs(id) {
|
||||
let entry = this._findMatchingAddonEntry(this._addonEntries, id);
|
||||
_getAddonPrefs: function Blocklist_getAddonPrefs(addon) {
|
||||
let entry = this._findMatchingAddonEntry(this._addonEntries, addon);
|
||||
return entry.prefs.slice(0);
|
||||
},
|
||||
|
||||
_findMatchingAddonEntry: function Blocklist_findMatchingAddonEntry(aAddonEntries,
|
||||
aId) {
|
||||
for (let entry of aAddonEntries) {
|
||||
if (entry.id instanceof RegExp) {
|
||||
if (entry.id.test(aId))
|
||||
return entry;
|
||||
} else if (entry.id == aId) {
|
||||
return entry;
|
||||
aAddon) {
|
||||
if (!aAddon)
|
||||
return null;
|
||||
// Returns true if the params object passes the constraints set by entry.
|
||||
// (For every non-null property in entry, the same key must exist in
|
||||
// params and value must be the same)
|
||||
function checkEntry(entry, params) {
|
||||
for (let [key, value] of entry) {
|
||||
if (value === null || value === undefined)
|
||||
continue;
|
||||
if (params[key]) {
|
||||
if (value instanceof RegExp) {
|
||||
if (!value.test(params[key])) {
|
||||
return false;
|
||||
}
|
||||
} else if (value !== params[key]) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
|
||||
let params = {};
|
||||
for (let filter of EXTENSION_BLOCK_FILTERS) {
|
||||
params[filter] = aAddon[filter];
|
||||
}
|
||||
if (params.creator)
|
||||
params.creator = params.creator.name;
|
||||
for (let entry of aAddonEntries) {
|
||||
if (checkEntry(entry.attributes, params)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/* See nsIBlocklistService */
|
||||
getAddonBlocklistURL: function Blocklist_getAddonBlocklistURL(id, version, appVersion, toolkitVersion) {
|
||||
getAddonBlocklistURL: function Blocklist_getAddonBlocklistURL(addon, appVersion, toolkitVersion) {
|
||||
if (!gBlocklistEnabled)
|
||||
return "";
|
||||
|
||||
if (!this._addonEntries)
|
||||
this._loadBlocklist();
|
||||
|
||||
let blItem = this._findMatchingAddonEntry(this._addonEntries, id);
|
||||
let blItem = this._findMatchingAddonEntry(this._addonEntries, addon);
|
||||
if (!blItem || !blItem.blockID)
|
||||
return null;
|
||||
|
||||
@ -734,18 +763,26 @@ Blocklist.prototype = {
|
||||
return;
|
||||
|
||||
let blockEntry = {
|
||||
id: null,
|
||||
versions: [],
|
||||
prefs: [],
|
||||
blockID: null
|
||||
blockID: null,
|
||||
attributes: new Map()
|
||||
// Atleast one of EXTENSION_BLOCK_FILTERS must get added to attributes
|
||||
};
|
||||
|
||||
// Any filter starting with '/' is interpreted as a regex. So if an attribute
|
||||
// starts with a '/' it must be checked via a regex.
|
||||
function regExpCheck(attr) {
|
||||
return attr.startsWith("/") ? parseRegExp(attr) : attr;
|
||||
}
|
||||
|
||||
for (let filter of EXTENSION_BLOCK_FILTERS) {
|
||||
let attr = blocklistElement.getAttribute(filter);
|
||||
if (attr)
|
||||
blockEntry.attributes.set(filter, regExpCheck(attr));
|
||||
}
|
||||
|
||||
var childNodes = blocklistElement.childNodes;
|
||||
var id = blocklistElement.getAttribute("id");
|
||||
// Add-on IDs cannot contain '/', so an ID starting with '/' must be a regex
|
||||
if (id.startsWith("/"))
|
||||
id = parseRegExp(id);
|
||||
blockEntry.id = id;
|
||||
|
||||
for (let x = 0; x < childNodes.length; x++) {
|
||||
var childElement = childNodes.item(x);
|
||||
@ -928,9 +965,8 @@ Blocklist.prototype = {
|
||||
for (let addon of addons) {
|
||||
let oldState = Ci.nsIBlocklistService.STATE_NOTBLOCKED;
|
||||
if (oldAddonEntries)
|
||||
oldState = self._getAddonBlocklistState(addon.id, addon.version,
|
||||
oldAddonEntries);
|
||||
let state = self.getAddonBlocklistState(addon.id, addon.version);
|
||||
oldState = self._getAddonBlocklistState(addon, oldAddonEntries);
|
||||
let state = self.getAddonBlocklistState(addon);
|
||||
|
||||
LOG("Blocklist state for " + addon.id + " changed from " +
|
||||
oldState + " to " + state);
|
||||
@ -941,7 +977,7 @@ Blocklist.prototype = {
|
||||
|
||||
if (state === Ci.nsIBlocklistService.STATE_BLOCKED) {
|
||||
// It's a hard block. We must reset certain preferences.
|
||||
let prefs = self._getAddonPrefs(addon.id);
|
||||
let prefs = self._getAddonPrefs(addon);
|
||||
resetPrefs(prefs);
|
||||
}
|
||||
|
||||
@ -973,7 +1009,7 @@ Blocklist.prototype = {
|
||||
disable: false,
|
||||
blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED,
|
||||
item: addon,
|
||||
url: self.getAddonBlocklistURL(addon.id),
|
||||
url: self.getAddonBlocklistURL(addon),
|
||||
});
|
||||
}
|
||||
|
||||
@ -1056,7 +1092,7 @@ Blocklist.prototype = {
|
||||
// This add-on is softblocked.
|
||||
addon.item.softDisabled = true;
|
||||
// We must revert certain prefs.
|
||||
let prefs = self._getAddonPrefs(addon.item.id);
|
||||
let prefs = self._getAddonPrefs(addon.item);
|
||||
resetPrefs(prefs);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
|
||||
<emItems>
|
||||
<emItem name="/^Mozilla Corp\.$/">
|
||||
<versionRange severity="1">
|
||||
<targetApplication id="xpcshell@tests.mozilla.org">
|
||||
<versionRange minVersion="1" maxVersion="2.*"/>
|
||||
</targetApplication>
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem id="/block2/" name="/^Moz/" creator="Dangerous"
|
||||
homepageURL="/\.dangerous\.com/" updateURL="/\.dangerous\.com/">
|
||||
<versionRange severity="3">
|
||||
<targetApplication id="xpcshell@tests.mozilla.org">
|
||||
<versionRange minVersion="1" maxVersion="2.*"/>
|
||||
</targetApplication>
|
||||
</versionRange>
|
||||
</emItem>
|
||||
</emItems>
|
||||
</blocklist>
|
@ -3,7 +3,10 @@
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
|
||||
<emItems>
|
||||
<emItem id="test_bug393285_2@tests.mozilla.org"/>
|
||||
<emItem id="test_bug393285_3@tests.mozilla.org">
|
||||
<emItem id="test_bug393285_3a@tests.mozilla.org">
|
||||
<versionRange minVersion="1.0" maxVersion="1.0"/>
|
||||
</emItem>
|
||||
<emItem id="test_bug393285_3b@tests.mozilla.org">
|
||||
<versionRange minVersion="1.0" maxVersion="1.0"/>
|
||||
</emItem>
|
||||
<emItem id="test_bug393285_4@tests.mozilla.org">
|
||||
|
@ -0,0 +1,159 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Tests blocking of extensions by ID, name, creator, homepageURL, updateURL
|
||||
// and RegExps for each. See bug 897735.
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
var testserver = new HttpServer();
|
||||
testserver.start(-1);
|
||||
gPort = testserver.identity.primaryPort;
|
||||
|
||||
// register static files with server and interpolate port numbers in them
|
||||
mapFile("/data/test_blocklist_metadata_filters_1.xml", testserver);
|
||||
|
||||
const profileDir = gProfD.clone();
|
||||
profileDir.append("extensions");
|
||||
|
||||
// Don't need the full interface, attempts to call other methods will just
|
||||
// throw which is just fine
|
||||
var WindowWatcher = {
|
||||
openWindow: function(parent, url, name, features, arguments) {
|
||||
// Should be called to list the newly blocklisted items
|
||||
do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
|
||||
|
||||
// Simulate auto-disabling any softblocks
|
||||
var list = arguments.wrappedJSObject.list;
|
||||
list.forEach(function(aItem) {
|
||||
if (!aItem.blocked)
|
||||
aItem.disable = true;
|
||||
});
|
||||
|
||||
//run the code after the blocklist is closed
|
||||
Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
|
||||
|
||||
},
|
||||
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(Ci.nsIWindowWatcher)
|
||||
|| iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
var WindowWatcherFactory = {
|
||||
createInstance: function createInstance(outer, iid) {
|
||||
if (outer != null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return WindowWatcher.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
|
||||
"Fake Window Watcher",
|
||||
"@mozilla.org/embedcomp/window-watcher;1",
|
||||
WindowWatcherFactory);
|
||||
|
||||
|
||||
function load_blocklist(aFile, aCallback) {
|
||||
Services.obs.addObserver(function() {
|
||||
Services.obs.removeObserver(arguments.callee, "blocklist-updated");
|
||||
|
||||
do_execute_soon(aCallback);
|
||||
}, "blocklist-updated", false);
|
||||
|
||||
Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
|
||||
gPort + "/data/" + aFile);
|
||||
var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsITimerCallback);
|
||||
blocklist.notify(null);
|
||||
}
|
||||
|
||||
|
||||
function end_test() {
|
||||
testserver.stop(do_test_finished);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
|
||||
|
||||
// Should get blocked by name
|
||||
writeInstallRDFForExtension({
|
||||
id: "block1@tests.mozilla.org",
|
||||
version: "1.0",
|
||||
name: "Mozilla Corp.",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Should get blocked by all the attributes.
|
||||
writeInstallRDFForExtension({
|
||||
id: "block2@tests.mozilla.org",
|
||||
version: "1.0",
|
||||
name: "Moz-addon",
|
||||
creator: "Dangerous",
|
||||
homepageURL: "www.extension.dangerous.com",
|
||||
updateURL: "www.extension.dangerous.com/update.rdf",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Fails to get blocked because of a different ID even though other
|
||||
// attributes match against a blocklist entry.
|
||||
writeInstallRDFForExtension({
|
||||
id: "block3@tests.mozilla.org",
|
||||
version: "1.0",
|
||||
name: "Moz-addon",
|
||||
creator: "Dangerous",
|
||||
homepageURL: "www.extensions.dangerous.com",
|
||||
updateURL: "www.extension.dangerous.com/update.rdf",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
startupManager();
|
||||
|
||||
AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
|
||||
"block2@tests.mozilla.org",
|
||||
"block3@tests.mozilla.org"], function([a1, a2, a3]) {
|
||||
do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
|
||||
do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
|
||||
do_check_eq(a3.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
|
||||
|
||||
run_test_1();
|
||||
});
|
||||
}
|
||||
|
||||
function run_test_1() {
|
||||
load_blocklist("test_blocklist_metadata_filters_1.xml", function() {
|
||||
restartManager();
|
||||
|
||||
AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
|
||||
"block2@tests.mozilla.org",
|
||||
"block3@tests.mozilla.org"], function([a1, a2, a3]) {
|
||||
do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
|
||||
do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_BLOCKED);
|
||||
do_check_eq(a3.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
|
||||
end_test();
|
||||
});
|
||||
});
|
||||
}
|
@ -79,10 +79,10 @@ var ADDONS = [
|
||||
|
||||
// This is a replacement for the blocklist service
|
||||
var BlocklistService = {
|
||||
getAddonBlocklistState: function(aId, aVersion, aAppVersion, aToolkitVersion) {
|
||||
if (aId == "bug335238_3@tests.mozilla.org")
|
||||
getAddonBlocklistState: function(aAddon, aAppVersion, aToolkitVersion) {
|
||||
if (aAddon.id == "bug335238_3@tests.mozilla.org")
|
||||
return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
|
||||
if (aId == "bug335238_4@tests.mozilla.org")
|
||||
if (aAddon.id == "bug335238_4@tests.mozilla.org")
|
||||
return Ci.nsIBlocklistService.STATE_BLOCKED;
|
||||
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
|
||||
},
|
||||
@ -91,8 +91,8 @@ var BlocklistService = {
|
||||
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
|
||||
},
|
||||
|
||||
isAddonBlocklisted: function(aId, aVersion, aAppVersion, aToolkitVersion) {
|
||||
return this.getAddonBlocklistState(aId, aVersion, aAppVersion, aToolkitVersion) ==
|
||||
isAddonBlocklisted: function(aAddon, aAppVersion, aToolkitVersion) {
|
||||
return this.getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion) ==
|
||||
Ci.nsIBlocklistService.STATE_BLOCKED;
|
||||
},
|
||||
|
||||
|
@ -3,53 +3,325 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
var testserver = new HttpServer();
|
||||
testserver.start(-1);
|
||||
gPort = testserver.identity.primaryPort;
|
||||
|
||||
// register static files with server and interpolate port numbers in them
|
||||
mapFile("/data/test_bug393285.xml", testserver);
|
||||
|
||||
const profileDir = gProfD.clone();
|
||||
profileDir.append("extensions");
|
||||
|
||||
let addonIDs = ["test_bug393285_1@tests.mozilla.org",
|
||||
"test_bug393285_2@tests.mozilla.org",
|
||||
"test_bug393285_3a@tests.mozilla.org",
|
||||
"test_bug393285_3b@tests.mozilla.org",
|
||||
"test_bug393285_4@tests.mozilla.org",
|
||||
"test_bug393285_5@tests.mozilla.org",
|
||||
"test_bug393285_6@tests.mozilla.org",
|
||||
"test_bug393285_7@tests.mozilla.org",
|
||||
"test_bug393285_8@tests.mozilla.org",
|
||||
"test_bug393285_9@tests.mozilla.org",
|
||||
"test_bug393285_10@tests.mozilla.org",
|
||||
"test_bug393285_11@tests.mozilla.org",
|
||||
"test_bug393285_12@tests.mozilla.org",
|
||||
"test_bug393285_13@tests.mozilla.org",
|
||||
"test_bug393285_14@tests.mozilla.org"];
|
||||
|
||||
// A window watcher to deal with the blocklist UI dialog.
|
||||
var WindowWatcher = {
|
||||
openWindow: function(parent, url, name, features, arguments) {
|
||||
// Should be called to list the newly blocklisted items
|
||||
do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
|
||||
|
||||
// Simulate auto-disabling any softblocks
|
||||
var list = arguments.wrappedJSObject.list;
|
||||
list.forEach(function(aItem) {
|
||||
if (!aItem.blocked)
|
||||
aItem.disable = true;
|
||||
});
|
||||
|
||||
//run the code after the blocklist is closed
|
||||
Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
|
||||
|
||||
},
|
||||
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(Ci.nsIWindowWatcher)
|
||||
|| iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
var WindowWatcherFactory = {
|
||||
createInstance: function createInstance(outer, iid) {
|
||||
if (outer != null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return WindowWatcher.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
|
||||
"Fake Window Watcher",
|
||||
"@mozilla.org/embedcomp/window-watcher;1",
|
||||
WindowWatcherFactory);
|
||||
|
||||
|
||||
function load_blocklist(aFile, aCallback) {
|
||||
Services.obs.addObserver(function() {
|
||||
Services.obs.removeObserver(arguments.callee, "blocklist-updated");
|
||||
|
||||
do_execute_soon(aCallback);
|
||||
}, "blocklist-updated", false);
|
||||
|
||||
Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
|
||||
gPort + "/data/" + aFile);
|
||||
var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsITimerCallback);
|
||||
blocklist.notify(null);
|
||||
}
|
||||
|
||||
|
||||
function end_test() {
|
||||
testserver.stop(do_test_finished);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
|
||||
|
||||
// We cannot force the blocklist to update so just copy our test list to the profile
|
||||
var blocklistFile = gProfD.clone();
|
||||
blocklistFile.append("blocklist.xml");
|
||||
if (blocklistFile.exists())
|
||||
blocklistFile.remove(false);
|
||||
var source = do_get_file("data/test_bug393285.xml");
|
||||
source.copyTo(gProfD, "blocklist.xml");
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_1@tests.mozilla.org",
|
||||
name: "extension 1",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
|
||||
.getService(Components.interfaces.nsIBlocklistService);
|
||||
|
||||
// No info in blocklist, shouldn't be blocked
|
||||
do_check_false(blocklist.isAddonBlocklisted("test_bug393285_1@tests.mozilla.org", "1", "1", "1.9"));
|
||||
|
||||
// Should always be blocked
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_2@tests.mozilla.org", "1", "1", "1.9"));
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_2@tests.mozilla.org",
|
||||
name: "extension 2",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Only version 1 should be blocked
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_3@tests.mozilla.org", "1", "1", "1.9"));
|
||||
do_check_false(blocklist.isAddonBlocklisted("test_bug393285_3@tests.mozilla.org", "2", "1", "1.9"));
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_3a@tests.mozilla.org",
|
||||
name: "extension 3a",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Should be blocked for app version 1
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_4@tests.mozilla.org", "1", "1", "1.9"));
|
||||
do_check_false(blocklist.isAddonBlocklisted("test_bug393285_4@tests.mozilla.org", "1", "2", "1.9"));
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_3b@tests.mozilla.org",
|
||||
name: "extension 3b",
|
||||
version: "2.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Not blocklisted because we are a different OS
|
||||
do_check_false(blocklist.isAddonBlocklisted("test_bug393285_5@tests.mozilla.org", "1", "2", "1.9"));
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_4@tests.mozilla.org",
|
||||
name: "extension 4",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Blocklisted based on OS
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_6@tests.mozilla.org", "1", "2", "1.9"));
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_7@tests.mozilla.org", "1", "2", "1.9"));
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_5@tests.mozilla.org",
|
||||
name: "extension 5",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Not blocklisted because we are a different ABI
|
||||
do_check_false(blocklist.isAddonBlocklisted("test_bug393285_8@tests.mozilla.org", "1", "2", "1.9"));
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_6@tests.mozilla.org",
|
||||
name: "extension 6",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Blocklisted based on ABI
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_9@tests.mozilla.org", "1", "2", "1.9"));
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_10@tests.mozilla.org", "1", "2", "1.9"));
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_7@tests.mozilla.org",
|
||||
name: "extension 7",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Doesnt match both os and abi so not blocked
|
||||
do_check_false(blocklist.isAddonBlocklisted("test_bug393285_11@tests.mozilla.org", "1", "2", "1.9"));
|
||||
do_check_false(blocklist.isAddonBlocklisted("test_bug393285_12@tests.mozilla.org", "1", "2", "1.9"));
|
||||
do_check_false(blocklist.isAddonBlocklisted("test_bug393285_13@tests.mozilla.org", "1", "2", "1.9"));
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_8@tests.mozilla.org",
|
||||
name: "extension 8",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
// Matches both os and abi so blocked
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_14@tests.mozilla.org", "1", "2", "1.9"));
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_9@tests.mozilla.org",
|
||||
name: "extension 9",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_10@tests.mozilla.org",
|
||||
name: "extension 10",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_11@tests.mozilla.org",
|
||||
name: "extension 11",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_12@tests.mozilla.org",
|
||||
name: "extension 12",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_13@tests.mozilla.org",
|
||||
name: "extension 13",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_14@tests.mozilla.org",
|
||||
name: "extension 14",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
startupManager();
|
||||
|
||||
AddonManager.getAddonsByIDs(addonIDs, function(addons) {
|
||||
for (addon of addons) {
|
||||
do_check_eq(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
|
||||
}
|
||||
run_test_1();
|
||||
});
|
||||
}
|
||||
|
||||
function run_test_1() {
|
||||
load_blocklist("test_bug393285.xml", function() {
|
||||
restartManager();
|
||||
|
||||
var blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
|
||||
.getService(Ci.nsIBlocklistService);
|
||||
|
||||
AddonManager.getAddonsByIDs(addonIDs,
|
||||
function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
|
||||
a11, a12, a13, a14, a15]) {
|
||||
// No info in blocklist, shouldn't be blocked
|
||||
do_check_false(blocklist.isAddonBlocklisted(a1, "1", "1.9"));
|
||||
|
||||
// Should always be blocked
|
||||
do_check_true(blocklist.isAddonBlocklisted(a2, "1", "1.9"));
|
||||
|
||||
// Only version 1 should be blocked
|
||||
do_check_true(blocklist.isAddonBlocklisted(a3, "1", "1.9"));
|
||||
do_check_false(blocklist.isAddonBlocklisted(a4, "1", "1.9"));
|
||||
|
||||
// Should be blocked for app version 1
|
||||
do_check_true(blocklist.isAddonBlocklisted(a5, "1", "1.9"));
|
||||
do_check_false(blocklist.isAddonBlocklisted(a5, "2", "1.9"));
|
||||
|
||||
// Not blocklisted because we are a different OS
|
||||
do_check_false(blocklist.isAddonBlocklisted(a6, "2", "1.9"));
|
||||
|
||||
// Blocklisted based on OS
|
||||
do_check_true(blocklist.isAddonBlocklisted(a7, "2", "1.9"));
|
||||
do_check_true(blocklist.isAddonBlocklisted(a8, "2", "1.9"));
|
||||
|
||||
// Not blocklisted because we are a different ABI
|
||||
do_check_false(blocklist.isAddonBlocklisted(a9, "2", "1.9"));
|
||||
|
||||
// Blocklisted based on ABI
|
||||
do_check_true(blocklist.isAddonBlocklisted(a10, "2", "1.9"));
|
||||
do_check_true(blocklist.isAddonBlocklisted(a11, "2", "1.9"));
|
||||
|
||||
// Doesnt match both os and abi so not blocked
|
||||
do_check_false(blocklist.isAddonBlocklisted(a12, "2", "1.9"));
|
||||
do_check_false(blocklist.isAddonBlocklisted(a13, "2", "1.9"));
|
||||
do_check_false(blocklist.isAddonBlocklisted(a14, "2", "1.9"));
|
||||
|
||||
// Matches both os and abi so blocked
|
||||
do_check_true(blocklist.isAddonBlocklisted(a15, "2", "1.9"));
|
||||
end_test();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -3,24 +3,165 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
let addonIDs = ["test_bug393285_1@tests.mozilla.org",
|
||||
"test_bug393285_2@tests.mozilla.org",
|
||||
"test_bug393285_3a@tests.mozilla.org",
|
||||
"test_bug393285_4@tests.mozilla.org"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
var testserver = new HttpServer();
|
||||
testserver.start(-1);
|
||||
gPort = testserver.identity.primaryPort;
|
||||
|
||||
// register static files with server and interpolate port numbers in them
|
||||
mapFile("/data/test_bug393285.xml", testserver);
|
||||
|
||||
const profileDir = gProfD.clone();
|
||||
profileDir.append("extensions");
|
||||
|
||||
// A window watcher to deal with the blocklist UI dialog.
|
||||
var WindowWatcher = {
|
||||
openWindow: function(parent, url, name, features, arguments) {
|
||||
// Should be called to list the newly blocklisted items
|
||||
do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
|
||||
|
||||
// Simulate auto-disabling any softblocks
|
||||
var list = arguments.wrappedJSObject.list;
|
||||
list.forEach(function(aItem) {
|
||||
if (!aItem.blocked)
|
||||
aItem.disable = true;
|
||||
});
|
||||
|
||||
//run the code after the blocklist is closed
|
||||
Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
|
||||
|
||||
},
|
||||
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(Ci.nsIWindowWatcher)
|
||||
|| iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
var WindowWatcherFactory = {
|
||||
createInstance: function createInstance(outer, iid) {
|
||||
if (outer != null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return WindowWatcher.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
|
||||
"Fake Window Watcher",
|
||||
"@mozilla.org/embedcomp/window-watcher;1",
|
||||
WindowWatcherFactory);
|
||||
|
||||
|
||||
function load_blocklist(aFile, aCallback) {
|
||||
Services.obs.addObserver(function() {
|
||||
Services.obs.removeObserver(arguments.callee, "blocklist-updated");
|
||||
|
||||
do_execute_soon(aCallback);
|
||||
}, "blocklist-updated", false);
|
||||
|
||||
Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
|
||||
gPort + "/data/" + aFile);
|
||||
var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsITimerCallback);
|
||||
blocklist.notify(null);
|
||||
}
|
||||
|
||||
|
||||
function end_test() {
|
||||
testserver.stop(do_test_finished);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
|
||||
|
||||
// We cannot force the blocklist to update so just copy our test list to the profile
|
||||
var blocklistFile = gProfD.clone();
|
||||
blocklistFile.append("blocklist.xml");
|
||||
if (blocklistFile.exists())
|
||||
blocklistFile.remove(false);
|
||||
var source = do_get_file("data/test_bug393285.xml");
|
||||
source.copyTo(gProfD, "blocklist.xml");
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_1@tests.mozilla.org",
|
||||
name: "extension 1",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
|
||||
.getService(Components.interfaces.nsIBlocklistService);
|
||||
|
||||
// All these should be blocklisted for the current app.
|
||||
do_check_false(blocklist.isAddonBlocklisted("test_bug393285_1@tests.mozilla.org", "1", null, null));
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_2@tests.mozilla.org", "1", null, null));
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_3@tests.mozilla.org", "1", null, null));
|
||||
do_check_true(blocklist.isAddonBlocklisted("test_bug393285_4@tests.mozilla.org", "1", null, null));
|
||||
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_2@tests.mozilla.org",
|
||||
name: "extension 2",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_3a@tests.mozilla.org",
|
||||
name: "extension 3a",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
writeInstallRDFForExtension({
|
||||
id: "test_bug393285_4@tests.mozilla.org",
|
||||
name: "extension 4",
|
||||
version: "1.0",
|
||||
targetApplications: [{
|
||||
id: "xpcshell@tests.mozilla.org",
|
||||
minVersion: "1",
|
||||
maxVersion: "3"
|
||||
}]
|
||||
}, profileDir);
|
||||
|
||||
startupManager();
|
||||
|
||||
AddonManager.getAddonsByIDs(addonIDs, function(addons) {
|
||||
for (addon of addons) {
|
||||
do_check_eq(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
|
||||
}
|
||||
run_test_1();
|
||||
});
|
||||
}
|
||||
|
||||
function run_test_1() {
|
||||
load_blocklist("test_bug393285.xml", function() {
|
||||
restartManager();
|
||||
|
||||
var blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
|
||||
.getService(Ci.nsIBlocklistService);
|
||||
|
||||
AddonManager.getAddonsByIDs(addonIDs,
|
||||
function([a1, a2, a3, a4]) {
|
||||
// No info in blocklist, shouldn't be blocked
|
||||
do_check_false(blocklist.isAddonBlocklisted(a1, null, null));
|
||||
|
||||
// All these should be blocklisted for the current app.
|
||||
do_check_true(blocklist.isAddonBlocklisted(a2, null, null));
|
||||
do_check_true(blocklist.isAddonBlocklisted(a3, null, null));
|
||||
do_check_true(blocklist.isAddonBlocklisted(a4, null, null));
|
||||
|
||||
end_test();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -461,11 +461,11 @@ function check_test_pt3() {
|
||||
do_check_eq(check_addon_state(addons[3]), "false,false,true");
|
||||
|
||||
// Check blockIDs are correct
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[0].id,''),create_blocklistURL(addons[0].id));
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[1].id,''),create_blocklistURL(addons[1].id));
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[2].id,''),create_blocklistURL(addons[2].id));
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[3].id,''),create_blocklistURL(addons[3].id));
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[4].id,''),create_blocklistURL(addons[4].id));
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[0]),create_blocklistURL(addons[0].id));
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[1]),create_blocklistURL(addons[1].id));
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[2]),create_blocklistURL(addons[2].id));
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[3]),create_blocklistURL(addons[3].id));
|
||||
do_check_eq(blocklist.getAddonBlocklistURL(addons[4]),create_blocklistURL(addons[4].id));
|
||||
|
||||
// All plugins have the same blockID on the test
|
||||
do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[0]), create_blocklistURL('test_bug455906_plugin'));
|
||||
|
@ -17,6 +17,7 @@ skip-if = os == "android"
|
||||
[test_badschema.js]
|
||||
[test_blocklistchange.js]
|
||||
[test_blocklist_prefs.js]
|
||||
[test_blocklist_metadata_filters.js]
|
||||
# Bug 676992: test consistently hangs on Android
|
||||
skip-if = os == "android"
|
||||
[test_blocklist_regexp.js]
|
||||
|
@ -869,7 +869,7 @@ var gIncompatibleCheckPage = {
|
||||
// the add-on will become incompatible.
|
||||
let bs = CoC["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(CoI.nsIBlocklistService);
|
||||
if (bs.isAddonBlocklisted(addon.id, install.version,
|
||||
if (bs.isAddonBlocklisted(addon,
|
||||
gUpdates.update.appVersion,
|
||||
gUpdates.update.platformVersion))
|
||||
return;
|
||||
|
@ -2957,7 +2957,7 @@ UpdateService.prototype = {
|
||||
// the add-on will become incompatible.
|
||||
let bs = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsIBlocklistService);
|
||||
if (bs.isAddonBlocklisted(addon.id, install.version,
|
||||
if (bs.isAddonBlocklisted(addon,
|
||||
gUpdates.update.appVersion,
|
||||
gUpdates.update.platformVersion))
|
||||
return;
|
||||
|
@ -9,7 +9,7 @@
|
||||
interface nsIPluginTag;
|
||||
interface nsIVariant;
|
||||
|
||||
[scriptable, uuid(cbba15b8-316d-4ae6-8ed9-fe9cf8386730)]
|
||||
[scriptable, uuid(d463dfbb-89c4-4553-97af-b4fd8854e161)]
|
||||
interface nsIBlocklistService : nsISupports
|
||||
{
|
||||
// Indicates that the item does not appear in the blocklist.
|
||||
@ -29,10 +29,8 @@ interface nsIBlocklistService : nsISupports
|
||||
|
||||
/**
|
||||
* Determine if an item is blocklisted
|
||||
* @param id
|
||||
* The ID of the item.
|
||||
* @param version
|
||||
* The item's version.
|
||||
* @param addon
|
||||
* The addon item to be checked.
|
||||
* @param appVersion
|
||||
* The version of the application we are checking in the blocklist.
|
||||
* If this parameter is null, the version of the running application
|
||||
@ -44,16 +42,14 @@ interface nsIBlocklistService : nsISupports
|
||||
* @returns true if the item is compatible with this version of the
|
||||
* application or this version of the toolkit, false, otherwise.
|
||||
*/
|
||||
boolean isAddonBlocklisted(in AString id, in AString version,
|
||||
boolean isAddonBlocklisted(in jsval addon,
|
||||
[optional] in AString appVersion,
|
||||
[optional] in AString toolkitVersion);
|
||||
|
||||
/**
|
||||
* Determine the blocklist state of an add-on
|
||||
* @param id
|
||||
* The ID of the item.
|
||||
* @param version
|
||||
* The item's version.
|
||||
* The addon item to be checked.
|
||||
* @param appVersion
|
||||
* The version of the application we are checking in the blocklist.
|
||||
* If this parameter is null, the version of the running application
|
||||
@ -64,7 +60,7 @@ interface nsIBlocklistService : nsISupports
|
||||
* is used.
|
||||
* @returns The STATE constant.
|
||||
*/
|
||||
unsigned long getAddonBlocklistState(in AString id, in AString version,
|
||||
unsigned long getAddonBlocklistState(in jsval addon,
|
||||
[optional] in AString appVersion,
|
||||
[optional] in AString toolkitVersion);
|
||||
|
||||
@ -88,11 +84,11 @@ interface nsIBlocklistService : nsISupports
|
||||
|
||||
/**
|
||||
* Determine the blocklist web page of an add-on.
|
||||
* @param id
|
||||
* The ID of the blocked add-on.
|
||||
* @param addon
|
||||
* The addon item whose url is required.
|
||||
* @returns The URL of the description page.
|
||||
*/
|
||||
AString getAddonBlocklistURL(in AString id, in AString version,
|
||||
AString getAddonBlocklistURL(in jsval addon,
|
||||
[optional] in AString appVersion,
|
||||
[optional] in AString toolkitVersion);
|
||||
|
||||
@ -110,7 +106,7 @@ interface nsIBlocklistService : nsISupports
|
||||
* nsIBlocklistService to display a confirmation UI to the user before blocking
|
||||
* extensions/plugins.
|
||||
*/
|
||||
[scriptable, uuid(36f97f40-b0c9-11df-94e2-0800200c9a66)]
|
||||
[scriptable, uuid(ba915921-b9c0-400d-8e4f-ca1b80c5699a)]
|
||||
interface nsIBlocklistPrompt : nsISupports
|
||||
{
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user