Merge m-c to inbound

This commit is contained in:
Wes Kocher 2014-01-14 16:13:50 -08:00
commit 04440648e9
86 changed files with 2803 additions and 1658 deletions

View File

@ -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

View File

@ -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");

View File

@ -1,4 +1,4 @@
{
"revision": "c8716b0dcb8cc8946903355c3533481f967b52d3",
"revision": "5fed274e799a9f111cdf6476ec53c9328c55c7b9",
"repo_path": "/integration/gaia-central"
}

View File

@ -1,7 +1,7 @@
[
{
"size": 55073212,
"digest": "10560463c0804186fc59a84ecab4d5ccd1ffdf298336d3b91065c38888698f4cfab4bad743d6170e73f9bc0538f47a96baf5f1381e8b4ab484d8721301bea31e",
"size": 55276112,
"digest": "035efc2064a88bdb6f60fd126f17ee0e35de708f4666837cbeb0f3381024044a6ec3a05c244c553f9c2d852d94706f5263edde3d7dbff33872bd05db0215f5e6",
"algorithm": "sha512",
"filename": "backup-hamachi.tar.xz"
},

View File

@ -1,7 +1,7 @@
[
{
"size": 55073212,
"digest": "10560463c0804186fc59a84ecab4d5ccd1ffdf298336d3b91065c38888698f4cfab4bad743d6170e73f9bc0538f47a96baf5f1381e8b4ab484d8721301bea31e",
"size": 55276112,
"digest": "035efc2064a88bdb6f60fd126f17ee0e35de708f4666837cbeb0f3381024044a6ec3a05c244c553f9c2d852d94706f5263edde3d7dbff33872bd05db0215f5e6",
"algorithm": "sha512",
"filename": "backup-hamachi.tar.xz"
},

View File

@ -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.

View File

@ -26,7 +26,7 @@ let tests = [
sessionToken: "dead",
kA: "beef",
kB: "cafe",
isVerified: true
verified: true
},
payloadType: "message",
validateResponse: function(payload) {

View File

@ -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();
});
});

View File

@ -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;

View File

@ -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"

View File

@ -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;"

View File

@ -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();
}
}

View File

@ -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">

View File

@ -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");

View 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);
}
}
};

View File

@ -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;

View File

@ -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.
*/

View File

@ -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.

View File

@ -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();

View File

@ -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;

View File

@ -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. -->

View File

@ -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.

View File

@ -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. -->

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -216,7 +216,7 @@ documenttab[selected] .documenttab-selection {
/* Start UI ----------------------------------------------------------------- */
#content-viewport[startpage] browser {
#content-viewport[startpage] .active-tab-notificationbox {
padding-bottom: @toolbar_height@;
}

View File

@ -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;
}
},
};
/**

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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.

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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

View File

@ -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();

View File

@ -10,5 +10,4 @@ qemu = false
[test_one_seek_at_once.js]
[test_seek_up_and_down.js]
[test_bug862672.js]
[test_bug876597.js]

View File

@ -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();

View File

@ -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();

View File

@ -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.

View File

@ -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;
}

View File

@ -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
});
};

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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>

View File

@ -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;

View File

@ -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>

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -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) {

View File

@ -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
};
}

View File

@ -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"),

View File

@ -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",

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View 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]);

View 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

View File

@ -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',
]

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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
}));
}

View File

@ -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

View File

@ -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() {

View File

@ -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)) &&

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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">

View File

@ -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();
});
});
}

View File

@ -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;
},

View File

@ -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();
});
});
}

View File

@ -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();
});
});
}

View File

@ -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'));

View File

@ -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]

View File

@ -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;

View File

@ -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;

View File

@ -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
{
/**