merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-11-26 16:53:23 +01:00
commit 4805a299e4
63 changed files with 680 additions and 131 deletions

View File

@ -38,6 +38,6 @@ exports.encode = function (data, charset) {
if (isUTF8(charset))
return btoa(unescape(encodeURIComponent(data)))
data = String.fromCharCode(...[(c.charCodeAt(0) & 0xff) for (c of data)]);
data = String.fromCharCode(...Array.from(data, c => (c.charCodeAt(0) & 0xff)));
return btoa(data);
}

View File

@ -307,7 +307,7 @@ function getItemWorkerForWindow(item, window) {
var RemoteItem = Class({
initialize: function(options, manager) {
this.id = options.id;
this.contexts = [instantiateContext(c) for (c of options.contexts)];
this.contexts = options.contexts.map(instantiateContext);
this.contentScript = options.contentScript;
this.contentScriptFile = options.contentScriptFile;

View File

@ -90,7 +90,7 @@ function makeChildOptions(options) {
function makeStringArray(arrayOrValue) {
if (!arrayOrValue)
return [];
return [String(v) for (v of [].concat(arrayOrValue))];
return [].concat(arrayOrValue).map(String);
}
return {

View File

@ -355,7 +355,7 @@ function itemActivated(item, clickedNode) {
function serializeItem(item) {
return {
id: internal(item).id,
contexts: [c.serialize() for (c of item.context)],
contexts: item.context.map(c => c.serialize()),
contentScript: item.contentScript,
contentScriptFile: item.contentScriptFile,
};

View File

@ -380,7 +380,7 @@ TestRunner.prototype = {
name: this.test.name,
passed: this.test.passed,
failed: this.test.failed,
errors: [error for (error in this.test.errors)].join(", ")
errors: Object.keys(this.test.errors).join(", ")
});
if (this.onDone !== null) {

View File

@ -193,7 +193,7 @@ function showResults() {
var data = ref.__url__ ? ref.__url__ : ref;
var warning = data == "[object Object]"
? "[object " + data.constructor.name + "(" +
[p for (p in data)].join(", ") + ")]"
Object.keys(data).join(", ") + ")]"
: data;
console.warn("LEAK", warning, info.bin);
}
@ -461,8 +461,7 @@ var consoleListener = {
testConsole.error(message);
return;
}
var pointless = [err for (err of POINTLESS_ERRORS)
if (message.indexOf(err) >= 0)];
var pointless = POINTLESS_ERRORS.filter(err => message.indexOf(err) >= 0);
if (pointless.length == 0 && message)
testConsole.log(message);
}

View File

@ -62,7 +62,7 @@ var dispatcher = _ => {
// starting a dispatch loop, in order to ignore timers registered
// in side effect to dispatch while also skipping immediates that
// were removed in side effect.
let ids = [id for ([id] of immediates)];
let ids = [...immediates.keys()];
for (let id of ids) {
let immediate = immediates.get(id);
if (immediate) {

View File

@ -65,12 +65,9 @@ exports.testBasename = function(assert) {
exports.testList = function(assert) {
let list = file.list(profilePath);
let found = [ true for (name of list)
if (name === fileNameInProfile) ];
let found = list.filter(name => name === fileNameInProfile);
if (found.length > 1)
assert.fail("a dir can't contain two files of the same name!");
assert.equal(found[0], true, "file.list() should work");
assert.equal(found.length, 1, "file.list() should work");
assert.throws(function() {
file.list(filePathInProfile);

View File

@ -472,18 +472,8 @@ var LoopUI;
return;
}
// Create the menu that is shown when the menu-button' dropmarker is clicked
// inside the notification bar.
let menuPopup = document.createElementNS(kNSXUL, "menupopup");
let menuItem = menuPopup.appendChild(document.createElementNS(kNSXUL, "menuitem"));
menuItem.setAttribute("label", this._getString("infobar_menuitem_dontshowagain_label"));
menuItem.setAttribute("accesskey", this._getString("infobar_menuitem_dontshowagain_accesskey"));
menuItem.addEventListener("command", () => {
// We're being told to hide the bar permanently.
this._hideBrowserSharingInfoBar(true);
});
let box = gBrowser.getNotificationBox();
let paused = false;
let bar = box.appendNotification(
this._getString("infobar_screenshare_browser_message"),
kBrowserSharingNotificationId,
@ -491,13 +481,28 @@ var LoopUI;
null,
box.PRIORITY_WARNING_LOW,
[{
label: this._getString("infobar_button_gotit_label"),
accessKey: this._getString("infobar_button_gotit_accesskey"),
type: "menu-button",
popup: menuPopup,
anchor: "dropmarker",
label: this._getString("infobar_button_pause_label"),
accessKey: this._getString("infobar_button_pause_accesskey"),
isDefault: false,
callback: (event, buttonInfo, buttonNode) => {
paused = !paused;
bar.label = paused ? this._getString("infobar_screenshare_paused_browser_message") :
this._getString("infobar_screenshare_browser_message");
bar.classList.toggle("paused", paused);
buttonNode.label = paused ? this._getString("infobar_button_resume_label") :
this._getString("infobar_button_pause_label");
buttonNode.accessKey = paused ? this._getString("infobar_button_resume_accesskey") :
this._getString("infobar_button_pause_accesskey");
return true;
}
},
{
label: this._getString("infobar_button_stop_label"),
accessKey: this._getString("infobar_button_stop_accesskey"),
isDefault: true,
callback: () => {
this._hideBrowserSharingInfoBar();
LoopUI.MozLoopService.hangupAllChatWindows();
}
}]
);

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1 @@
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>FX_Hello-glyph-pause-12x12</title><desc>Created with Sketch.</desc><path d="M5 .5v11c0 .136-.05.253-.149.351-.099.099-.215.149-.351.149h-4c-.136 0-.253-.05-.352-.149-.099-.098-.148-.216-.148-.351v-11c0-.136.049-.253.148-.352.099-.098.217-.148.352-.148h4c.136 0 .252.05.351.148.099.099.149.217.149.352zm7 0v11c0 .136-.05.253-.149.351-.099.099-.216.149-.351.149h-4c-.136 0-.253-.05-.352-.149-.099-.098-.148-.216-.148-.351v-11c0-.136.049-.253.148-.352.099-.098.216-.148.352-.148h4c.136 0 .252.05.351.148.099.099.149.217.149.352z" sketch:type="MSShapeGroup" fill="#333"/></svg>

After

Width:  |  Height:  |  Size: 718 B

View File

@ -0,0 +1 @@
<svg width="11" height="12" viewBox="0 0 11 12" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>FX_Hello-glyph-play-12x12</title><desc>Created with Sketch.</desc><path d="M10.695 6.24l-10.263 5.704c-.118.066-.22.074-.305.023-.085-.052-.127-.144-.127-.278v-11.377c0-.134.043-.228.127-.278.085-.052.186-.044.305.022l10.263 5.704c.118.066.178.147.178.239 0 .094-.06.174-.178.241z" sketch:type="MSShapeGroup" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 473 B

View File

@ -0,0 +1 @@
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><title>FX_Hello-glyph-stop-12x12</title><desc>Created with Sketch.</desc><path d="M12 .5v11c0 .136-.05.253-.149.351-.099.099-.216.149-.351.149h-11c-.136 0-.253-.05-.352-.149-.099-.098-.148-.216-.148-.351v-11c0-.136.049-.253.148-.352.099-.098.217-.148.352-.148h11c.136 0 .252.05.351.148.099.099.149.217.149.352z" sketch:type="MSShapeGroup" fill="#D92215"/></svg>

After

Width:  |  Height:  |  Size: 499 B

View File

@ -89,6 +89,9 @@ browser.jar:
content/browser/loop/shared/img/animated-spinner.svg (content/shared/img/animated-spinner.svg)
content/browser/loop/shared/img/avatars.svg (content/shared/img/avatars.svg)
content/browser/loop/shared/img/firefox-avatar.svg (content/shared/img/firefox-avatar.svg)
content/browser/loop/shared/img/pause-12x12.svg (content/shared/img/pause-12x12.svg)
content/browser/loop/shared/img/play-12x12.svg (content/shared/img/play-12x12.svg)
content/browser/loop/shared/img/stop-12x12.svg (content/shared/img/stop-12x12.svg)
# Shared scripts
content/browser/loop/shared/js/actions.js (content/shared/js/actions.js)

View File

@ -875,10 +875,11 @@ var MozLoopServiceInternal = {
*/
hangupAllChatWindows() {
let isLoopURL = ({ src }) => /^about:loopconversation#/.test(src);
[...Chat.chatboxes].filter(isLoopURL).forEach(chatbox => {
let loopChatWindows = [...Chat.chatboxes].filter(isLoopURL);
for (let chatbox of loopChatWindows) {
let window = chatbox.content.contentWindow;
window.dispatchEvent(new window.CustomEvent("LoopHangupNow"));
});
}
},
/**

View File

@ -156,11 +156,6 @@ add_task(function* test_infoBar() {
let button = bar.querySelector(".notification-button");
Assert.ok(button, "There should be a button present");
Assert.strictEqual(button.type, "menu-button", "We're expecting a menu-button");
Assert.strictEqual(button.getAttribute("anchor"), "dropmarker",
"The popup should be opening anchored to the dropmarker");
Assert.strictEqual(button.getElementsByTagNameNS(kNSXUL, "menupopup").length, 1,
"There should be a popup attached to the button");
};
testBarProps();
@ -175,11 +170,8 @@ add_task(function* test_infoBar() {
testBarProps();
// Test hiding the infoBar.
getInfoBar().querySelector(".notification-button")
.getElementsByTagNameNS(kNSXUL, "menuitem")[0].click();
getInfoBar().querySelector(".notification-button-default").click();
Assert.equal(getInfoBar(), null, "The notification should be hidden now");
Assert.strictEqual(Services.prefs.getBoolPref(kPrefBrowserSharingInfoBar), false,
"The pref should be set to false when the menu item is clicked");
gBrowser.selectedIndex = Array.indexOf(gBrowser.tabs, createdTabs[1]);

View File

@ -198,11 +198,14 @@ room_name_untitled_page=Untitled Page
# Infobar strings
infobar_screenshare_browser_message=Users in your conversation will now be able to see the contents of any tab you click on.
infobar_button_gotit_label=Got it!
infobar_button_gotit_accesskey=G
infobar_menuitem_dontshowagain_label=Don't show this again
infobar_menuitem_dontshowagain_accesskey=D
infobar_screenshare_browser_message=You are sharing your tabs. Any tab you click on can be seen by your friends
infobar_screenshare_paused_browser_message=Tab sharing is paused
infobar_button_pause_label=Pause
infobar_button_resume_label=Resume
infobar_button_stop_label=Stop
infobar_button_pause_accesskey=P
infobar_button_stop_accesskey=S
infobar_button_resume_accesskey=R
# Context in conversation strings

View File

@ -1118,16 +1118,6 @@ notification[value="translation"] menulist > .menulist-dropmarker {
display: block;
}
/* Loop/ Hello browser styles */
notification[value="loop-sharing-notification"] .button-menubutton-button {
min-width: 0;
}
notification[value="loop-sharing-notification"] .messageImage {
list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
}
#treecolAutoCompleteImage {
max-width : 36px;
}

View File

@ -3212,24 +3212,37 @@ menulist.translate-infobar-element > .menulist-dropmarker {
}
/* Loop/ Hello browser styles */
notification[value="loop-sharing-notification"] {
background: #00a9dc;
padding: 0;
border: 0;
}
notification[value="loop-sharing-notification"].paused {
background: #ebebeb;
}
notification[value="loop-sharing-notification"] .notification-button {
padding: 1px 5px;
background: #fff;
border-radius: 0;
}
notification[value="loop-sharing-notification"] .button-menubutton-button {
-moz-appearance: none;
min-width: 0;
margin: 0;
notification[value="loop-sharing-notification"].paused .notification-button {
background: #57bd35;
}
notification[value="loop-sharing-notification"] .messageImage {
list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-menubar.png);
notification[value="loop-sharing-notification"].paused .notification-button:hover {
background: #39a017;
}
@media (min-resolution: 2dppx) {
notification[value="loop-sharing-notification"] .messageImage {
list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-menubar@2x.png);
}
notification[value="loop-sharing-notification"] .notification-button:hover,
notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
background: #ebebeb;
}
notification[value="loop-sharing-notification"] .notification-button-default,
notification[value="loop-sharing-notification"].paused .notification-button-default {
background: #fff;
}
.popup-notification-body[popupid="addon-progress"],

View File

@ -445,3 +445,83 @@
}
%endif
}
/* Loop notification */
notification[value="loop-sharing-notification"] {
-moz-appearance: none;
height: 40px;
background-color: #00a9dc;
box-shadow: 0 40px 1px rgba(0,0,0,.5) inset;
}
notification[value="loop-sharing-notification"].paused {
background-color: #ebebeb;
}
notification[value="loop-sharing-notification"] .notification-inner {
color: #fff;
padding: 0;
}
notification[value="loop-sharing-notification"].paused .notification-inner {
color: #00a9dc;
}
notification[value="loop-sharing-notification"] .notification-button {
-moz-appearance: none;
background-color: #fff;
border: 0;
border-right: solid 1px #ebebeb;
width: 100px;
height: 40px;
margin: 0;
list-style-image: url(chrome://browser/content/loop/shared/img/pause-12x12.svg);
box-shadow: 0 40px 1px rgba(0,0,0,.5) inset;
text-shadow: none;
}
notification[value="loop-sharing-notification"] .notification-button:-moz-locale-dir(rtl) {
border-right: 0;
border-left: solid 1px #ebebeb;
}
notification[value="loop-sharing-notification"].paused .notification-button {
background-color: #57bd35;
color: #fff;
list-style-image: url(chrome://browser/content/loop/shared/img/play-12x12.svg);
}
notification[value="loop-sharing-notification"].paused .notification-button:hover {
background-color: #39a017;
}
notification[value="loop-sharing-notification"] .notification-button:hover,
notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
background-color: #ebebeb;
}
notification[value="loop-sharing-notification"] .notification-button-default,
notification[value="loop-sharing-notification"].paused .notification-button-default {
color: #d92215;
background-color: #fff;
border-right: 0;
list-style-image: url(chrome://browser/content/loop/shared/img/stop-12x12.svg);
}
notification[value="loop-sharing-notification"] .notification-button .button-icon {
display: block;
-moz-margin-end: 6px;
}
notification[value="loop-sharing-notification"] .button-menubutton-button {
min-width: 0;
}
notification[value="loop-sharing-notification"] .messageImage {
list-style-image: url(chrome://browser/content/loop/shared/img/icons-16x16.svg#loop-icon-white);
margin-inline-start: 14px;
}
notification[value="loop-sharing-notification"].paused .messageImage {
list-style-image: url(chrome://browser/content/loop/shared/img/icons-16x16.svg#loop-icon-still);
}

View File

@ -2316,19 +2316,6 @@ notification[value="translation"] {
-moz-appearance: none;
}
/* Loop/ Hello browser styles */
notification[value="loop-sharing-notification"] .button-menubutton-button {
-moz-appearance: none;
min-width: 0;
border: 0;
margin: 0;
}
notification[value="loop-sharing-notification"] .messageImage {
list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
}
/* Bookmarks roots menu-items */
#subscribeToPageMenuitem:not([disabled]),
#subscribeToPageMenupopup,

View File

@ -565,6 +565,7 @@ function AnimationsTimeline(inspector) {
this.onScrubberMouseUp = this.onScrubberMouseUp.bind(this);
this.onScrubberMouseOut = this.onScrubberMouseOut.bind(this);
this.onScrubberMouseMove = this.onScrubberMouseMove.bind(this);
this.onAnimationSelected = this.onAnimationSelected.bind(this);
EventEmitter.decorate(this);
}
@ -633,14 +634,17 @@ AnimationsTimeline.prototype = {
this.win = null;
this.inspector = null;
},
destroyTargetNodes: function() {
for (let targetNode of this.targetNodes) {
targetNode.destroy();
}
this.targetNodes = [];
},
destroyTimeBlocks: function() {
for (let timeBlock of this.timeBlocks) {
timeBlock.off("selected", this.onAnimationSelected);
timeBlock.destroy();
}
this.timeBlocks = [];
@ -656,6 +660,24 @@ AnimationsTimeline.prototype = {
this.animationsEl.innerHTML = "";
},
onAnimationSelected: function(e, animation) {
// Unselect the previously selected animation if any.
[...this.rootWrapperEl.querySelectorAll(".animation.selected")].forEach(el => {
el.classList.remove("selected");
});
// Select the new animation.
let index = this.animations.indexOf(animation);
if (index === -1) {
return;
}
this.rootWrapperEl.querySelectorAll(".animation")[index]
.classList.toggle("selected");
// Relay the event to the parent component.
this.emit("selected", animation);
},
onScrubberMouseDown: function(e) {
this.moveScrubberTo(e.pageX);
this.win.addEventListener("mouseup", this.onScrubberMouseUp);
@ -764,6 +786,8 @@ AnimationsTimeline.prototype = {
timeBlock.init(timeBlockEl);
timeBlock.render(animation);
this.timeBlocks.push(timeBlock);
timeBlock.on("selected", this.onAnimationSelected);
}
// Use the document's current time to position the scrubber (if the server
// doesn't provide it, hide the scrubber entirely).
@ -872,15 +896,21 @@ AnimationsTimeline.prototype = {
* UI component responsible for displaying a single animation timeline, which
* basically looks like a rectangle that shows the delay and iterations.
*/
function AnimationTimeBlock() {}
function AnimationTimeBlock() {
EventEmitter.decorate(this);
this.onClick = this.onClick.bind(this);
}
exports.AnimationTimeBlock = AnimationTimeBlock;
AnimationTimeBlock.prototype = {
init: function(containerEl) {
this.containerEl = containerEl;
this.containerEl.addEventListener("click", this.onClick);
},
destroy: function() {
this.containerEl.removeEventListener("click", this.onClick);
while (this.containerEl.firstChild) {
this.containerEl.firstChild.remove();
}
@ -999,6 +1029,10 @@ AnimationTimeBlock.prototype = {
}
return text;
},
onClick: function() {
this.emit("selected", this.animation);
}
};

View File

@ -9,6 +9,7 @@ support-files =
doc_simple_animation.html
head.js
[browser_animation_click_selects_animation.js]
[browser_animation_controller_exposes_document_currentTime.js]
[browser_animation_empty_on_invalid_nodes.js]
[browser_animation_mutations_with_same_names.js]

View File

@ -0,0 +1,47 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that animations displayed in the timeline can be selected by clicking
// them, and that this emits the right events and adds the right classes.
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let selected = timeline.rootWrapperEl.querySelectorAll(".animation.selected");
ok(!selected.length, "There are no animations selected by default");
info("Click on the first animation, expect the right event and right class");
let animation0 = yield clickToSelect(timeline, 0);
is(animation0, timeline.animations[0],
"The selected event was emitted with the right animation");
ok(isTimeBlockSelected(timeline, 0),
"The time block has the right selected class");
info("Click on the second animation, expect the first one to be unselected");
let animation1 = yield clickToSelect(timeline, 1);
is(animation1, timeline.animations[1],
"The selected event was emitted with the right animation");
ok(isTimeBlockSelected(timeline, 1),
"The second time block has the right selected class");
ok(!isTimeBlockSelected(timeline, 0),
"The first time block has been unselected");
});
function* clickToSelect(timeline, index) {
info("Click on animation " + index + " in the timeline");
let onSelected = timeline.once("selected");
let timeBlock = timeline.rootWrapperEl.querySelectorAll(".time-block")[index];
EventUtils.sendMouseEvent({type: "click"}, timeBlock,
timeBlock.ownerDocument.defaultView);
return yield onSelected;
}
function isTimeBlockSelected(timeline, index) {
let animation = timeline.rootWrapperEl.querySelectorAll(".animation")[index];
return animation.classList.contains("selected");
}

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>

View File

@ -75,6 +75,7 @@ support-files =
[browser_cmd_pref1.js]
[browser_cmd_pref2.js]
[browser_cmd_pref3.js]
[browser_cmd_qsa.js]
[browser_cmd_restart.js]
[browser_cmd_rulers.js]
[browser_cmd_screenshot.js]

View File

@ -0,0 +1,33 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the qsa commands work as they should.
const TEST_URI = "data:text/html;charset=utf-8,<body></body>";
function test() {
helpers.addTabWithToolbar(TEST_URI, function(options) {
return helpers.audit(options, [
{
setup: 'qsa',
check: {
input: 'qsa',
hints: ' [query]',
markup: 'VVV',
status: 'VALID'
}
},
{
setup: 'qsa body',
check: {
input: 'qsa body',
hints: '',
markup: 'VVVVVVVV',
status: 'VALID'
}
}
]);
}).then(finish, helpers.handleError);
}

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="debugger.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>

View File

@ -6,6 +6,7 @@
<!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
%toolboxDTD;
]>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet rel="stylesheet" href="chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="dev-edition-promo">

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>

View File

@ -7,6 +7,8 @@
%toolboxDTD;
]>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="devtools-toolbox-window"
macanimationtype="document"

View File

@ -7,6 +7,8 @@
%toolboxDTD;
]>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="devtools-toolbox-window"
macanimationtype="document"

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/inspector/inspector.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>

View File

@ -18,7 +18,7 @@ add_task(function*() {
markup.isDragging = true;
info("Simulate a mousemove on the view, at the bottom, and expect scrolling");
let onScrolled = waitForViewScroll(markup);
let onScrolled = waitForScrollStop(markup);
markup._onMouseMove({
preventDefault: () => {},
@ -30,7 +30,7 @@ add_task(function*() {
ok(bottomScrollPos > 0, "The view was scrolled down");
info("Simulate a mousemove at the top and expect more scrolling");
onScrolled = waitForViewScroll(markup);
onScrolled = waitForScrollStop(markup);
markup._onMouseMove({
preventDefault: () => {},
@ -46,22 +46,28 @@ add_task(function*() {
markup._onMouseUp();
});
function waitForViewScroll(markup) {
/**
* Waits until the element has not scrolled for 30 consecutive frames.
*/
function* waitForScrollStop(markup) {
let el = markup.doc.documentElement;
let startPos = el.scrollTop;
let win = markup.doc.defaultView;
let lastScrollTop = el.scrollTop;
let stopFrameCount = 0;
while (stopFrameCount < 30) {
// Wait for a frame.
yield new Promise(resolve => win.requestAnimationFrame(resolve));
return new Promise(resolve => {
let isDone = () => {
if (el.scrollTop === startPos) {
resolve(el.scrollTop);
} else {
startPos = el.scrollTop;
// Continue checking every 50ms.
setTimeout(isDone, 50);
}
};
// Check if the element has scrolled.
if (lastScrollTop == el.scrollTop) {
// No scrolling since the last frame.
stopFrameCount++;
} else {
// The element has scrolled. Reset the frame counter.
stopFrameCount = 0;
lastScrollTop = el.scrollTop;
}
}
// Start checking if the view scrolled after a while.
setTimeout(isDone, 50);
});
return lastScrollTop;
}

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/netmonitor/netmonitor.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/shadereditor.css" type="text/css"?>

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>

View File

@ -238,6 +238,10 @@ body {
background-color: var(--even-animation-timeline-background-color);
}
.animation-timeline .animation.selected {
background-color: var(--theme-selection-background-semitransparent);
}
.animation-timeline .animation .target {
width: var(--timeline-sidebar-width);
overflow: hidden;
@ -254,6 +258,7 @@ body {
left: var(--timeline-sidebar-width);
right: 0;
height: var(--timeline-animation-height);
cursor: pointer;
}
/* Animation iterations */

View File

@ -2,6 +2,7 @@
<!-- 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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>

View File

@ -69,6 +69,7 @@ exports.devtoolsModules = [
"devtools/shared/gcli/commands/media",
"devtools/shared/gcli/commands/pagemod",
"devtools/shared/gcli/commands/paintflashing",
"devtools/shared/gcli/commands/qsa",
"devtools/shared/gcli/commands/restart",
"devtools/shared/gcli/commands/rulers",
"devtools/shared/gcli/commands/screenshot",

View File

@ -21,6 +21,7 @@ DevToolsModules(
'media.js',
'pagemod.js',
'paintflashing.js',
'qsa.js',
'restart.js',
'rulers.js',
'screenshot.js',

View File

@ -0,0 +1,24 @@
/* 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 l10n = require("gcli/l10n");
exports.items = [
{
item: "command",
runAt: "server",
name: "qsa",
description: l10n.lookup("qsaDesc"),
params: [{
name: "query",
type: "nodelist",
description: l10n.lookup("qsaQueryDesc")
}],
exec: function(args, context) {
return args.query.length;
}
}
];

View File

@ -1557,6 +1557,11 @@ mediaEmulateManual=View the document as if rendered on a device supporting the g
mediaEmulateType=The media type to emulate
mediaResetDesc=Stop emulating a CSS media type
# LOCALIZATION NOTE (qsaDesc, qsaQueryDesc)
# These strings describe the 'qsa' commands and all available parameters.
qsaDesc=Perform querySelectorAll on the current document and return number of matches
qsaQueryDesc=CSS selectors separated by comma
# LOCALIZATION NOTE (injectDesc, injectManual, injectLibraryDesc, injectLoaded,
# injectFailed) These strings describe the 'inject' commands and all available
# parameters.

View File

@ -683,8 +683,11 @@ this.Extension = function(addonData)
*
* To make things easier, the value of "background" and "files"[] can
* be a function, which is converted to source that is run.
*
* The generated extension is stored in the system temporary directory,
* and an nsIFile object pointing to it is returned.
*/
this.Extension.generate = function(id, data)
this.Extension.generateXPI = function(id, data)
{
let manifest = data.manifest;
if (!manifest) {
@ -766,6 +769,17 @@ this.Extension.generate = function(id, data)
zipW.close();
return file;
};
/**
* Generates a new extension using |Extension.generateXPI|, and initializes a
* new |Extension| instance which will execute it.
*/
this.Extension.generate = function(id, data)
{
let file = this.generateXPI(id, data);
flushJarCache(file);
Services.ppmm.broadcastAsyncMessage("Extension:FlushJarCache", {path: file.path});
@ -777,7 +791,7 @@ this.Extension.generate = function(id, data)
resourceURI: jarURI,
cleanupFile: file
});
}
};
Extension.prototype = extend(Object.create(ExtensionData.prototype), {
on(hook, f) {

View File

@ -458,7 +458,7 @@
else {
var callback = button.callback;
if (callback) {
var result = callback(this, button);
var result = callback(this, button, aEvent.target);
if (!result)
this.close();
aEvent.stopPropagation();

View File

@ -2975,6 +2975,8 @@ this.AddonManager = {
ERROR_FILE_ACCESS: -4,
// The add-on must be signed and isn't.
ERROR_SIGNEDSTATE_REQUIRED: -5,
// The downloaded add-on had a different type than expected.
ERROR_UNEXPECTED_ADDON_TYPE: -6,
// These must be kept in sync with AddonUpdateChecker.
// No error was encountered.

View File

@ -868,20 +868,24 @@ var loadManifestFromWebManifest = Task.async(function* loadManifestFromWebManife
return findProp(obj[field], current, properties.slice(1));
}
function getProp(path) {
return findProp(manifest, "", path.split("."));
function getProp(path, type = "String") {
let val = findProp(manifest, "", path.split("."));
if ({}.toString.call(val) != `[object ${type}]`)
throw new SyntaxError(`Expected property ${path} to be of type ${type}`);
return val;
}
function getOptionalProp(path, defValue = null) {
function getOptionalProp(path, defValue = null, type = "String") {
try {
return findProp(manifest, "", path.split("."));
return getProp(path, type);
}
catch (e) {
catch (e if !(e instanceof SyntaxError)) {
return defValue;
}
}
let mVersion = getProp("manifest_version");
let mVersion = getProp("manifest_version", "Number");
if (mVersion != 2) {
throw new Error("Expected manifest_version to be 2 but was " + mVersion);
}
@ -898,18 +902,27 @@ var loadManifestFromWebManifest = Task.async(function* loadManifestFromWebManife
addon.hasBinaryComponents = false;
addon.multiprocessCompatible = true;
addon.internalName = null;
addon.updateURL = null;
addon.updateURL = getOptionalProp("applications.gecko.update_url");
addon.updateKey = null;
addon.optionsURL = null;
addon.optionsType = null;
addon.aboutURL = null;
if (addon.updateURL != null) {
// Make sure that the URL is a valid absolute URL, and that anyone is
// allowed to load it.
let ssm = Services.scriptSecurityManager;
ssm.checkLoadURIStrWithPrincipal(ssm.createNullPrincipal({}),
addon.updateURL,
ssm.DISALLOW_INHERIT_PRINCIPAL);
}
// WebExtensions don't use iconURLs
addon.iconURL = null;
addon.icon64URL = null;
addon.icons = {};
let icons = getOptionalProp('icons');
let icons = getOptionalProp("icons", null, "Object");
if (icons) {
// filter out invalid (non-integer) size keys
Object.keys(icons)
@ -950,8 +963,9 @@ var loadManifestFromWebManifest = Task.async(function* loadManifestFromWebManife
addon.targetApplications = [{
id: TOOLKIT_ID,
minVersion: AddonManagerPrivate.webExtensionsMinPlatformVersion,
maxVersion: "*",
minVersion: getOptionalProp("application.gecko.strict_min_version",
AddonManagerPrivate.webExtensionsMinPlatformVersion),
maxVersion: getOptionalProp("application.gecko.strict_max_version", "*"),
}];
addon.targetPlatforms = [];
@ -5423,6 +5437,13 @@ AddonInstall.prototype = {
}
}
if (this.existingAddon && this.existingAddon.type == "webextension" &&
this.addon.type != "webextension") {
zipreader.close();
return Promise.reject([AddonManager.ERROR_UNEXPECTED_ADDON_TYPE,
"WebExtensions may not be upated to other extension types"]);
}
if (this.addon.type == "multipackage")
return this._loadMultipackageManifests(zipreader);

View File

@ -30,6 +30,11 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
Components.utils.import("resource://gre/modules/AsyncShutdown.jsm");
Components.utils.import("resource://testing-common/MockRegistrar.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
"resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
"resource://testing-common/httpd.js");
// We need some internal bits of AddonManager
var AMscope = Components.utils.import("resource://gre/modules/AddonManager.jsm");
var AddonManager = AMscope.AddonManager;
@ -1022,6 +1027,26 @@ function createTempXPIFile(aData) {
return file;
}
/**
* Creates an XPI file for some WebExtension data in the temporary directory and
* returns the nsIFile for it. The file will be deleted when the test completes.
*
* @param aData
* The object holding data about the add-on, as expected by
* |Extension.generateXPI|.
* @return A file pointing to the created XPI file
*/
function createTempWebExtensionFile(aData) {
if (!aData.id) {
const uuidGenerator = AM_Cc["@mozilla.org/uuid-generator;1"].getService(AM_Ci.nsIUUIDGenerator);
aData.id = uuidGenerator.generateUUID().number;
}
let file = Extension.generateXPI(aData.id, aData);
temp_xpis.push(file);
return file;
}
/**
* Sets the last modified time of the extension, usually to trigger an update
* of its metadata. If the extension is unpacked, this function assumes that
@ -1802,7 +1827,7 @@ function interpolateAndServeFile(request, response) {
response.write(data);
} catch (e) {
do_throw("Exception while serving interpolated file.");
do_throw(`Exception while serving interpolated file: ${e}\n${e.stack}`);
} finally {
cstream.close(); // this closes fstream as well
}
@ -1953,23 +1978,23 @@ function promiseFindAddonUpdates(addon, reason = AddonManager.UPDATE_WHEN_PERIOD
if ("compatibilityUpdate" in result) {
do_throw("Saw multiple compatibility update events");
}
equal(addon, addon2);
addon.compatibilityUpdate = false;
equal(addon, addon2, "onNoCompatibilityUpdateAvailable");
result.compatibilityUpdate = false;
},
onCompatibilityUpdateAvailable: function(addon2) {
if ("compatibilityUpdate" in result) {
do_throw("Saw multiple compatibility update events");
}
equal(addon, addon2);
addon.compatibilityUpdate = true;
equal(addon, addon2, "onCompatibilityUpdateAvailable");
result.compatibilityUpdate = true;
},
onNoUpdateAvailable: function(addon2) {
if ("updateAvailable" in result) {
do_throw("Saw multiple update available events");
}
equal(addon, addon2);
equal(addon, addon2, "onNoUpdateAvailable");
result.updateAvailable = false;
},
@ -1977,12 +2002,12 @@ function promiseFindAddonUpdates(addon, reason = AddonManager.UPDATE_WHEN_PERIOD
if ("updateAvailable" in result) {
do_throw("Saw multiple update available events");
}
equal(addon, addon2);
equal(addon, addon2, "onUpdateAvailable");
result.updateAvailable = install;
},
onUpdateFinished: function(addon2, error) {
equal(addon, addon2);
equal(addon, addon2, "onUpdateFinished");
if (error == AddonManager.UPDATE_STATUS_NO_ERROR) {
resolve(result);
} else {

View File

@ -6,7 +6,6 @@
Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
Components.utils.import("resource://testing-common/httpd.js");
var gServer;
const PORT = 4444;

View File

@ -40,7 +40,6 @@ Cu.import("resource://testing-common/MockRegistrar.jsm");
// Allow insecure updates
Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false)
Cu.import("resource://testing-common/httpd.js");
var testserver = createHttpServer();
gPort = testserver.identity.primaryPort;

View File

@ -14,7 +14,6 @@ var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://testing-common/MockRegistrar.jsm");
var testserver;

View File

@ -7,7 +7,6 @@
// The test extension uses an insecure update url.
Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
Components.utils.import("resource://testing-common/httpd.js");
var testserver;
const profileDir = gProfD.clone();
profileDir.append("extensions");

View File

@ -13,7 +13,6 @@ const TOOLKIT_MINVERSION = "42.0a1";
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "42.0a2", "42.0a2");
Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
Components.utils.import("resource://testing-common/httpd.js");
let testserver = createHttpServer();
gPort = testserver.identity.primaryPort;

View File

@ -28,7 +28,6 @@ var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://testing-common/MockRegistrar.jsm");
var testserver;

View File

@ -15,7 +15,6 @@ const ADDONS = {
const WORKING = "signed_bootstrap_1.xpi";
const ID = "test@tests.mozilla.org";
Components.utils.import("resource://testing-common/httpd.js");
var gServer = createHttpServer(4444);
// Creates an add-on with a broken signature by changing an existing file

View File

@ -23,7 +23,6 @@ const PARAMS = "?%REQ_VERSION%/%ITEM_ID%/%ITEM_VERSION%/%ITEM_MAXAPPVERSION%/" +
var gInstallDate;
Components.utils.import("resource://testing-common/httpd.js");
var testserver = createHttpServer();
gPort = testserver.identity.primaryPort;
mapFile("/data/test_update.rdf", testserver);
@ -1172,7 +1171,7 @@ for (let test of testParams) {
});
}
add_task(function cleanup() {
add_task(function* cleanup() {
let addons = yield new Promise(resolve => {
AddonManager.getAddonsByTypes(["extension"], resolve);
});

View File

@ -11,7 +11,6 @@ const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
Components.utils.import("resource://testing-common/httpd.js");
var testserver = createHttpServer();
gPort = testserver.identity.primaryPort;
mapFile("/data/test_update.rdf", testserver);

View File

@ -23,7 +23,6 @@ const PARAMS = "?%REQ_VERSION%/%ITEM_ID%/%ITEM_VERSION%/%ITEM_MAXAPPVERSION%/" +
var gInstallDate;
Components.utils.import("resource://testing-common/httpd.js");
var testserver = createHttpServer();
gPort = testserver.identity.primaryPort;
mapFile("/data/test_update.rdf", testserver);

View File

@ -0,0 +1,246 @@
"use strict";
const TOOLKIT_ID = "toolkit@mozilla.org";
// We don't have an easy way to serve update manifests from a secure URL.
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
var testserver = createHttpServer();
gPort = testserver.identity.primaryPort;
const uuidGenerator = AM_Cc["@mozilla.org/uuid-generator;1"].getService(AM_Ci.nsIUUIDGenerator);
const extensionsDir = gProfD.clone();
extensionsDir.append("extensions");
const addonsDir = gTmpD.clone();
addonsDir.append("addons");
addonsDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
do_register_cleanup(() => addonsDir.remove(true));
testserver.registerDirectory("/addons/", addonsDir);
let gUpdateManifests = {};
function mapManifest(aPath, aManifestData) {
gUpdateManifests[aPath] = aManifestData;
testserver.registerPathHandler(aPath, serveManifest);
}
function serveManifest(request, response) {
let manifest = gUpdateManifests[request.path];
response.setHeader("Content-Type", manifest.contentType, false);
response.write(manifest.data);
}
function promiseInstallWebExtension(aData) {
let addonFile = createTempWebExtensionFile(aData);
return promiseInstallAllFiles([addonFile]).then(() => {
Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
return promiseAddonByID(aData.id);
});
}
var checkUpdates = Task.async(function* (aData, aReason = AddonManager.UPDATE_WHEN_PERIODIC_UPDATE) {
function provide(obj, path, value) {
path = path.split(".");
let prop = path.pop();
for (let key of path) {
if (!(key in obj))
obj[key] = {};
obj = obj[key];
}
if (!(prop in obj))
obj[prop] = value;
}
provide(aData, "addon.id", uuidGenerator.generateUUID().number);
let id = aData.addon.id;
let updatePath = `/updates/${id}.json`.replace(/[{}]/g, "");
let updateUrl = `http://localhost:${gPort}${updatePath}`
let addonData = { updates: [] };
let manifestJSON = {
addons: { [id]: addonData }
};
provide(aData, "addon.manifest.applications.gecko.update_url", updateUrl);
let awaitInstall = promiseInstallWebExtension(aData.addon);
for (let version of Object.keys(aData.updates)) {
let update = aData.updates[version];
update.version = version;
provide(update, "addon.id", id);
let addon = update.addon;
delete update.addon;
let file;
if (addon.rdf) {
provide(addon, "version", version);
provide(addon, "targetApplications", [{id: TOOLKIT_ID,
minVersion: "42",
maxVersion: "*"}]);
file = createTempXPIFile(addon);
} else {
provide(addon, "manifest.version", version);
file = createTempWebExtensionFile(addon);
}
file.moveTo(addonsDir, `${id}-${version}.xpi`.replace(/[{}]/g, ""));
let path = `/addons/${file.leafName}`;
provide(update, "update_link", `http://localhost:${gPort}${path}`);
addonData.updates.push(update);
}
mapManifest(updatePath, { data: JSON.stringify(manifestJSON),
contentType: "application/json" });
let addon = yield awaitInstall;
let updates = yield promiseFindAddonUpdates(addon, aReason);
updates.addon = addon;
return updates;
});
function run_test() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "42.0", "42.0");
startupManager();
do_register_cleanup(promiseShutdownManager);
run_next_test();
}
// Check that compatibility updates are applied.
add_task(function* checkUpdateMetadata() {
let update = yield checkUpdates({
addon: {
manifest: {
version: "1.0",
application: { gecko: { strict_max_version: "45" } },
}
},
updates: {
"1.0": {
applications: { gecko: { strict_min_version: "40",
strict_max_version: "48" } },
}
}
});
ok(update.compatibilityUpdate, "have compat update");
ok(!update.updateAvailable, "have no add-on update");
ok(update.addon.isCompatibleWith("40", "40"), "compatible min");
ok(update.addon.isCompatibleWith("48", "48"), "compatible max");
ok(!update.addon.isCompatibleWith("49", "49"), "not compatible max");
update.addon.uninstall();
});
// Check that updates from web extensions to web extensions succeed.
add_task(function* checkUpdateToWebExt() {
let update = yield checkUpdates({
addon: { manifest: { version: "1.0" } },
updates: {
"1.1": { },
"1.2": { },
"1.3": { "applications": { "gecko": { "strict_min_version": "48" } } },
}
});
ok(!update.compatibilityUpdate, "have no compat update");
ok(update.updateAvailable, "have add-on update");
equal(update.addon.version, "1.0", "add-on version");
yield promiseCompleteAllInstalls([update.updateAvailable]);
let addon = yield promiseAddonByID(update.addon.id);
equal(addon.version, "1.2", "new add-on version");
addon.uninstall();
});
// Check that updates from web extensions to XUL extensions fail.
add_task(function* checkUpdateToRDF() {
let update = yield checkUpdates({
addon: { manifest: { version: "1.0" } },
updates: {
"1.1": { addon: { rdf: true } },
}
});
ok(!update.compatibilityUpdate, "have no compat update");
ok(update.updateAvailable, "have add-on update");
equal(update.addon.version, "1.0", "add-on version");
let result = yield new Promise((resolve, reject) => {
update.updateAvailable.addListener({
onDownloadFailed: resolve,
onDownloadEnded: reject,
onInstalling: reject,
onInstallStarted: reject,
onInstallEnded: reject,
});
update.updateAvailable.install();
});
equal(result.error, AddonManager.ERROR_UNEXPECTED_ADDON_TYPE, "error: unexpected add-on type");
let addon = yield promiseAddonByID(update.addon.id);
equal(addon.version, "1.0", "new add-on version");
addon.uninstall();
});
// Check that illegal update URLs are rejected.
add_task(function* checkIllegalUpdateURL() {
const URLS = ["chrome://browser/content/",
"data:text/json,...",
"javascript:;",
"/"];
for (let url of URLS) {
let { messages } = yield promiseConsoleOutput(() => {
return new Promise((resolve, reject) => {
let addonFile = createTempWebExtensionFile({
manifest: { applications: { gecko: { update_url: url } } },
});
AddonManager.getInstallForFile(addonFile, install => {
Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
if (install && install.state == AddonManager.STATE_DOWNLOAD_FAILED)
resolve();
reject(new Error("Unexpected state: " + (install && install.state)))
});
});
});
ok(messages.some(msg => /nsIScriptSecurityManager.checkLoadURIStrWithPrincipal/.test(msg)),
"Got checkLoadURI error");
}
});

View File

@ -268,6 +268,7 @@ fail-if = os == "android"
[test_update.js]
# Bug 676992: test consistently hangs on Android
skip-if = os == "android"
[test_update_webextensions.js]
[test_updateCancel.js]
[test_update_strictcompat.js]
# Bug 676992: test consistently hangs on Android

View File

@ -32,5 +32,4 @@ skip-if = appname != "firefox"
[test_temporary.js]
[include:xpcshell-shared.ini]