mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge fx-team to mozilla-central a=merge
This commit is contained in:
commit
4805a299e4
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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)
|
||||
|
@ -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"));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"],
|
||||
|
@ -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);
|
||||
}
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
}
|
@ -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"?>
|
||||
|
@ -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]
|
||||
|
33
devtools/client/commandline/test/browser_cmd_qsa.js
Normal file
33
devtools/client/commandline/test/browser_cmd_qsa.js
Normal 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);
|
||||
}
|
@ -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"?>
|
||||
|
@ -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">
|
||||
|
@ -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"?>
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"?>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"?>
|
||||
|
@ -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"?>
|
||||
|
@ -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"?>
|
||||
|
@ -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"?>
|
||||
|
@ -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"?>
|
||||
|
@ -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 */
|
||||
|
@ -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"?>
|
||||
|
@ -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",
|
||||
|
@ -21,6 +21,7 @@ DevToolsModules(
|
||||
'media.js',
|
||||
'pagemod.js',
|
||||
'paintflashing.js',
|
||||
'qsa.js',
|
||||
'restart.js',
|
||||
'rulers.js',
|
||||
'screenshot.js',
|
||||
|
24
devtools/shared/gcli/commands/qsa.js
Normal file
24
devtools/shared/gcli/commands/qsa.js
Normal 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;
|
||||
}
|
||||
}
|
||||
];
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
});
|
@ -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
|
||||
|
@ -32,5 +32,4 @@ skip-if = appname != "firefox"
|
||||
[test_temporary.js]
|
||||
|
||||
|
||||
|
||||
[include:xpcshell-shared.ini]
|
||||
|
Loading…
Reference in New Issue
Block a user