зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team. a=merge
This commit is contained in:
Коммит
60c3cb1548
|
@ -7,11 +7,15 @@ module.metadata = {
|
|||
"stability": "unstable"
|
||||
};
|
||||
|
||||
const { Ci } = require("chrome");
|
||||
const { Ci, Cc, Cu } = require("chrome");
|
||||
const core = require("../l10n/core");
|
||||
const { loadSheet, removeSheet } = require("../stylesheet/utils");
|
||||
const { process, frames } = require("../remote/child");
|
||||
const { Services } = require("resource://gre/modules/Services.jsm");
|
||||
var observerService = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
|
||||
const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
|
||||
const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");
|
||||
|
||||
const assetsURI = require('../self').data.url();
|
||||
|
||||
|
@ -115,7 +119,7 @@ let enabled = false;
|
|||
function enable() {
|
||||
if (enabled)
|
||||
return;
|
||||
Services.obs.addObserver(onContentWindow, ON_CONTENT, false);
|
||||
addObserver(onContentWindow, ON_CONTENT, false);
|
||||
enabled = true;
|
||||
}
|
||||
process.port.on("sdk/l10n/html/enable", enable);
|
||||
|
@ -123,7 +127,7 @@ process.port.on("sdk/l10n/html/enable", enable);
|
|||
function disable() {
|
||||
if (!enabled)
|
||||
return;
|
||||
Services.obs.removeObserver(onContentWindow, ON_CONTENT);
|
||||
removeObserver(onContentWindow, ON_CONTENT);
|
||||
enabled = false;
|
||||
}
|
||||
process.port.on("sdk/l10n/html/disable", disable);
|
||||
|
|
|
@ -62,7 +62,6 @@ function WindowTracker(delegate) {
|
|||
}
|
||||
|
||||
this._delegate = delegate;
|
||||
this._loadingWindows = [];
|
||||
|
||||
for (let window of getWindows())
|
||||
this._regWindow(window);
|
||||
|
@ -81,17 +80,12 @@ WindowTracker.prototype = {
|
|||
if (ignoreWindow(window))
|
||||
return;
|
||||
|
||||
this._loadingWindows.push(window);
|
||||
window.addEventListener('load', this, true);
|
||||
},
|
||||
|
||||
_unregLoadingWindow: function _unregLoadingWindow(window) {
|
||||
var index = this._loadingWindows.indexOf(window);
|
||||
|
||||
if (index != -1) {
|
||||
this._loadingWindows.splice(index, 1);
|
||||
window.removeEventListener('load', this, true);
|
||||
}
|
||||
// This may have no effect if we ignored the window in _regLoadingWindow().
|
||||
window.removeEventListener('load', this, true);
|
||||
},
|
||||
|
||||
_regWindow: function _regWindow(window) {
|
||||
|
|
|
@ -254,16 +254,6 @@ toolbar[customizing] > .overflow-button {
|
|||
-moz-box-ordinal-group: 0;
|
||||
}
|
||||
|
||||
/* In private windows, the #titlebar-content is higher because of the
|
||||
* private browsing indicator. With the margin-top the titlebar buttons
|
||||
* align to the top correctly in that case, but only if we don't stretch
|
||||
* the box they're in because the container is too high, so we override
|
||||
* the default alignment value (stretch).
|
||||
*/
|
||||
#main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-buttonbox-container {
|
||||
-moz-box-align: start;
|
||||
}
|
||||
|
||||
%else
|
||||
/* On non-OSX, these should be start-aligned */
|
||||
#titlebar-buttonbox-container {
|
||||
|
|
|
@ -5394,7 +5394,9 @@ function hrefAndLinkNodeForClickEvent(event)
|
|||
if (node.nodeType == Node.ELEMENT_NODE &&
|
||||
(node.localName == "a" ||
|
||||
node.namespaceURI == "http://www.w3.org/1998/Math/MathML")) {
|
||||
href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
||||
href = node.getAttribute("href") ||
|
||||
node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
||||
|
||||
if (href) {
|
||||
baseURI = node.baseURI;
|
||||
break;
|
||||
|
|
|
@ -593,7 +593,8 @@ var ClickEventHandler = {
|
|||
if (node.nodeType == content.Node.ELEMENT_NODE &&
|
||||
(node.localName == "a" ||
|
||||
node.namespaceURI == "http://www.w3.org/1998/Math/MathML")) {
|
||||
href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
||||
href = node.getAttribute("href") ||
|
||||
node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
||||
if (href) {
|
||||
baseURI = node.ownerDocument.baseURIObject;
|
||||
break;
|
||||
|
|
|
@ -221,7 +221,7 @@ var gDropTargetShim = {
|
|||
*/
|
||||
_dispatchEvent: function (aEvent, aType, aTarget) {
|
||||
let node = aTarget.node;
|
||||
let event = document.createEvent("DragEvents");
|
||||
let event = document.createEvent("DragEvent");
|
||||
|
||||
// The event should not bubble to prevent recursion.
|
||||
event.initDragEvent(aType, false, true, window, 0, 0, 0, 0, 0, false, false,
|
||||
|
|
|
@ -1566,8 +1566,8 @@ nsContextMenu.prototype = {
|
|||
if (href)
|
||||
return href;
|
||||
|
||||
href = this.link.getAttributeNS("http://www.w3.org/1999/xlink",
|
||||
"href");
|
||||
href = this.link.getAttribute("href") ||
|
||||
this.link.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
||||
|
||||
if (!href || !href.match(/\S/)) {
|
||||
// Without this we try to save as the current doc,
|
||||
|
|
|
@ -19,7 +19,7 @@ add_task(function* () {
|
|||
|
||||
function sendDragEvent(aEventType, aTarget) {
|
||||
let dataTransfer = new content.DataTransfer(aEventType, false);
|
||||
let event = content.document.createEvent("DragEvents");
|
||||
let event = content.document.createEvent("DragEvent");
|
||||
event.initDragEvent(aEventType, true, true, content, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
aTarget.dispatchEvent(event);
|
||||
|
|
|
@ -12,7 +12,7 @@ add_task(function* () {
|
|||
return ContentTask.spawn(gBrowser.selectedBrowser, { data: data }, function*(args) {
|
||||
let dataTransfer = new content.DataTransfer("dragstart", false);
|
||||
dataTransfer.mozSetDataAt("text/x-moz-url", args.data, 0);
|
||||
let event = content.document.createEvent("DragEvents");
|
||||
let event = content.document.createEvent("DragEvent");
|
||||
event.initDragEvent("drop", true, true, content, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ add_task(function* () {
|
|||
function sendDropEvent(aCellIndex, aDragData) {
|
||||
let dataTransfer = new content.DataTransfer("dragstart", false);
|
||||
dataTransfer.mozSetDataAt("text/x-moz-url", aDragData, 0);
|
||||
let event = content.document.createEvent("DragEvents");
|
||||
let event = content.document.createEvent("DragEvent");
|
||||
event.initDragEvent("drop", true, true, content, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
|
||||
|
|
|
@ -78,14 +78,14 @@ function doDragEvent(sourceIndex, dropIndex) {
|
|||
return ContentTask.spawn(gBrowser.selectedBrowser,
|
||||
{ sourceIndex: sourceIndex, dropIndex: dropIndex }, function*(args) {
|
||||
let dataTransfer = new content.DataTransfer("dragstart", false);
|
||||
let event = content.document.createEvent("DragEvents");
|
||||
let event = content.document.createEvent("DragEvent");
|
||||
event.initDragEvent("dragstart", true, true, content, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
|
||||
let target = content.gGrid.cells[args.sourceIndex].site.node;
|
||||
target.dispatchEvent(event);
|
||||
|
||||
event = content.document.createEvent("DragEvents");
|
||||
event = content.document.createEvent("DragEvent");
|
||||
event.initDragEvent("drop", true, true, content, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
|
||||
|
|
|
@ -419,7 +419,7 @@ function* simulateExternalDrop(aDestIndex) {
|
|||
let dataTransfer = new iframe.contentWindow.DataTransfer("dragstart", false);
|
||||
dataTransfer.mozSetDataAt("text/x-moz-url", "http://example99.com/", 0);
|
||||
|
||||
let event = content.document.createEvent("DragEvents");
|
||||
let event = content.document.createEvent("DragEvent");
|
||||
event.initDragEvent("drop", true, true, content, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
|
||||
|
|
|
@ -834,7 +834,7 @@
|
|||
"name": "result",
|
||||
"optional": true,
|
||||
"type": "array",
|
||||
"items": {"type": "any", "minimum": 0},
|
||||
"items": {"type": "any"},
|
||||
"description": "The result of the script in every injected frame."
|
||||
}
|
||||
]
|
||||
|
|
|
@ -41,8 +41,28 @@ const AutoMigrate = {
|
|||
return BOOKMARKS | HISTORY | PASSWORDS;
|
||||
},
|
||||
|
||||
_checkIfEnabled() {
|
||||
let pref = Preferences.get(kAutoMigrateEnabledPref, false);
|
||||
// User-set values should take precedence:
|
||||
if (Services.prefs.prefHasUserValue(kAutoMigrateEnabledPref)) {
|
||||
return pref;
|
||||
}
|
||||
// If we're using the default value, make sure the distribution.ini
|
||||
// value is taken into account even early on startup.
|
||||
try {
|
||||
let distributionFile = Services.dirsvc.get("XREAppDist", Ci.nsIFile);
|
||||
distributionFile.append("distribution.ini");
|
||||
let parser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
|
||||
getService(Ci.nsIINIParserFactory).
|
||||
createINIParser(distributionFile);
|
||||
return JSON.parse(parser.getString("Preferences", kAutoMigrateEnabledPref));
|
||||
} catch (ex) { /* ignore exceptions (file doesn't exist, invalid value, etc.) */ }
|
||||
|
||||
return pref;
|
||||
},
|
||||
|
||||
init() {
|
||||
this.enabled = Preferences.get(kAutoMigrateEnabledPref, false);
|
||||
this.enabled = this._checkIfEnabled();
|
||||
if (this.enabled) {
|
||||
this.maybeInitUndoObserver();
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
MARIONETTE_UNIT_MANIFESTS += ['tests/marionette/manifest.ini']
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
|
|
|
@ -2614,6 +2614,45 @@ ContentPermissionPrompt.prototype = {
|
|||
"geo-notification-icon", options);
|
||||
},
|
||||
|
||||
_promptFlyWebPublishServer : function(aRequest) {
|
||||
var message = "Would you like to let this site start a server accessible to nearby devices and people?";
|
||||
var actions = [
|
||||
{
|
||||
stringId: "flyWebPublishServer.allowPublishServer",
|
||||
action: Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION
|
||||
},
|
||||
{
|
||||
stringId: "flyWebPublishServer.denyPublishServer",
|
||||
action: Ci.nsIPermissionManager.DENY_ACTION,
|
||||
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION
|
||||
}
|
||||
];
|
||||
|
||||
let options = {
|
||||
learnMoreURL: "https://flyweb.github.io",
|
||||
popupIconURL: "chrome://flyweb/skin/icon-64.png"
|
||||
};
|
||||
|
||||
let browser = this._getBrowserForRequest(aRequest);
|
||||
let chromeDoc = browser.ownerDocument;
|
||||
let iconElem = chromeDoc.getElementById("flyweb-publish-server-notification-icon");
|
||||
if (!iconElem) {
|
||||
let notificationPopupBox = chromeDoc.getElementById("notification-popup-box");
|
||||
let notificationIcon = chromeDoc.createElement("image");
|
||||
notificationIcon.setAttribute("id", "flyweb-publish-server-notification-icon");
|
||||
notificationIcon.setAttribute("src", "chrome://flyweb/skin/icon-64.png");
|
||||
notificationIcon.setAttribute("class", "notification-anchor-icon flyweb-publish-server-icon");
|
||||
notificationIcon.setAttribute("style", "filter: url(chrome://browser/skin/filters.svg#fill); fill: currentColor; opacity: .4;");
|
||||
notificationIcon.setAttribute("role", "button");
|
||||
notificationIcon.setAttribute("aria-label", "View the publish-server request");
|
||||
notificationPopupBox.appendChild(notificationIcon);
|
||||
}
|
||||
|
||||
this._showPrompt(aRequest, message, "flyweb-publish-server", actions, "flyweb-publish-server",
|
||||
"flyweb-publish-server-notification-icon", options);
|
||||
},
|
||||
|
||||
_promptWebNotifications : function(aRequest) {
|
||||
var message = gBrowserBundle.GetStringFromName("webNotifications.receiveFromSite");
|
||||
|
||||
|
@ -2678,7 +2717,8 @@ ContentPermissionPrompt.prototype = {
|
|||
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
|
||||
|
||||
const kFeatureKeys = { "geolocation" : "geo",
|
||||
"desktop-notification" : "desktop-notification"
|
||||
"desktop-notification" : "desktop-notification",
|
||||
"flyweb-publish-server": "flyweb-publish-server"
|
||||
};
|
||||
|
||||
// Make sure that we support the request.
|
||||
|
@ -2721,6 +2761,11 @@ ContentPermissionPrompt.prototype = {
|
|||
case "desktop-notification":
|
||||
this._promptWebNotifications(request);
|
||||
break;
|
||||
case "flyweb-publish-server":
|
||||
if (AppConstants.NIGHTLY_BUILD) {
|
||||
this._promptFlyWebPublishServer(request);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
|||
// The amount of people to be part of e10s
|
||||
const TEST_THRESHOLD = {
|
||||
"beta" : 0.5, // 50%
|
||||
"release" : 1.0, // 100%
|
||||
};
|
||||
|
||||
const ADDON_ROLLOUT_POLICY = {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>e10srollout@mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:version>1.2</em:version>
|
||||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
|
|
|
@ -378,6 +378,16 @@ geolocation.neverShareLocation.accesskey=N
|
|||
geolocation.shareWithSite2=Would you like to share your location with this site?
|
||||
geolocation.shareWithFile2=Would you like to share your location with this file?
|
||||
|
||||
# FlyWeb UI
|
||||
# LOCALIZATION NOTE (flyWebPublishServer.allowPublishServer): This is an experimental feature only shipping in Nightly, and doesn't need translation.
|
||||
flyWebPublishServer.allowPublishServer=Allow Server
|
||||
# LOCALIZATION NOTE (flyWebPublishServer.allowPublishServer.accessKey): This is an experimental feature only shipping in Nightly, and doesn't need translation.
|
||||
flyWebPublishServer.allowPublishServer.accesskey=A
|
||||
# LOCALIZATION NOTE (flyWebPublishServer.denyPublishServer): This is an experimental feature only shipping in Nightly, and doesn't need translation.
|
||||
flyWebPublishServer.denyPublishServer=Block Server
|
||||
# LOCALIZATION NOTE (flyWebPublishServer.denyPublishServer.accessKey): This is an experimental feature only shipping in Nightly, and doesn't need translation.
|
||||
flyWebPublishServer.denyPublishServer.accesskey=B
|
||||
|
||||
webNotifications.receiveForSession=Receive for this session
|
||||
webNotifications.receiveForSession.accesskey=s
|
||||
webNotifications.alwaysReceive=Always Receive Notifications
|
||||
|
|
|
@ -118,13 +118,17 @@
|
|||
/**
|
||||
* We also vertically center the window buttons.
|
||||
*/
|
||||
#titlebar-buttonbox-container {
|
||||
-moz-box-align: start;
|
||||
}
|
||||
|
||||
#main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-buttonbox-container,
|
||||
#main-window[tabsintitlebar] > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
|
||||
margin-top: @windowButtonMarginTop@;
|
||||
}
|
||||
|
||||
#main-window:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-buttonbox-container:-moz-lwtheme,
|
||||
#main-window:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button:-moz-lwtheme {
|
||||
#main-window:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-buttonbox-container,
|
||||
#main-window:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,10 +58,6 @@
|
|||
--urlbar-dropmarker-active-2x-region: rect(0px, 33px, 14px, 22px);
|
||||
}
|
||||
|
||||
:root[devtoolstheme="dark"] #identity-box {
|
||||
--identity-box-chrome-color: #46afe3;
|
||||
}
|
||||
|
||||
:root[devtoolstheme="light"] {
|
||||
--url-and-searchbar-background-color: #fff;
|
||||
|
||||
|
|
|
@ -5,17 +5,6 @@
|
|||
%endif
|
||||
|
||||
#identity-box {
|
||||
--identity-box-verified-color: hsl(92,100%,30%);
|
||||
%ifdef MOZ_OFFICIAL_BRANDING
|
||||
--identity-box-chrome-color: rgb(229,115,0);
|
||||
%else
|
||||
%if MOZ_UPDATE_CHANNEL == aurora
|
||||
--identity-box-chrome-color: rgb(51,30,84);
|
||||
%else
|
||||
--identity-box-chrome-color: rgb(0,33,71);
|
||||
%endif
|
||||
%endif
|
||||
|
||||
font-size: .9em;
|
||||
padding: 3px 5px;
|
||||
overflow: hidden;
|
||||
|
@ -24,14 +13,16 @@
|
|||
transition: padding-left, padding-right;
|
||||
}
|
||||
|
||||
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #connection-icon,
|
||||
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #identity-icon-labels {
|
||||
color: var(--identity-box-verified-color);
|
||||
color: hsl(92,100%,30%);
|
||||
}
|
||||
|
||||
#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #connection-icon,
|
||||
#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #identity-icon-labels {
|
||||
color: var(--identity-box-chrome-color);
|
||||
%ifdef MOZ_OFFICIAL_BRANDING
|
||||
color: rgb(229,115,0);
|
||||
%else
|
||||
color: inherit;
|
||||
%endif
|
||||
}
|
||||
|
||||
#identity-icon-labels:-moz-locale-dir(ltr) {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -23,7 +23,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {Ci, Cc} = require("chrome");
|
||||
const Services = require("Services");
|
||||
const focusManager = Services.focus;
|
||||
const {KeyCodes} = require("devtools/client/shared/keycodes");
|
||||
|
@ -43,7 +42,6 @@ const MAX_POPUP_ENTRIES = 500;
|
|||
const FOCUS_FORWARD = focusManager.MOVEFOCUS_FORWARD;
|
||||
const FOCUS_BACKWARD = focusManager.MOVEFOCUS_BACKWARD;
|
||||
|
||||
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");
|
||||
|
||||
|
@ -1467,7 +1465,7 @@ InplaceEditor.prototype = {
|
|||
* @return {Array} array of CSS property names (Strings)
|
||||
*/
|
||||
_getCSSPropertyList: function () {
|
||||
return CSSPropertyList;
|
||||
return this.cssProperties.getNames().sort();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1556,11 +1554,3 @@ function copyBoxModelStyles(from, to) {
|
|||
function moveFocus(win, direction) {
|
||||
return focusManager.moveFocus(win, null, direction, 0);
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "CSSPropertyList", function () {
|
||||
return domUtils.getCSSPropertyNames(domUtils.INCLUDE_ALIASES).sort();
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "domUtils", function () {
|
||||
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||
});
|
||||
|
|
|
@ -41,10 +41,6 @@ function PrefBranch(parent, name, fullName) {
|
|||
this._hasUserValue = false;
|
||||
this._userValue = null;
|
||||
this._type = PREF_INVALID;
|
||||
|
||||
if (!parent) {
|
||||
this._initializeRoot();
|
||||
}
|
||||
}
|
||||
|
||||
PrefBranch.prototype = {
|
||||
|
@ -469,6 +465,7 @@ const Services = {
|
|||
get prefs() {
|
||||
if (!this._prefs) {
|
||||
this._prefs = new PrefBranch(null, "", "");
|
||||
this._prefs._initializeRoot();
|
||||
}
|
||||
return this._prefs;
|
||||
},
|
||||
|
|
|
@ -105,7 +105,8 @@ function initializeAutoCompletion(ctx, options = {}) {
|
|||
// TODO: Integrate tern autocompletion with this autocomplete API.
|
||||
return;
|
||||
} else if (ed.config.mode == Editor.modes.css) {
|
||||
completer = new CSSCompleter({walker: options.walker});
|
||||
completer = new CSSCompleter({walker: options.walker,
|
||||
cssProperties: options.cssProperties});
|
||||
}
|
||||
|
||||
function insertSelectedPopupItem() {
|
||||
|
|
|
@ -5,11 +5,8 @@
|
|||
"use strict";
|
||||
|
||||
/* eslint-disable complexity */
|
||||
|
||||
/* eslint-disable mozilla/reject-some-requires */
|
||||
const { Cc, Ci } = require("chrome");
|
||||
/* eslint-enable mozilla/reject-some-requires */
|
||||
const {cssTokenizer, cssTokenizerWithLineColumn} = require("devtools/shared/css-parsing-utils");
|
||||
const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
|
||||
/**
|
||||
* Here is what this file (+ css-parsing-utils.js) do.
|
||||
|
@ -78,8 +75,6 @@ const SELECTOR_STATES = {
|
|||
};
|
||||
/* eslint-enable no-inline-comments */
|
||||
|
||||
const { properties, propertyNames } = getCSSKeywords();
|
||||
|
||||
/**
|
||||
* Constructor for the autocompletion object.
|
||||
*
|
||||
|
@ -87,10 +82,15 @@ const { properties, propertyNames } = getCSSKeywords();
|
|||
* - walker {Object} The object used for query selecting from the current
|
||||
* target's DOM.
|
||||
* - maxEntries {Number} Maximum selectors suggestions to display.
|
||||
* - cssProperties {Object} The database of CSS properties.
|
||||
*/
|
||||
function CSSCompleter(options = {}) {
|
||||
this.walker = options.walker;
|
||||
this.maxEntries = options.maxEntries || 15;
|
||||
// If no css properties database is passed in, default to the client list.
|
||||
this.cssProperties = options.cssProperties || getClientCssProperties();
|
||||
|
||||
this.propertyNames = this.cssProperties.getNames().sort();
|
||||
|
||||
// Array containing the [line, ch, scopeStack] for the locations where the
|
||||
// CSS state is "null"
|
||||
|
@ -844,18 +844,18 @@ CSSCompleter.prototype = {
|
|||
return Promise.resolve(finalList);
|
||||
}
|
||||
|
||||
let length = propertyNames.length;
|
||||
let length = this.propertyNames.length;
|
||||
let i = 0, count = 0;
|
||||
for (; i < length && count < this.maxEntries; i++) {
|
||||
if (propertyNames[i].startsWith(startProp)) {
|
||||
if (this.propertyNames[i].startsWith(startProp)) {
|
||||
count++;
|
||||
let propName = propertyNames[i];
|
||||
let propName = this.propertyNames[i];
|
||||
finalList.push({
|
||||
preLabel: startProp,
|
||||
label: propName,
|
||||
text: propName + ": "
|
||||
});
|
||||
} else if (propertyNames[i] > startProp) {
|
||||
} else if (this.propertyNames[i] > startProp) {
|
||||
// We have crossed all possible matches alphabetically.
|
||||
break;
|
||||
}
|
||||
|
@ -872,7 +872,7 @@ CSSCompleter.prototype = {
|
|||
*/
|
||||
completeValues: function (propName, startValue) {
|
||||
let finalList = [];
|
||||
let list = ["!important;", ...(properties[propName] || [])];
|
||||
let list = ["!important;", ...this.cssProperties.getValues(propName)];
|
||||
// If there is no character being completed, we are showing an initial list
|
||||
// of possible values. Skipping '!important' in this case.
|
||||
if (!startValue) {
|
||||
|
@ -1211,29 +1211,4 @@ CSSCompleter.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of all property names and a map of property name vs possible
|
||||
* CSS values provided by the Gecko engine.
|
||||
*
|
||||
* @return {Object} An object with following properties:
|
||||
* - propertyNames {Array} Array of string containing all the possible
|
||||
* CSS property names.
|
||||
* - properties {Object|Map} A map where key is the property name and
|
||||
* value is an array of string containing all the possible
|
||||
* CSS values the property can have.
|
||||
*/
|
||||
function getCSSKeywords() {
|
||||
let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
|
||||
.getService(Ci.inIDOMUtils);
|
||||
let props = {};
|
||||
let propNames = domUtils.getCSSPropertyNames(domUtils.INCLUDE_ALIASES);
|
||||
propNames.forEach(prop => {
|
||||
props[prop] = domUtils.getCSSValuesForProperty(prop).sort();
|
||||
});
|
||||
return {
|
||||
properties: props,
|
||||
propertyNames: propNames.sort()
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = CSSCompleter;
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/* eslint-disable mozilla/reject-some-requires */
|
||||
const {Cc, Ci} = require("chrome");
|
||||
/* eslint-enable mozilla/reject-some-requires */
|
||||
|
||||
const {
|
||||
EXPAND_TAB,
|
||||
TAB_SIZE,
|
||||
|
@ -37,6 +33,7 @@ const Services = require("Services");
|
|||
const promise = require("promise");
|
||||
const events = require("devtools/shared/event-emitter");
|
||||
const { PrefObserver } = require("devtools/client/styleeditor/utils");
|
||||
const { getClientCssProperties } = require("devtools/shared/fronts/css-properties");
|
||||
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
const L10N = new LocalizationHelper("devtools/locale/sourceeditor.properties");
|
||||
|
@ -114,8 +111,6 @@ const CM_MAPPING = [
|
|||
"getViewport"
|
||||
];
|
||||
|
||||
const { cssProperties, cssValues, cssColors } = getCSSKeywords();
|
||||
|
||||
const editors = new WeakMap();
|
||||
|
||||
Editor.modes = {
|
||||
|
@ -240,6 +235,14 @@ function Editor(config) {
|
|||
this.config.externalScripts = [];
|
||||
}
|
||||
|
||||
if (this.config.cssProperties) {
|
||||
// Ensure that autocompletion has cssProperties if it's passed in via the options.
|
||||
this.config.autocompleteOpts.cssProperties = this.config.cssProperties;
|
||||
} else {
|
||||
// Use a static client-side database of CSS values if none is provided.
|
||||
this.config.cssProperties = getClientCssProperties();
|
||||
}
|
||||
|
||||
events.decorate(this);
|
||||
}
|
||||
|
||||
|
@ -289,17 +292,25 @@ Editor.prototype = {
|
|||
}
|
||||
});
|
||||
// Replace the propertyKeywords, colorKeywords and valueKeywords
|
||||
// properties of the CSS MIME type with the values provided by Gecko.
|
||||
// properties of the CSS MIME type with the values provided by the CSS properties
|
||||
// database.
|
||||
|
||||
const {
|
||||
propertyKeywords,
|
||||
colorKeywords,
|
||||
valueKeywords
|
||||
} = getCSSKeywords(this.config.cssProperties);
|
||||
|
||||
let cssSpec = win.CodeMirror.resolveMode("text/css");
|
||||
cssSpec.propertyKeywords = cssProperties;
|
||||
cssSpec.colorKeywords = cssColors;
|
||||
cssSpec.valueKeywords = cssValues;
|
||||
cssSpec.propertyKeywords = propertyKeywords;
|
||||
cssSpec.colorKeywords = colorKeywords;
|
||||
cssSpec.valueKeywords = valueKeywords;
|
||||
win.CodeMirror.defineMIME("text/css", cssSpec);
|
||||
|
||||
let scssSpec = win.CodeMirror.resolveMode("text/x-scss");
|
||||
scssSpec.propertyKeywords = cssProperties;
|
||||
scssSpec.colorKeywords = cssColors;
|
||||
scssSpec.valueKeywords = cssValues;
|
||||
scssSpec.propertyKeywords = propertyKeywords;
|
||||
scssSpec.colorKeywords = colorKeywords;
|
||||
scssSpec.valueKeywords = valueKeywords;
|
||||
win.CodeMirror.defineMIME("text/x-scss", scssSpec);
|
||||
|
||||
win.CodeMirror.commands.save = () => this.emit("saveRequested");
|
||||
|
@ -1279,12 +1290,14 @@ Editor.keyFor = function (cmd, opts = { noaccel: false }) {
|
|||
return opts.noaccel ? key : Editor.accel(key);
|
||||
};
|
||||
|
||||
// Since Gecko already provide complete and up to date list of CSS property
|
||||
// names, values and color names, we compute them so that they can replace
|
||||
// the ones used in CodeMirror while initiating an editor object. This is done
|
||||
// here instead of the file codemirror/css.js so as to leave that file untouched
|
||||
// and easily upgradable.
|
||||
function getCSSKeywords() {
|
||||
/**
|
||||
* We compute the CSS property names, values, and color names to be used with
|
||||
* CodeMirror to more closely reflect what is supported by the target platform.
|
||||
* The database is used to replace the values used in CodeMirror while initiating
|
||||
* an editor object. This is done here instead of the file codemirror/css.js so
|
||||
* as to leave that file untouched and easily upgradable.
|
||||
*/
|
||||
function getCSSKeywords(cssProperties) {
|
||||
function keySet(array) {
|
||||
let keys = {};
|
||||
for (let i = 0; i < array.length; ++i) {
|
||||
|
@ -1293,26 +1306,26 @@ function getCSSKeywords() {
|
|||
return keys;
|
||||
}
|
||||
|
||||
let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
|
||||
.getService(Ci.inIDOMUtils);
|
||||
let properties = domUtils.getCSSPropertyNames(domUtils.INCLUDE_ALIASES);
|
||||
let colors = {};
|
||||
let values = {};
|
||||
properties.forEach(property => {
|
||||
let propertyKeywords = cssProperties.getNames();
|
||||
let colorKeywords = {};
|
||||
let valueKeywords = {};
|
||||
|
||||
propertyKeywords.forEach(property => {
|
||||
if (property.includes("color")) {
|
||||
domUtils.getCSSValuesForProperty(property).forEach(value => {
|
||||
colors[value] = true;
|
||||
cssProperties.getValues(property).forEach(value => {
|
||||
colorKeywords[value] = true;
|
||||
});
|
||||
} else {
|
||||
domUtils.getCSSValuesForProperty(property).forEach(value => {
|
||||
values[value] = true;
|
||||
cssProperties.getValues(property).forEach(value => {
|
||||
valueKeywords[value] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
cssProperties: keySet(properties),
|
||||
cssValues: values,
|
||||
cssColors: colors
|
||||
propertyKeywords: keySet(propertyKeywords),
|
||||
colorKeywords: colorKeywords,
|
||||
valueKeywords: valueKeywords
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,8 @@ function runTests() {
|
|||
target.makeRemote().then(() => {
|
||||
inspector = InspectorFront(target.client, target.form);
|
||||
inspector.getWalker().then(walker => {
|
||||
completer = new CSSCompleter({walker: walker});
|
||||
completer = new CSSCompleter({walker: walker,
|
||||
cssProperties: getClientCssProperties()});
|
||||
checkStateAndMoveOn();
|
||||
});
|
||||
});
|
||||
|
@ -107,13 +108,15 @@ function checkStateAndMoveOn() {
|
|||
return;
|
||||
}
|
||||
|
||||
let test = tests[index];
|
||||
let [lineCh, expectedSuggestions] = tests[index];
|
||||
let [line, ch] = lineCh;
|
||||
|
||||
progress.dataset.progress = ++index;
|
||||
progressDiv.style.width = 100 * index / tests.length + "%";
|
||||
completer.complete(limit(source, test[0]),
|
||||
{line: test[0][0], ch: test[0][1]}).then(suggestions => {
|
||||
checkState(test[1], suggestions);
|
||||
}).then(checkStateAndMoveOn);
|
||||
|
||||
completer.complete(limit(source, lineCh), {line, ch})
|
||||
.then(actualSuggestions => checkState(expectedSuggestions, actualSuggestions))
|
||||
.then(checkStateAndMoveOn);
|
||||
}
|
||||
|
||||
function checkState(expected, actual) {
|
||||
|
|
|
@ -132,7 +132,7 @@ function test() {
|
|||
}
|
||||
|
||||
function runTests() {
|
||||
let completer = new CSSCompleter();
|
||||
let completer = new CSSCompleter({cssProperties: getClientCssProperties()});
|
||||
let matches = (arr, toCheck) => !arr.some((x, i) => x != toCheck[i]);
|
||||
let checkState = (expected, actual) => {
|
||||
if (expected[0] == "null" && actual == null) {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
"use strict";
|
||||
|
||||
const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
|
||||
const { Cc, Ci } = require("chrome");
|
||||
|
||||
const CSS_URI = "http://mochi.test:8888/browser/devtools/client/sourceeditor" +
|
||||
"/test/css_statemachine_testcases.css";
|
||||
|
@ -66,7 +65,7 @@ function test() {
|
|||
}
|
||||
|
||||
function runTests() {
|
||||
let completer = new CSSCompleter();
|
||||
let completer = new CSSCompleter({cssProperties: getClientCssProperties()});
|
||||
let checkState = state => {
|
||||
if (state[0] == "null" && (!completer.state || completer.state == "null")) {
|
||||
return true;
|
||||
|
|
|
@ -25,7 +25,7 @@ function* runTests() {
|
|||
let {ed, win, edWin} = yield setup(null, {
|
||||
autocomplete: true,
|
||||
mode: Editor.modes.css,
|
||||
autocompleteOpts: {walker: walker}
|
||||
autocompleteOpts: {walker: walker, cssProperties: getClientCssProperties()}
|
||||
});
|
||||
yield testMouse(ed, edWin);
|
||||
yield testKeyboard(ed, edWin);
|
||||
|
|
|
@ -8,8 +8,8 @@ const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
|||
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
|
||||
const Editor = require("devtools/client/sourceeditor/editor");
|
||||
const promise = require("promise");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
const flags = require("devtools/shared/flags");
|
||||
const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
|
||||
flags.testing = true;
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
|
@ -62,8 +62,10 @@ function setup(cb, additionalOpts = {}) {
|
|||
value: "Hello.",
|
||||
lineNumbers: true,
|
||||
foldGutter: true,
|
||||
gutters: ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-foldgutter"]
|
||||
gutters: ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-foldgutter"],
|
||||
cssProperties: getClientCssProperties()
|
||||
};
|
||||
|
||||
for (let o in additionalOpts) {
|
||||
opts[o] = additionalOpts[o];
|
||||
}
|
||||
|
|
|
@ -57,13 +57,15 @@ const PREF_NAV_WIDTH = "devtools.styleeditor.navSidebarWidth";
|
|||
* Interface for the page we're debugging
|
||||
* @param {Document} panelDoc
|
||||
* Document of the toolbox panel to populate UI in.
|
||||
* @param {CssProperties} A css properties database.
|
||||
*/
|
||||
function StyleEditorUI(debuggee, target, panelDoc) {
|
||||
function StyleEditorUI(debuggee, target, panelDoc, cssProperties) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this._debuggee = debuggee;
|
||||
this._target = target;
|
||||
this._panelDoc = panelDoc;
|
||||
this._cssProperties = cssProperties;
|
||||
this._window = this._panelDoc.defaultView;
|
||||
this._root = this._panelDoc.getElementById("style-editor-chrome");
|
||||
|
||||
|
@ -603,7 +605,7 @@ StyleEditorUI.prototype = {
|
|||
// only initialize source editor when we switch to this view
|
||||
let inputElement =
|
||||
details.querySelector(".stylesheet-editor-input");
|
||||
yield showEditor.load(inputElement);
|
||||
yield showEditor.load(inputElement, this._cssProperties);
|
||||
}
|
||||
|
||||
showEditor.onShow();
|
||||
|
|
|
@ -417,11 +417,13 @@ StyleSheetEditor.prototype = {
|
|||
* Create source editor and load state into it.
|
||||
* @param {DOMElement} inputElement
|
||||
* Element to load source editor in
|
||||
* @param {CssProperties} cssProperties
|
||||
* A css properties database.
|
||||
*
|
||||
* @return {Promise}
|
||||
* Promise that will resolve when the style editor is loaded.
|
||||
*/
|
||||
load: function (inputElement) {
|
||||
load: function (inputElement, cssProperties) {
|
||||
if (this._isDestroyed) {
|
||||
return promise.reject("Won't load source editor as the style sheet has " +
|
||||
"already been removed from Style Editor.");
|
||||
|
@ -438,7 +440,8 @@ StyleSheetEditor.prototype = {
|
|||
extraKeys: this._getKeyBindings(),
|
||||
contextMenu: "sourceEditorContextMenu",
|
||||
autocomplete: Services.prefs.getBoolPref(AUTOCOMPLETION_PREF),
|
||||
autocompleteOpts: { walker: this.walker }
|
||||
autocompleteOpts: { walker: this.walker, cssProperties },
|
||||
cssProperties
|
||||
};
|
||||
let sourceEditor = this._sourceEditor = new Editor(config);
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ var EventEmitter = require("devtools/shared/event-emitter");
|
|||
|
||||
var {StyleEditorUI} = require("resource://devtools/client/styleeditor/StyleEditorUI.jsm");
|
||||
var {getString} = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
|
||||
var {initCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
|
||||
loader.lazyGetter(this, "StyleSheetsFront",
|
||||
() => require("devtools/shared/fronts/stylesheets").StyleSheetsFront);
|
||||
|
@ -60,8 +61,12 @@ StyleEditorPanel.prototype = {
|
|||
this._debuggee = StyleEditorFront(this.target.client, this.target.form);
|
||||
}
|
||||
|
||||
// Initialize the CSS properties database.
|
||||
const {cssProperties} = yield initCssProperties(this._toolbox);
|
||||
|
||||
// Initialize the UI
|
||||
this.UI = new StyleEditorUI(this._debuggee, this.target, this._panelDoc);
|
||||
this.UI = new StyleEditorUI(this._debuggee, this.target, this._panelDoc,
|
||||
cssProperties);
|
||||
this.UI.on("error", this._showError);
|
||||
yield this.UI.initialize();
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
const TESTCASE_URI = TEST_BASE_HTTP + "autocomplete.html";
|
||||
const MAX_SUGGESTIONS = 15;
|
||||
|
||||
const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
const {CSSProperties, CSSValues} = getCSSKeywords();
|
||||
|
||||
// Test cases to test that autocompletion works correctly when enabled.
|
||||
|
@ -195,12 +196,11 @@ function checkState(index, sourceEditor, popup) {
|
|||
* CSS values the property can have.
|
||||
*/
|
||||
function getCSSKeywords() {
|
||||
let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
|
||||
.getService(Ci.inIDOMUtils);
|
||||
let cssProperties = getClientCssProperties();
|
||||
let props = {};
|
||||
let propNames = domUtils.getCSSPropertyNames(domUtils.INCLUDE_ALIASES);
|
||||
let propNames = cssProperties.getNames();
|
||||
propNames.forEach(prop => {
|
||||
props[prop] = domUtils.getCSSValuesForProperty(prop).sort();
|
||||
props[prop] = cssProperties.getValues(prop).sort();
|
||||
});
|
||||
return {
|
||||
CSSValues: props,
|
||||
|
|
|
@ -109,6 +109,15 @@ CssProperties.prototype = {
|
|||
*/
|
||||
getValues(property) {
|
||||
return this.properties[property] ? this.properties[property].values : [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the CSS property names.
|
||||
*
|
||||
* @return {Array} An array of strings.
|
||||
*/
|
||||
getNames(property) {
|
||||
return Object.keys(this.properties);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -140,7 +149,7 @@ const initCssProperties = Task.async(function* (toolbox) {
|
|||
if (!serverDB.properties && !serverDB.margin) {
|
||||
db = CSS_PROPERTIES_DB;
|
||||
} else {
|
||||
db = normalizeCssData(serverDB);
|
||||
db = serverDB;
|
||||
}
|
||||
} else {
|
||||
// The target does not support this actor, so require a static list of supported
|
||||
|
@ -148,10 +157,7 @@ const initCssProperties = Task.async(function* (toolbox) {
|
|||
db = CSS_PROPERTIES_DB;
|
||||
}
|
||||
|
||||
// Color values are omitted to save on space. Add them back here.
|
||||
reattachCssColorValues(db);
|
||||
|
||||
const cssProperties = new CssProperties(db);
|
||||
const cssProperties = new CssProperties(normalizeCssData(db));
|
||||
cachedCssProperties.set(client, {cssProperties, front});
|
||||
return {cssProperties, front};
|
||||
});
|
||||
|
@ -171,6 +177,16 @@ function getCssProperties(toolbox) {
|
|||
return cachedCssProperties.get(toolbox.target.client).cssProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a client-side CssProperties. This is useful for dependencies in tests, or parts
|
||||
* of the codebase that don't particularly need to match every known CSS property on
|
||||
* the target.
|
||||
* @return {CssProperties}
|
||||
*/
|
||||
function getClientCssProperties() {
|
||||
return new CssProperties(normalizeCssData(CSS_PROPERTIES_DB));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current browser version.
|
||||
* @returns {string} The browser version.
|
||||
|
@ -186,37 +202,42 @@ function getClientBrowserVersion(toolbox) {
|
|||
|
||||
/**
|
||||
* Even if the target has the cssProperties actor, the returned data may not be in the
|
||||
* same shape or have all of the data we need. This normalizes this data.
|
||||
* same shape or have all of the data we need. This normalizes the data and fills in
|
||||
* any missing information like color values.
|
||||
*
|
||||
* @return {Object} The normalized CSS database.
|
||||
*/
|
||||
function normalizeCssData(db) {
|
||||
// Firefox 49's getCSSDatabase() just returned the properties object, but
|
||||
// now it returns an object with multiple types of CSS information.
|
||||
if (!db.properties) {
|
||||
db = { properties: db };
|
||||
}
|
||||
if (db !== CSS_PROPERTIES_DB) {
|
||||
// Firefox 49's getCSSDatabase() just returned the properties object, but
|
||||
// now it returns an object with multiple types of CSS information.
|
||||
if (!db.properties) {
|
||||
db = { properties: db };
|
||||
}
|
||||
|
||||
// Fill in any missing DB information from the static database.
|
||||
db = Object.assign({}, CSS_PROPERTIES_DB, db);
|
||||
// Fill in any missing DB information from the static database.
|
||||
db = Object.assign({}, CSS_PROPERTIES_DB, db);
|
||||
|
||||
// Add "supports" information to the css properties if it's missing.
|
||||
if (!db.properties.color.supports) {
|
||||
for (let name in db.properties) {
|
||||
if (typeof CSS_PROPERTIES_DB.properties[name] === "object") {
|
||||
db.properties[name].supports = CSS_PROPERTIES_DB.properties[name].supports;
|
||||
// Add "supports" information to the css properties if it's missing.
|
||||
if (!db.properties.color.supports) {
|
||||
for (let name in db.properties) {
|
||||
if (typeof CSS_PROPERTIES_DB.properties[name] === "object") {
|
||||
db.properties[name].supports = CSS_PROPERTIES_DB.properties[name].supports;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add "values" information to the css properties if it's missing.
|
||||
if (!db.properties.color.values) {
|
||||
for (let name in db.properties) {
|
||||
if (typeof CSS_PROPERTIES_DB.properties[name] === "object") {
|
||||
db.properties[name].values = CSS_PROPERTIES_DB.properties[name].values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add "values" information to the css properties if it's missing.
|
||||
if (!db.properties.color.values) {
|
||||
for (let name in db.properties) {
|
||||
if (typeof CSS_PROPERTIES_DB.properties[name] === "object") {
|
||||
db.properties[name].values = CSS_PROPERTIES_DB.properties[name].values;
|
||||
}
|
||||
}
|
||||
}
|
||||
reattachCssColorValues(db);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
@ -243,5 +264,6 @@ module.exports = {
|
|||
CssPropertiesFront,
|
||||
CssProperties,
|
||||
getCssProperties,
|
||||
getClientCssProperties,
|
||||
initCssProperties
|
||||
};
|
||||
|
|
|
@ -174,14 +174,16 @@ Animation::SetEffectNoUpdate(AnimationEffectReadOnly* aEffect)
|
|||
prevAnim->SetEffect(nullptr);
|
||||
}
|
||||
|
||||
// Create links with the new effect.
|
||||
// Create links with the new effect. SetAnimation(this) will also update
|
||||
// mIsRelevant of this animation, and then notify mutation observer if
|
||||
// needed by calling Animation::UpdateRelevance(), so we don't need to
|
||||
// call it again.
|
||||
mEffect = newEffect;
|
||||
mEffect->SetAnimation(this);
|
||||
|
||||
// Update relevance and then notify possible add or change.
|
||||
// Notify possible add or change.
|
||||
// If the target is different, the change notification will be ignored by
|
||||
// AutoMutationBatchForAnimation.
|
||||
UpdateRelevance();
|
||||
if (wasRelevant && mIsRelevant) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
}
|
||||
|
|
|
@ -104,13 +104,7 @@ KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
|
|||
// animation cascade for this element whenever that changes.
|
||||
bool inEffect = IsInEffect();
|
||||
if (inEffect != mInEffectOnLastAnimationTimingUpdate) {
|
||||
if (mTarget) {
|
||||
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
|
||||
mTarget->mPseudoType);
|
||||
if (effectSet) {
|
||||
effectSet->MarkCascadeNeedsUpdate();
|
||||
}
|
||||
}
|
||||
MarkCascadeNeedsUpdate();
|
||||
mInEffectOnLastAnimationTimingUpdate = inEffect;
|
||||
}
|
||||
|
||||
|
@ -305,15 +299,9 @@ KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
|
|||
|
||||
CalculateCumulativeChangeHint(aStyleContext);
|
||||
|
||||
if (mTarget) {
|
||||
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
|
||||
mTarget->mPseudoType);
|
||||
if (effectSet) {
|
||||
effectSet->MarkCascadeNeedsUpdate();
|
||||
}
|
||||
MarkCascadeNeedsUpdate();
|
||||
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
}
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -569,14 +557,6 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect(
|
|||
return effect.forget();
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::ResetWinsInCascade()
|
||||
{
|
||||
for (AnimationProperty& property : mProperties) {
|
||||
property.mWinsInCascade = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::UpdateTargetRegistration()
|
||||
{
|
||||
|
@ -1234,18 +1214,17 @@ KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation)
|
|||
|
||||
mAnimation = aAnimation;
|
||||
|
||||
// Restyle for the new animation.
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
|
||||
if (mTarget) {
|
||||
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
|
||||
mTarget->mPseudoType);
|
||||
if (effectSet) {
|
||||
effectSet->MarkCascadeNeedsUpdate();
|
||||
}
|
||||
// The order of these function calls is important:
|
||||
// NotifyAnimationTimingUpdated() need the updated mIsRelevant flag to check
|
||||
// if it should create the effectSet or not, and MarkCascadeNeedsUpdate()
|
||||
// needs a valid effectSet, so we should call them in this order.
|
||||
if (mAnimation) {
|
||||
mAnimation->UpdateRelevance();
|
||||
}
|
||||
|
||||
NotifyAnimationTimingUpdated();
|
||||
if (mAnimation) {
|
||||
MarkCascadeNeedsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1283,6 +1262,21 @@ KeyframeEffectReadOnly::MaybeUpdateFrameForCompositor()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::MarkCascadeNeedsUpdate()
|
||||
{
|
||||
if (!mTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
|
||||
mTarget->mPseudoType);
|
||||
if (!effectSet) {
|
||||
return;
|
||||
}
|
||||
effectSet->MarkCascadeNeedsUpdate();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
//
|
||||
// KeyframeEffect
|
||||
|
@ -1361,7 +1355,9 @@ KeyframeEffect::SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget)
|
|||
if (mTarget) {
|
||||
UnregisterTarget();
|
||||
ResetIsRunningOnCompositor();
|
||||
ResetWinsInCascade();
|
||||
// We don't need to reset the mWinsInCascade member since it will be updated
|
||||
// when we later associate with a different target (and until that time this
|
||||
// flag is not used).
|
||||
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
|
||||
|
|
|
@ -319,8 +319,6 @@ protected:
|
|||
const OptionsType& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void ResetWinsInCascade();
|
||||
|
||||
// This effect is registered with its target element so long as:
|
||||
//
|
||||
// (a) It has a target element, and
|
||||
|
@ -351,6 +349,10 @@ protected:
|
|||
already_AddRefed<nsStyleContext>
|
||||
GetTargetStyleContext();
|
||||
|
||||
// A wrapper for marking cascade update according to the current
|
||||
// target and its effectSet.
|
||||
void MarkCascadeNeedsUpdate();
|
||||
|
||||
Maybe<OwningAnimationTarget> mTarget;
|
||||
|
||||
KeyframeEffectParams mEffectOptions;
|
||||
|
|
|
@ -560,5 +560,199 @@ promise_test(function(t) {
|
|||
}, 'Clearing *important* opacity style on the target element sends the ' +
|
||||
'animation to the compositor');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var firstAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
|
||||
var secondAnimation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
|
||||
|
||||
var another = addDiv(t);
|
||||
|
||||
return Promise.all([firstAnimation.ready, secondAnimation.ready]).then(function() {
|
||||
assert_equals(secondAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'The second opacity animation on an element reports that ' +
|
||||
'it is running on the compositor');
|
||||
assert_equals(firstAnimation.isRunningOnCompositor, false,
|
||||
'The first opacity animation on the same element reports ' +
|
||||
'that it is NOT running on the compositor');
|
||||
|
||||
firstAnimation.effect.target = another;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(secondAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'The second opacity animation on the element keeps ' +
|
||||
'running on the compositor after the preiously overridden ' +
|
||||
'animation is applied to a different element');
|
||||
assert_equals(firstAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'The previously overridden opacity animation reports that ' +
|
||||
'it it running on the compositor after being applied to a ' +
|
||||
'different element');
|
||||
});
|
||||
}, 'Active animation which was not running on the compositor due to ' +
|
||||
'composite order starts running on the compositor after changing ' +
|
||||
'the target element');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var firstAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
|
||||
var secondAnimation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
|
||||
|
||||
var another = addDiv(t);
|
||||
|
||||
return Promise.all([firstAnimation.ready, secondAnimation.ready]).then(function() {
|
||||
assert_equals(secondAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'The second opacity animation on an element reports that ' +
|
||||
'it is running on the compositor');
|
||||
assert_equals(firstAnimation.isRunningOnCompositor, false,
|
||||
'The first opacity animation on the same element reports ' +
|
||||
'that it is NOT running on the compositor');
|
||||
|
||||
secondAnimation.effect.target = another;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(secondAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'The second opacity animation continues to run on the ' +
|
||||
'compositor after being applied to a different element');
|
||||
assert_equals(firstAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'The previously overridden opacity animation now reports ' +
|
||||
'that it is running on the compositor after the animation ' +
|
||||
'that was overridding it is applied to a different element');
|
||||
});
|
||||
}, 'Animation which was overridden and, as a result, not running on the ' +
|
||||
'compositor begins running on the compositor after higher-priority ' +
|
||||
'animation is applied to a different element');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var another = addDiv(t);
|
||||
|
||||
var animation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
|
||||
var anotherAnimation = another.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
|
||||
|
||||
return Promise.all([animation.ready, anotherAnimation.ready]).then(function() {
|
||||
assert_equals(anotherAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'An opacity animation on an element reports that ' +
|
||||
'it is running on the compositor');
|
||||
assert_equals(animation.isRunningOnCompositor, omtaEnabled,
|
||||
'Opacity animation running on a different element reports ' +
|
||||
'that it is running on the compositor');
|
||||
|
||||
anotherAnimation.effect.target = div;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(anotherAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'Animation continues to run on the compositor after ' +
|
||||
'being applied to a different element with a ' +
|
||||
'lower-priority animation');
|
||||
assert_equals(animation.isRunningOnCompositor, false,
|
||||
'Animation stops running on the compositor after ' +
|
||||
'a higher-priority animation originally applied to ' +
|
||||
'a different element is applied to the same element');
|
||||
});
|
||||
}, 'Moving a higher-priority animation to an element which already has the ' +
|
||||
'same property animation running on the compositor makes the initial ' +
|
||||
'animation stop running on the compositor');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var importantOpacityElement = addDiv(t, { style: "opacity: 1 ! important" });
|
||||
|
||||
var animation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
|
||||
|
||||
return animation.ready.then(function() {
|
||||
assert_equals(animation.isRunningOnCompositor, omtaEnabled,
|
||||
'Opacity animation on an element reports ' +
|
||||
'that it is running on the compositor');
|
||||
|
||||
animation.effect.target = null;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(animation.isRunningOnCompositor, false,
|
||||
'Animation is no longer running on the compositor after ' +
|
||||
'removing from the element');
|
||||
animation.effect.target = importantOpacityElement;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(animation.isRunningOnCompositor, false,
|
||||
'Animation is NOT running on the compositor even after ' +
|
||||
'being applied to a different element which has an ' +
|
||||
'!important opacity declaration');
|
||||
});
|
||||
}, 'Animation continues not running on the compositor after being ' +
|
||||
'applied to an element which has an important declaration and ' +
|
||||
'having previously been temporarily associated with no target element');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var another = addDiv(t);
|
||||
|
||||
var lowerAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
|
||||
var higherAnimation = another.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
|
||||
|
||||
return Promise.all([lowerAnimation.ready, higherAnimation.ready]).then(function() {
|
||||
assert_equals(lowerAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'An opacity animation on an element reports that ' +
|
||||
'it is running on the compositor');
|
||||
assert_equals(higherAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'Opacity animation on a different element reports ' +
|
||||
'that it is running on the compositor');
|
||||
|
||||
lowerAnimation.effect.target = null;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(lowerAnimation.isRunningOnCompositor, false,
|
||||
'Animation is no longer running on the compositor after ' +
|
||||
'being removed from the element');
|
||||
lowerAnimation.effect.target = another;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(lowerAnimation.isRunningOnCompositor, false,
|
||||
'A lower-priority animation does NOT begin running ' +
|
||||
'on the compositor after being applied to an element ' +
|
||||
'which has a higher-priority animation');
|
||||
assert_equals(higherAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'A higher-priority animation continues to run on the ' +
|
||||
'compositor even after a lower-priority animation is ' +
|
||||
'applied to the same element');
|
||||
});
|
||||
}, 'Animation continues not running on the compositor after being applied ' +
|
||||
'to an element which has a higher-priority animation and after ' +
|
||||
'being remporarily associated with no target element');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var another = addDiv(t);
|
||||
|
||||
var lowerAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
|
||||
var higherAnimation = another.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
|
||||
|
||||
return Promise.all([lowerAnimation.ready, higherAnimation.ready]).then(function() {
|
||||
assert_equals(lowerAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'An opacity animation on an element reports that ' +
|
||||
'it is running on the compositor');
|
||||
assert_equals(higherAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'Opacity animation on a different element reports ' +
|
||||
'that it is running on the compositor');
|
||||
|
||||
higherAnimation.effect.target = null;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(higherAnimation.isRunningOnCompositor, false,
|
||||
'Animation is no longer running on the compositor after ' +
|
||||
'being removed from the element');
|
||||
higherAnimation.effect.target = div;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(lowerAnimation.isRunningOnCompositor, false,
|
||||
'Animation stops running on the compositor after ' +
|
||||
'a higher-priority animation applied to the same element');
|
||||
assert_equals(higherAnimation.isRunningOnCompositor, omtaEnabled,
|
||||
'A higher-priority animation begins to running on the ' +
|
||||
'compositor after being applied to an element which has ' +
|
||||
'a lower-priority-animation');
|
||||
});
|
||||
}, 'Animation begins running on the compositor after being applied ' +
|
||||
'to an element which has a lower-priority animation once after ' +
|
||||
'disassociating with an element');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -45,10 +45,9 @@ Link::~Link()
|
|||
bool
|
||||
Link::ElementHasHref() const
|
||||
{
|
||||
return ((!mElement->IsSVGElement() &&
|
||||
mElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href))
|
||||
|| (!mElement->IsHTMLElement() &&
|
||||
mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href)));
|
||||
return mElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
|
||||
(!mElement->IsHTMLElement() &&
|
||||
mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -120,10 +120,6 @@
|
|||
#include "mozilla/DetailedPromise.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <cutils/properties.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -1639,157 +1635,6 @@ Navigator::PublishServer(const nsAString& aName,
|
|||
return domPromise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Navigator::GetFeature(const nsAString& aName, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
|
||||
RefPtr<Promise> p = Promise::Create(go, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
if (aName.EqualsLiteral("hardware.memory")) {
|
||||
// with seccomp enabled, fopen() should be in a non-sandboxed process
|
||||
if (XRE_IsParentProcess()) {
|
||||
uint32_t memLevel = mozilla::hal::GetTotalSystemMemoryLevel();
|
||||
if (memLevel == 0) {
|
||||
p->MaybeReject(NS_ERROR_NOT_AVAILABLE);
|
||||
return p.forget();
|
||||
}
|
||||
p->MaybeResolve((int)memLevel);
|
||||
} else {
|
||||
mozilla::dom::ContentChild* cc =
|
||||
mozilla::dom::ContentChild::GetSingleton();
|
||||
RefPtr<Promise> ipcRef(p);
|
||||
cc->SendGetSystemMemory(reinterpret_cast<uint64_t>(ipcRef.forget().take()));
|
||||
}
|
||||
return p.forget();
|
||||
} // hardware.memory
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (StringBeginsWith(aName, NS_LITERAL_STRING("acl.")) &&
|
||||
(aName.EqualsLiteral("acl.version") || CheckPermission("external-app"))) {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
nsCString propertyKey("persist.");
|
||||
propertyKey.Append(NS_ConvertUTF16toUTF8(aName));
|
||||
uint32_t len = property_get(propertyKey.get(), value, nullptr);
|
||||
if (len > 0) {
|
||||
p->MaybeResolve(NS_ConvertUTF8toUTF16(value));
|
||||
return p.forget();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Mirror the dom.apps.developer_mode pref to let apps get it read-only.
|
||||
if (aName.EqualsLiteral("dom.apps.developer_mode")) {
|
||||
p->MaybeResolve(Preferences::GetBool("dom.apps.developer_mode", false));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
p->MaybeResolveWithUndefined();
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Navigator::HasFeature(const nsAString& aName, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
|
||||
RefPtr<Promise> p = Promise::Create(go, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Hardcoded extensions features which are b2g specific.
|
||||
#ifdef MOZ_B2G
|
||||
if (aName.EqualsLiteral("web-extensions") ||
|
||||
aName.EqualsLiteral("late-customization")) {
|
||||
p->MaybeResolve(true);
|
||||
return p.forget();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Hardcoded manifest features. Some are still b2g specific.
|
||||
const char manifestFeatures[][64] = {
|
||||
"manifest.origin"
|
||||
, "manifest.redirects"
|
||||
#ifdef MOZ_B2G
|
||||
, "manifest.chrome.navigation"
|
||||
, "manifest.precompile"
|
||||
, "manifest.role.homescreen"
|
||||
#endif
|
||||
};
|
||||
|
||||
nsAutoCString feature = NS_ConvertUTF16toUTF8(aName);
|
||||
for (uint32_t i = 0; i < MOZ_ARRAY_LENGTH(manifestFeatures); i++) {
|
||||
if (feature.Equals(manifestFeatures[i])) {
|
||||
p->MaybeResolve(true);
|
||||
return p.forget();
|
||||
}
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_STRING(apiWindowPrefix, "api.window.");
|
||||
if (StringBeginsWith(aName, apiWindowPrefix)) {
|
||||
const nsAString& featureName = Substring(aName, apiWindowPrefix.Length());
|
||||
|
||||
// Temporary hardcoded entry points due to technical constraints
|
||||
if (featureName.EqualsLiteral("Navigator.mozTCPSocket")) {
|
||||
p->MaybeResolve(Preferences::GetBool("dom.mozTCPSocket.enabled"));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
if (featureName.EqualsLiteral("Navigator.mozMobileConnections") ||
|
||||
featureName.EqualsLiteral("MozMobileNetworkInfo")) {
|
||||
p->MaybeResolve(Preferences::GetBool("dom.mobileconnection.enabled"));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
if (featureName.EqualsLiteral("Navigator.mozInputMethod")) {
|
||||
p->MaybeResolve(Preferences::GetBool("dom.mozInputMethod.enabled"));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
if (featureName.EqualsLiteral("Navigator.getDeviceStorage")) {
|
||||
p->MaybeResolve(Preferences::GetBool("device.storage.enabled"));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
if (featureName.EqualsLiteral("Navigator.mozNetworkStats")) {
|
||||
p->MaybeResolve(Preferences::GetBool("dom.mozNetworkStats.enabled"));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
if (featureName.EqualsLiteral("Navigator.push")) {
|
||||
p->MaybeResolve(Preferences::GetBool("services.push.enabled"));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
if (featureName.EqualsLiteral("Navigator.mozAlarms")) {
|
||||
p->MaybeResolve(Preferences::GetBool("dom.mozAlarms.enabled"));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
if (featureName.EqualsLiteral("Navigator.mozCameras")) {
|
||||
p->MaybeResolve(true);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
if (featureName.EqualsLiteral("XMLHttpRequest.mozSystem")) {
|
||||
p->MaybeResolve(true);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
p->MaybeResolveWithUndefined();
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
// resolve with <undefined> because the feature name is not supported
|
||||
p->MaybeResolveWithUndefined();
|
||||
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
PowerManager*
|
||||
Navigator::GetMozPower(ErrorResult& aRv)
|
||||
{
|
||||
|
|
|
@ -185,13 +185,6 @@ public:
|
|||
// NavigatorBinding::ClearCachedUserAgentValue(this);
|
||||
void ClearUserAgentCache();
|
||||
|
||||
// Feature Detection API
|
||||
already_AddRefed<Promise> GetFeature(const nsAString& aName,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> HasFeature(const nsAString &aName,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool Vibrate(uint32_t aDuration);
|
||||
bool Vibrate(const nsTArray<uint32_t>& aDuration);
|
||||
void SetVibrationPermission(bool aPermitted, bool aPersistent);
|
||||
|
|
|
@ -593,6 +593,7 @@ nsIAtom** const kAttributesSVG[] = {
|
|||
};
|
||||
|
||||
nsIAtom** const kURLAttributesSVG[] = {
|
||||
&nsGkAtoms::href,
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,4 @@ skip-if = buildapp == 'mulet'
|
|||
[test_bug1008126.html]
|
||||
[test_sandboxed_blob_uri.html]
|
||||
[test_websocket_frame.html]
|
||||
[test_getFeature_with_perm.html]
|
||||
[test_hasFeature.html]
|
||||
[test_mozbrowser_apis_allowed.html]
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=979109
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 979109</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=983502">Mozilla Bug 983502</a>
|
||||
<script type="application/javascript">
|
||||
|
||||
function testSupported() {
|
||||
var mem;
|
||||
navigator.getFeature("hardware.memory").then(function(mem) {
|
||||
|
||||
var isLinux = (navigator.platform.indexOf('Linux') != -1);
|
||||
var isAndroid = !!navigator.userAgent.includes("Android");
|
||||
var isB2G = !isAndroid && /Mobile|Tablet/.test(navigator.userAgent);
|
||||
|
||||
if (isLinux) {
|
||||
info("It is Linux version:");
|
||||
}
|
||||
if (isAndroid) {
|
||||
info("It is Android version");
|
||||
}
|
||||
if (isB2G) {
|
||||
info("It is B2G version");
|
||||
}
|
||||
|
||||
if (isLinux || isAndroid || isB2G) {
|
||||
ok(typeof mem === 'number' && (mem) % 1 === 0, "We should receive an integer on this platform");
|
||||
ok(mem > 0, "hardware.memory is supported on this platform. mem=" + mem + "MiB");
|
||||
} else {
|
||||
ok(typeof mem === 'undefined', "hardware.memory is not support on this platform");
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
||||
},function(mem) {
|
||||
ok(false, "The Promise should not be rejected");
|
||||
});
|
||||
}
|
||||
|
||||
function testNotSupported() {
|
||||
var tv;
|
||||
navigator.getFeature("hardware.tv").then(function(tv) {
|
||||
ok(typeof tv === 'undefined', "Resolve the Promise with undefined value (hardware.tv)");
|
||||
runNextTest();
|
||||
},function(tv) {
|
||||
ok(false, "The Promise should not be rejected");
|
||||
});
|
||||
}
|
||||
|
||||
function testNotSupportedManifest() {
|
||||
navigator.getFeature("manifest.origin").then(function(feature) {
|
||||
ok(typeof feature == 'undefined', "manifest.* resolves with undefined on getFeature");
|
||||
runNextTest();
|
||||
}, function() {
|
||||
ok(false, "The Promise should not be rejected");
|
||||
});
|
||||
}
|
||||
|
||||
function createManifestTest(aFeature) {
|
||||
return function() {
|
||||
var res;
|
||||
navigator.hasFeature(aFeature).then(function(res) {
|
||||
ok(res === true, "Resolve the Promise with 'true' for " + aFeature);
|
||||
runNextTest();
|
||||
}, function(tv) {
|
||||
ok(false, "The Promise should not be rejected");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function testDevMode(aExpected) {
|
||||
return function() {
|
||||
navigator.getFeature("dom.apps.developer_mode").then(res => {
|
||||
is(res, aExpected, "dom.apps.developer_mode is " + aExpected);
|
||||
runNextTest();
|
||||
}, function() {
|
||||
ok(false, "The Promise should not be rejected");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function enableDevMode() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.apps.developer_mode", true]]}, runNextTest);
|
||||
}
|
||||
|
||||
var currentTest = -1;
|
||||
var tests = [
|
||||
testNotSupported,
|
||||
testNotSupportedManifest,
|
||||
testSupported,
|
||||
createManifestTest("manifest.origin"),
|
||||
createManifestTest("manifest.redirects"),
|
||||
testDevMode(false),
|
||||
enableDevMode,
|
||||
testDevMode(true)
|
||||
];
|
||||
|
||||
function runNextTest() {
|
||||
currentTest++;
|
||||
if (currentTest < tests.length) {
|
||||
tests[currentTest]();
|
||||
} else {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
info("About to run " + tests.length + " tests");
|
||||
|
||||
ok('getFeature' in navigator, "navigator.getFeature should exist");
|
||||
ok('hasFeature' in navigator, "navigator.hasFeature should exist");
|
||||
// B2G specific manifest features.
|
||||
// Touching navigator before pushPermissions makes it fail.
|
||||
if (!navigator.userAgent.includes("Android") &&
|
||||
/Mobile|Tablet/.test(navigator.userAgent)) {
|
||||
info("Adding B2G specific tests");
|
||||
tests.push(createManifestTest("manifest.chrome.navigation"));
|
||||
tests.push(createManifestTest("manifest.precompile"));
|
||||
tests.push(createManifestTest("manifest.role.homescreen"));
|
||||
}
|
||||
runNextTest();
|
||||
ok(true, "Test DONE");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,101 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1009645
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1009645</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1009645">Mozilla Bug 1009645</a>
|
||||
<script type="application/javascript">
|
||||
|
||||
var b2gOnly;
|
||||
|
||||
function pref(name) {
|
||||
try {
|
||||
return SpecialPowers.getBoolPref(name);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function testAPIs() {
|
||||
var APIEndPoints = [
|
||||
{ name: "MozMobileNetworkInfo", enabled: pref("dom.mobileconnection.enabled") },
|
||||
// { name: "Navigator.mozBluetooth", enabled: b2gOnly }, // conditional on MOZ_B2G_BT, tricky to test
|
||||
// Bug 1266035 { name: "Navigator.mozContacts", enabled: pref("dom.mozContacts.enabled") },
|
||||
{ name: "Navigator.getDeviceStorage", enabled: pref("device.storage.enabled") },
|
||||
// Bug 1266035 { name: "Navigator.addIdleObserver", enabled: true },
|
||||
{ name: "Navigator.mozNetworkStats", enabled: pref("dom.mozNetworkStats.enabled") },
|
||||
{ name: "Navigator.push", enabled: pref("services.push.enabled") },
|
||||
// { name: "Navigator.mozTime", enabled: b2gOnly }, // conditional on MOZ_TIME_MANAGER, tricky to test
|
||||
// { name: "Navigator.mozFMRadio", enabled: b2gOnly }, // conditional on MOZ_B2G_FM, tricky to test
|
||||
{ name: "Navigator.mozCameras", enabled: true },
|
||||
{ name: "Navigator.mozAlarms", enabled: pref("dom.mozAlarms.enabled") },
|
||||
{ name: "Navigator.mozTCPSocket", enabled: pref("dom.mozTCPSocket.enabled") },
|
||||
{ name: "Navigator.mozInputMethod", enabled: pref("dom.mozInputMethod.enabled") },
|
||||
{ name: "Navigator.mozMobileConnections", enabled: pref("dom.mobileconnection.enabled") },
|
||||
{ name: "XMLHttpRequest.mozSystem", enabled: true }
|
||||
];
|
||||
|
||||
var promises = [];
|
||||
APIEndPoints.forEach(function(v) {
|
||||
promises.push(navigator.hasFeature("api.window." + v.name));
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(function(values) {
|
||||
for (var i = 0; i < values.length; ++i) {
|
||||
is(values[i], APIEndPoints[i].enabled,
|
||||
"Endpoint " + APIEndPoints[i].name + " resolved with the correct value. " +
|
||||
"If this is failing because you're changing how an API is exposed, you " +
|
||||
"must contact the Marketplace team to let them know about the change.");
|
||||
}
|
||||
}, function() {
|
||||
ok(false, "The Promise should not be rejected");
|
||||
});
|
||||
}
|
||||
|
||||
function testExtensions() {
|
||||
if (!b2gOnly) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var builtInFeatures = [
|
||||
{feature: "web-extensions", value: true},
|
||||
{feature: "late-customization", value: true}
|
||||
];
|
||||
|
||||
builtInFeatures.forEach(function(x) {
|
||||
navigator.hasFeature(x.feature).then(function(value) {
|
||||
is(value, x.value, "Resolve the Promise with " + value + " for feature: " + x.feature);
|
||||
}).catch(function(ex) {
|
||||
ok(false, "The Promise should not be rejected");
|
||||
});
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: "feature-detection", allow: true, context: document}
|
||||
], function() {
|
||||
b2gOnly = (function() {
|
||||
var isAndroid = !!navigator.userAgent.includes("Android");
|
||||
var isMulet = pref("b2g.is_mulet");
|
||||
var isB2g = isMulet || (!isAndroid && /Mobile|Tablet/.test(navigator.userAgent));
|
||||
return isB2g ? true : undefined;
|
||||
})();
|
||||
|
||||
ok('hasFeature' in navigator, "navigator.hasFeature should exist");
|
||||
testAPIs().then(testExtensions).catch(function(e) {
|
||||
ok(false, "The Promise should not be rejected: " + e);
|
||||
}).then(SimpleTest.finish);
|
||||
});
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -789,39 +789,25 @@ BrowserElementParent.prototype = {
|
|||
radiisX, radiisY, rotationAngles, forces,
|
||||
count, modifiers) {
|
||||
|
||||
let tabParent = this._frameLoader.tabParent;
|
||||
if (tabParent && tabParent.useAsyncPanZoom) {
|
||||
tabParent.injectTouchEvent(type,
|
||||
identifiers,
|
||||
touchesX,
|
||||
touchesY,
|
||||
radiisX,
|
||||
radiisY,
|
||||
rotationAngles,
|
||||
forces,
|
||||
count,
|
||||
modifiers);
|
||||
} else {
|
||||
let offset = this.getChildProcessOffset();
|
||||
for (var i = 0; i < touchesX.length; i++) {
|
||||
touchesX[i] += offset.x;
|
||||
}
|
||||
for (var i = 0; i < touchesY.length; i++) {
|
||||
touchesY[i] += offset.y;
|
||||
}
|
||||
this._sendAsyncMsg("send-touch-event", {
|
||||
"type": type,
|
||||
"identifiers": identifiers,
|
||||
"touchesX": touchesX,
|
||||
"touchesY": touchesY,
|
||||
"radiisX": radiisX,
|
||||
"radiisY": radiisY,
|
||||
"rotationAngles": rotationAngles,
|
||||
"forces": forces,
|
||||
"count": count,
|
||||
"modifiers": modifiers
|
||||
});
|
||||
let offset = this.getChildProcessOffset();
|
||||
for (var i = 0; i < touchesX.length; i++) {
|
||||
touchesX[i] += offset.x;
|
||||
}
|
||||
for (var i = 0; i < touchesY.length; i++) {
|
||||
touchesY[i] += offset.y;
|
||||
}
|
||||
this._sendAsyncMsg("send-touch-event", {
|
||||
"type": type,
|
||||
"identifiers": identifiers,
|
||||
"touchesX": touchesX,
|
||||
"touchesY": touchesY,
|
||||
"radiisX": radiisX,
|
||||
"radiisY": radiisY,
|
||||
"rotationAngles": rotationAngles,
|
||||
"forces": forces,
|
||||
"count": count,
|
||||
"modifiers": modifiers
|
||||
});
|
||||
}),
|
||||
|
||||
getCanGoBack: defineDOMRequestMethod('get-can-go-back'),
|
||||
|
|
|
@ -77,17 +77,6 @@ void main() {
|
|||
color = (a == 2.0) ? vec4(0, 1.0, 0, 1.0) : vec4(1.0, 0, 0, 1.0);
|
||||
}
|
||||
</script>
|
||||
<script id="fshader-sequence-operator" type="x-shader/x-fragment">#version 300 es
|
||||
precision mediump float;
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
float a[2] = float[2](1.0, 0.0);
|
||||
float b[2] = float[2](2.0, 3.0);
|
||||
float c = (a, b)[0];
|
||||
color = (c == 2.0) ? vec4(0, 1.0, 0, 1.0) : vec4(1.0, 0, 0, 1.0);
|
||||
}
|
||||
</script>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
description("Indexing complex array expressions");
|
||||
|
@ -112,12 +101,6 @@ GLSLConformanceTester.runRenderTests([
|
|||
linkSuccess: true,
|
||||
passMsg: 'Test indexing an array initialization: (float[3](2.0, 1.0, 0.0))[0]'
|
||||
},
|
||||
{
|
||||
fShaderId: 'fshader-sequence-operator',
|
||||
fShaderSuccess: true,
|
||||
linkSuccess: true,
|
||||
passMsg: 'Test indexing a sequence operator: (a, b)[0]'
|
||||
},
|
||||
], 2);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -23,7 +23,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=456273
|
|||
/** Test for Bug 456273 **/
|
||||
|
||||
function doTest() {
|
||||
var ev = document.createEvent('KeyEvents');
|
||||
var ev = document.createEvent('KeyboardEvent');
|
||||
ev.initKeyEvent("keypress", true, true, null, true, false,
|
||||
false, false, 0, "z".charCodeAt(0));
|
||||
SpecialPowers.dispatchEvent(window, document.getElementById('edit456273'), ev);
|
||||
|
|
|
@ -39,7 +39,7 @@ function fireDrop(element, shouldAllowDrop, shouldAllowOnlyChromeDrop) {
|
|||
|
||||
ds.startDragSession();
|
||||
|
||||
var event = document.createEvent("DragEvents");
|
||||
var event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("dragover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
|
||||
fireEvent(element, event);
|
||||
|
||||
|
@ -47,7 +47,7 @@ function fireDrop(element, shouldAllowDrop, shouldAllowOnlyChromeDrop) {
|
|||
is(ds.getCurrentSession().onlyChromeDrop, shouldAllowOnlyChromeDrop,
|
||||
"Unexpected .onlyChromeDrop");
|
||||
|
||||
event = document.createEvent("DragEvents");
|
||||
event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("drop", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
|
||||
fireEvent(element, event);
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ function RunTest() {
|
|||
window.removeEventListener("dragstart", trapDrag, true);
|
||||
synthesizeMouse(image, 20, 20, { type: "mouseup" });
|
||||
|
||||
var event = document.createEvent("DragEvents");
|
||||
var event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("dragover", true, true, iBox.ownerDocument.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, iBox, dataTransfer);
|
||||
fireEvent(iBox, event);
|
||||
synthesizeMouse(image, 3, 3, { type: "mousedown" });
|
||||
|
|
|
@ -42,22 +42,22 @@ function testInitializingUntrustedEvent()
|
|||
ctrlKey: false, altKey: false, shiftKey: true, metaKey: false,
|
||||
keyCode: 0x30, charCode: 0x40 },
|
||||
|
||||
{ createEventArg: "KeyEvents",
|
||||
{ createEventArg: "KeyBoardEvent",
|
||||
type: "foo", bubbles: true, cancelable: true, view: null,
|
||||
ctrlKey: false, altKey: false, shiftKey: false, metaKey: true,
|
||||
keyCode: 0x00, charCode: 0x50 },
|
||||
|
||||
{ createEventArg: "keyevents",
|
||||
{ createEventArg: "keyboardevEnt",
|
||||
type: "bar", bubbles: false, cancelable: true, view: window,
|
||||
ctrlKey: true, altKey: true, shiftKey: false, metaKey: false,
|
||||
keyCode: 0x00, charCode: 0x60 },
|
||||
|
||||
{ createEventArg: "Keyevents",
|
||||
{ createEventArg: "KeyboaRdevent",
|
||||
type: "keydown", bubbles: true, cancelable: false, view: null,
|
||||
ctrlKey: false, altKey: true, shiftKey: false, metaKey: true,
|
||||
keyCode: 0x30, charCode: 0x00 },
|
||||
|
||||
{ createEventArg: "keyEvents",
|
||||
{ createEventArg: "KEYBOARDEVENT",
|
||||
type: "keyup", bubbles: false, cancelable: false, view: window,
|
||||
ctrlKey: true, altKey: false, shiftKey: true, metaKey: false,
|
||||
keyCode: 0x10, charCode: 0x80 },
|
||||
|
|
|
@ -57,8 +57,8 @@ function afterDragTests()
|
|||
false, true, false, false, 0, null, null);
|
||||
$("synthetic").dispatchEvent(evt);
|
||||
|
||||
var evt = document.createEvent("dragevents");
|
||||
ok(evt instanceof DragEvent, "synthetic dragevents class")
|
||||
var evt = document.createEvent("dragevent");
|
||||
ok(evt instanceof DragEvent, "synthetic dragevent class")
|
||||
evt.initDragEvent("dragover", true, true, window, 0, 40, 35, 20, 15,
|
||||
true, false, true, true, 2, document.documentElement, null);
|
||||
$("synthetic2").dispatchEvent(evt);
|
||||
|
|
|
@ -171,6 +171,16 @@ FlyWebPublishedServerImpl::FlyWebPublishedServerImpl(nsPIDOMWindowInner* aOwner,
|
|||
, mHttpServer(new HttpServer())
|
||||
{
|
||||
LOG_I("FlyWebPublishedServerImpl::FlyWebPublishedServerImpl(%p)", this);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebPublishedServerImpl::PermissionGranted(bool aGranted)
|
||||
{
|
||||
LOG_I("FlyWebPublishedServerImpl::PermissionGranted(%b)", aGranted);
|
||||
if (!aGranted) {
|
||||
PublishedServerStarted(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
mHttpServer->Init(-1, Preferences::GetBool("flyweb.use-tls", false), this);
|
||||
}
|
||||
|
@ -252,25 +262,37 @@ FlyWebPublishedServerChild::FlyWebPublishedServerChild(nsPIDOMWindowInner* aOwne
|
|||
const nsAString& aName,
|
||||
const FlyWebPublishOptions& aOptions)
|
||||
: FlyWebPublishedServer(aOwner, aName, aOptions)
|
||||
, mActorDestroyed(false)
|
||||
, mActorExists(false)
|
||||
{
|
||||
LOG_I("FlyWebPublishedServerChild::FlyWebPublishedServerChild(%p)", this);
|
||||
|
||||
ContentChild::GetSingleton()->
|
||||
SendPFlyWebPublishedServerConstructor(this,
|
||||
PromiseFlatString(aName),
|
||||
aOptions);
|
||||
|
||||
// The matching release happens when the actor is destroyed, in
|
||||
// ContentChild::DeallocPFlyWebPublishedServerChild
|
||||
NS_ADDREF_THIS();
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebPublishedServerChild::PermissionGranted(bool aGranted)
|
||||
{
|
||||
if (!aGranted) {
|
||||
PublishedServerStarted(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
mActorExists = true;
|
||||
FlyWebPublishOptions options;
|
||||
options.mUiUrl = mUiUrl;
|
||||
|
||||
// Proceed with initialization.
|
||||
ContentChild::GetSingleton()->
|
||||
SendPFlyWebPublishedServerConstructor(this, mName, options);
|
||||
}
|
||||
|
||||
bool
|
||||
FlyWebPublishedServerChild::RecvServerReady(const nsresult& aStatus)
|
||||
{
|
||||
LOG_I("FlyWebPublishedServerChild::RecvServerReady(%p)", this);
|
||||
MOZ_ASSERT(!mActorDestroyed);
|
||||
MOZ_ASSERT(mActorExists);
|
||||
|
||||
PublishedServerStarted(aStatus);
|
||||
return true;
|
||||
|
@ -280,7 +302,7 @@ bool
|
|||
FlyWebPublishedServerChild::RecvServerClose()
|
||||
{
|
||||
LOG_I("FlyWebPublishedServerChild::RecvServerClose(%p)", this);
|
||||
MOZ_ASSERT(!mActorDestroyed);
|
||||
MOZ_ASSERT(mActorExists);
|
||||
|
||||
Close();
|
||||
|
||||
|
@ -292,7 +314,7 @@ FlyWebPublishedServerChild::RecvFetchRequest(const IPCInternalRequest& aRequest,
|
|||
const uint64_t& aRequestId)
|
||||
{
|
||||
LOG_I("FlyWebPublishedServerChild::RecvFetchRequest(%p)", this);
|
||||
MOZ_ASSERT(!mActorDestroyed);
|
||||
MOZ_ASSERT(mActorExists);
|
||||
|
||||
RefPtr<InternalRequest> request = new InternalRequest(aRequest);
|
||||
mPendingRequests.Put(request, aRequestId);
|
||||
|
@ -307,7 +329,7 @@ FlyWebPublishedServerChild::RecvWebSocketRequest(const IPCInternalRequest& aRequ
|
|||
PTransportProviderChild* aProvider)
|
||||
{
|
||||
LOG_I("FlyWebPublishedServerChild::RecvWebSocketRequest(%p)", this);
|
||||
MOZ_ASSERT(!mActorDestroyed);
|
||||
MOZ_ASSERT(mActorExists);
|
||||
|
||||
RefPtr<InternalRequest> request = new InternalRequest(aRequest);
|
||||
mPendingRequests.Put(request, aRequestId);
|
||||
|
@ -327,7 +349,7 @@ FlyWebPublishedServerChild::ActorDestroy(ActorDestroyReason aWhy)
|
|||
{
|
||||
LOG_I("FlyWebPublishedServerChild::ActorDestroy(%p)", this);
|
||||
|
||||
mActorDestroyed = true;
|
||||
mActorExists = false;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -336,7 +358,7 @@ FlyWebPublishedServerChild::OnFetchResponse(InternalRequest* aRequest,
|
|||
{
|
||||
LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p)", this);
|
||||
|
||||
if (mActorDestroyed) {
|
||||
if (!mActorExists) {
|
||||
LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p) - No actor!", this);
|
||||
return;
|
||||
}
|
||||
|
@ -361,7 +383,7 @@ FlyWebPublishedServerChild::OnWebSocketAcceptInternal(InternalRequest* aRequest,
|
|||
{
|
||||
LOG_I("FlyWebPublishedServerChild::OnWebSocketAcceptInternal(%p)", this);
|
||||
|
||||
if (mActorDestroyed) {
|
||||
if (!mActorExists) {
|
||||
LOG_I("FlyWebPublishedServerChild::OnWebSocketAcceptInternal(%p) - No actor!", this);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -400,7 +422,7 @@ FlyWebPublishedServerChild::OnWebSocketResponse(InternalRequest* aRequest,
|
|||
{
|
||||
LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p)", this);
|
||||
|
||||
if (mActorDestroyed) {
|
||||
if (!mActorExists) {
|
||||
LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p) - No actor!", this);
|
||||
return;
|
||||
}
|
||||
|
@ -428,7 +450,7 @@ FlyWebPublishedServerChild::Close()
|
|||
|
||||
FlyWebPublishedServer::Close();
|
||||
|
||||
if (!mActorDestroyed) {
|
||||
if (mActorExists) {
|
||||
LOG_I("FlyWebPublishedServerChild::Close - sending __delete__ (%p)", this);
|
||||
|
||||
Send__delete__(this);
|
||||
|
|
|
@ -57,6 +57,8 @@ public:
|
|||
aUiUrl = mUiUrl;
|
||||
}
|
||||
|
||||
virtual void PermissionGranted(bool aGranted) = 0;
|
||||
|
||||
virtual void OnFetchResponse(InternalRequest* aRequest,
|
||||
InternalResponse* aResponse) = 0;
|
||||
already_AddRefed<WebSocket>
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
virtual void PermissionGranted(bool aGranted) override;
|
||||
virtual void OnFetchResponse(InternalRequest* aRequest,
|
||||
InternalResponse* aResponse) override;
|
||||
virtual void OnWebSocketResponse(InternalRequest* aConnectRequest,
|
||||
|
@ -98,6 +99,7 @@ public:
|
|||
const nsAString& aName,
|
||||
const FlyWebPublishOptions& aOptions);
|
||||
|
||||
virtual void PermissionGranted(bool aGranted) override;
|
||||
virtual bool RecvServerReady(const nsresult& aStatus) override;
|
||||
virtual bool RecvServerClose() override;
|
||||
virtual bool RecvFetchRequest(const IPCInternalRequest& aRequest,
|
||||
|
@ -125,7 +127,7 @@ private:
|
|||
nsDataHashtable<nsRefPtrHashKey<InternalRequest>, uint64_t> mPendingRequests;
|
||||
nsRefPtrHashtable<nsUint64HashKey, TransportProviderChild>
|
||||
mPendingTransportProviders;
|
||||
bool mActorDestroyed;
|
||||
bool mActorExists;
|
||||
};
|
||||
|
||||
class FlyWebPublishedServerParent final : public PFlyWebPublishedServerParent
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/FlyWebPublishedServerIPC.h"
|
||||
#include "mozilla/AddonPathService.h"
|
||||
#include "nsISocketTransportService.h"
|
||||
#include "mdns/libmdns/nsDNSServiceInfo.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
|
@ -18,6 +19,7 @@
|
|||
#include "mozilla/dom/FlyWebDiscoveryManagerBinding.h"
|
||||
#include "prnetdb.h"
|
||||
#include "DNS.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "nsSocketTransportService2.h"
|
||||
#include "nsSocketTransport2.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
|
@ -34,11 +36,123 @@ struct FlyWebPublishOptions;
|
|||
static LazyLogModule gFlyWebServiceLog("FlyWebService");
|
||||
#undef LOG_I
|
||||
#define LOG_I(...) MOZ_LOG(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
#undef LOG_E
|
||||
#define LOG_E(...) MOZ_LOG(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Error, (__VA_ARGS__))
|
||||
|
||||
#undef LOG_TEST_I
|
||||
#define LOG_TEST_I(...) MOZ_LOG_TEST(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Debug)
|
||||
|
||||
class FlyWebPublishServerPermissionCheck final
|
||||
: public nsIContentPermissionRequest
|
||||
, public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
FlyWebPublishServerPermissionCheck(const nsCString& aServiceName, uint64_t aWindowID,
|
||||
FlyWebPublishedServer* aServer)
|
||||
: mServiceName(aServiceName)
|
||||
, mWindowID(aWindowID)
|
||||
, mServer(aServer)
|
||||
{}
|
||||
|
||||
uint64_t WindowID() const
|
||||
{
|
||||
return mWindowID;
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsGlobalWindow* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
|
||||
if (!globalWindow) {
|
||||
return Cancel();
|
||||
}
|
||||
mWindow = globalWindow->AsInner();
|
||||
if (NS_WARN_IF(!mWindow)) {
|
||||
return Cancel();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return Cancel();
|
||||
}
|
||||
|
||||
mPrincipal = doc->NodePrincipal();
|
||||
MOZ_ASSERT(mPrincipal);
|
||||
|
||||
mRequester = new nsContentPermissionRequester(mWindow);
|
||||
return nsContentPermissionUtils::AskPermission(this, mWindow);
|
||||
}
|
||||
|
||||
NS_IMETHOD Cancel() override
|
||||
{
|
||||
Resolve(false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Allow(JS::HandleValue aChoices) override
|
||||
{
|
||||
MOZ_ASSERT(aChoices.isUndefined());
|
||||
Resolve(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetTypes(nsIArray** aTypes) override
|
||||
{
|
||||
nsTArray<nsString> emptyOptions;
|
||||
return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("flyweb-publish-server"),
|
||||
NS_LITERAL_CSTRING("unused"), emptyOptions, aTypes);
|
||||
}
|
||||
|
||||
NS_IMETHOD GetRequester(nsIContentPermissionRequester** aRequester) override
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRequester);
|
||||
nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
|
||||
requester.forget(aRequester);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetPrincipal(nsIPrincipal** aRequestingPrincipal) override
|
||||
{
|
||||
NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetWindow(mozIDOMWindow** aRequestingWindow) override
|
||||
{
|
||||
NS_IF_ADDREF(*aRequestingWindow = mWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetElement(nsIDOMElement** aRequestingElement) override
|
||||
{
|
||||
*aRequestingElement = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
void Resolve(bool aResolve)
|
||||
{
|
||||
mServer->PermissionGranted(aResolve);
|
||||
}
|
||||
|
||||
virtual ~FlyWebPublishServerPermissionCheck() = default;
|
||||
|
||||
nsCString mServiceName;
|
||||
uint64_t mWindowID;
|
||||
RefPtr<FlyWebPublishedServer> mServer;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsIContentPermissionRequester> mRequester;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(FlyWebPublishServerPermissionCheck,
|
||||
nsIContentPermissionRequest,
|
||||
nsIRunnable)
|
||||
|
||||
class FlyWebMDNSService final
|
||||
: public nsIDNSServiceDiscoveryListener
|
||||
, public nsIDNSServiceResolveListener
|
||||
|
@ -841,6 +955,15 @@ FlyWebService::Init()
|
|||
return ErrorResult(NS_OK);
|
||||
}
|
||||
|
||||
static already_AddRefed<FlyWebPublishPromise>
|
||||
MakeRejectionPromise(const char* name)
|
||||
{
|
||||
MozPromiseHolder<FlyWebPublishPromise> holder;
|
||||
RefPtr<FlyWebPublishPromise> promise = holder.Ensure(name);
|
||||
holder.Reject(NS_ERROR_FAILURE, name);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<FlyWebPublishPromise>
|
||||
FlyWebService::PublishServer(const nsAString& aName,
|
||||
const FlyWebPublishOptions& aOptions,
|
||||
|
@ -853,10 +976,7 @@ FlyWebService::PublishServer(const nsAString& aName,
|
|||
if (existingServer) {
|
||||
LOG_I("PublishServer: Trying to publish server with already-existing name %s.",
|
||||
NS_ConvertUTF16toUTF8(aName).get());
|
||||
MozPromiseHolder<FlyWebPublishPromise> holder;
|
||||
RefPtr<FlyWebPublishPromise> promise = holder.Ensure(__func__);
|
||||
holder.Reject(NS_ERROR_FAILURE, __func__);
|
||||
return promise.forget();
|
||||
return MakeRejectionPromise(__func__);
|
||||
}
|
||||
|
||||
RefPtr<FlyWebPublishedServer> server;
|
||||
|
@ -864,6 +984,49 @@ FlyWebService::PublishServer(const nsAString& aName,
|
|||
server = new FlyWebPublishedServerChild(aWindow, aName, aOptions);
|
||||
} else {
|
||||
server = new FlyWebPublishedServerImpl(aWindow, aName, aOptions);
|
||||
|
||||
// Before proceeding, ensure that the FlyWeb system addon exists.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("chrome://flyweb/skin/icon-64.png"));
|
||||
if (NS_FAILED(rv)) {
|
||||
return MakeRejectionPromise(__func__);
|
||||
}
|
||||
|
||||
JSAddonId *addonId = MapURIToAddonID(uri);
|
||||
if (!addonId) {
|
||||
LOG_E("PublishServer: Failed to find FlyWeb system addon.");
|
||||
return MakeRejectionPromise(__func__);
|
||||
}
|
||||
|
||||
JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(addonId));
|
||||
nsAutoString addonIdString;
|
||||
AssignJSFlatString(addonIdString, flat);
|
||||
if (!addonIdString.EqualsLiteral("flyweb@mozilla.org")) {
|
||||
nsCString addonIdCString = NS_ConvertUTF16toUTF8(addonIdString);
|
||||
LOG_E("PublishServer: FlyWeb resource found on wrong system addon: %s.", addonIdCString.get());
|
||||
return MakeRejectionPromise(__func__);
|
||||
}
|
||||
}
|
||||
|
||||
if (aWindow) {
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
rv = NS_DispatchToCurrentThread(
|
||||
MakeAndAddRef<FlyWebPublishServerPermissionCheck>(
|
||||
NS_ConvertUTF16toUTF8(aName), aWindow->WindowID(), server));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
LOG_E("PublishServer: Failed to dispatch permission check runnable for %s",
|
||||
NS_ConvertUTF16toUTF8(aName).get());
|
||||
return MakeRejectionPromise(__func__);
|
||||
}
|
||||
} else {
|
||||
// If aWindow is null, we're definitely in the e10s parent process.
|
||||
// In this case, we know that permission has already been granted
|
||||
// by the user because of content-process prompt.
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
server->PermissionGranted(true);
|
||||
}
|
||||
|
||||
mServers.AppendElement(server);
|
||||
|
|
|
@ -1537,7 +1537,7 @@ HTMLFormElement::NamedGetter(const nsAString& aName, bool &aFound)
|
|||
void
|
||||
HTMLFormElement::GetSupportedNames(nsTArray<nsString >& aRetval)
|
||||
{
|
||||
// TODO https://www.w3.org/Bugs/Public/show_bug.cgi?id=22320
|
||||
// TODO https://github.com/whatwg/html/issues/1731
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
|
|
|
@ -3289,7 +3289,7 @@ HTMLInputElement::GetRadioGroupContainer() const
|
|||
}
|
||||
|
||||
already_AddRefed<nsIDOMHTMLInputElement>
|
||||
HTMLInputElement::GetSelectedRadioButton()
|
||||
HTMLInputElement::GetSelectedRadioButton() const
|
||||
{
|
||||
nsIRadioGroupContainer* container = GetRadioGroupContainer();
|
||||
if (!container) {
|
||||
|
@ -3360,6 +3360,13 @@ HTMLInputElement::SetCheckedInternal(bool aChecked, bool aNotify)
|
|||
// Notify the document that the CSS :checked pseudoclass for this element
|
||||
// has changed state.
|
||||
UpdateState(aNotify);
|
||||
|
||||
// Notify all radios in the group that value has changed, this is to let
|
||||
// radios to have the chance to update its states, e.g., :indeterminate.
|
||||
if (mType == NS_FORM_INPUT_RADIO) {
|
||||
nsCOMPtr<nsIRadioVisitor> visitor = new nsRadioUpdateStateVisitor(this);
|
||||
VisitGroup(visitor, aNotify);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -6420,6 +6427,15 @@ HTMLInputElement::IntrinsicState() const
|
|||
state |= NS_EVENT_STATE_INDETERMINATE;
|
||||
}
|
||||
|
||||
if (mType == NS_FORM_INPUT_RADIO) {
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> selected = GetSelectedRadioButton();
|
||||
bool indeterminate = !selected && !mChecked;
|
||||
|
||||
if (indeterminate) {
|
||||
state |= NS_EVENT_STATE_INDETERMINATE;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether we are the default checked element (:default)
|
||||
if (DefaultChecked()) {
|
||||
state |= NS_EVENT_STATE_DEFAULT;
|
||||
|
@ -6643,6 +6659,9 @@ HTMLInputElement::WillRemoveFromRadioGroup()
|
|||
// longer a selected radio button
|
||||
if (mChecked) {
|
||||
container->SetCurrentRadioButton(name, nullptr);
|
||||
|
||||
nsCOMPtr<nsIRadioVisitor> visitor = new nsRadioUpdateStateVisitor(this);
|
||||
VisitGroup(visitor, true);
|
||||
}
|
||||
|
||||
// Remove this radio from its group in the container.
|
||||
|
|
|
@ -264,7 +264,7 @@ public:
|
|||
*
|
||||
* @return the selected button (or null).
|
||||
*/
|
||||
already_AddRefed<nsIDOMHTMLInputElement> GetSelectedRadioButton();
|
||||
already_AddRefed<nsIDOMHTMLInputElement> GetSelectedRadioButton() const;
|
||||
|
||||
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
|
||||
|
||||
|
|
|
@ -6490,11 +6490,19 @@ HTMLMediaElement::OnVisibilityChange(Visibility aOldVisibility,
|
|||
break;
|
||||
}
|
||||
case Visibility::NONVISIBLE: {
|
||||
if (mPlayTime.IsStarted()) {
|
||||
// Not visible, play time is running -> Start hidden play time if needed.
|
||||
HiddenVideoStart();
|
||||
}
|
||||
|
||||
mDecoder->NotifyOwnerActivityChanged(false);
|
||||
break;
|
||||
}
|
||||
case Visibility::MAY_BECOME_VISIBLE: {
|
||||
if (aOldVisibility == Visibility::NONVISIBLE) {
|
||||
// Visible -> Just pause hidden play time (no-op if already paused).
|
||||
HiddenVideoStop();
|
||||
|
||||
mDecoder->NotifyOwnerActivityChanged(true);
|
||||
} else if (aOldVisibility == Visibility::IN_DISPLAYPORT) {
|
||||
// Do nothing.
|
||||
|
@ -6502,6 +6510,9 @@ HTMLMediaElement::OnVisibilityChange(Visibility aOldVisibility,
|
|||
break;
|
||||
}
|
||||
case Visibility::IN_DISPLAYPORT: {
|
||||
// Visible -> Just pause hidden play time (no-op if already paused).
|
||||
HiddenVideoStop();
|
||||
|
||||
mDecoder->NotifyOwnerActivityChanged(true);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -55,3 +55,15 @@ nsRadioSetValueMissingState::Visit(nsIFormControl* aRadio)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsRadioUpdateStateVisitor::Visit(nsIFormControl* aRadio)
|
||||
{
|
||||
if (aRadio == mExcludeElement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HTMLInputElement* input = static_cast<HTMLInputElement*>(aRadio);
|
||||
input->UpdateState(true);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -94,5 +94,18 @@ protected:
|
|||
bool mNotify;
|
||||
};
|
||||
|
||||
class nsRadioUpdateStateVisitor : public nsRadioVisitor
|
||||
{
|
||||
public:
|
||||
explicit nsRadioUpdateStateVisitor(nsIFormControl* aExcludeElement)
|
||||
: mExcludeElement(aExcludeElement)
|
||||
{ }
|
||||
|
||||
virtual bool Visit(nsIFormControl* aRadio) override;
|
||||
|
||||
protected:
|
||||
nsIFormControl* mExcludeElement;
|
||||
};
|
||||
|
||||
#endif // _nsRadioVisitor_h__
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == button-load.html bu
|
|||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == button-create.html button-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == textarea-load.html textarea-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,3) needs-focus == textarea-create.html textarea-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,2,4) needs-focus == select-load.html select-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,9,6) needs-focus == select-load.html select-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,2,4) needs-focus == select-create.html select-ref.html # B2G timed out waiting for reftest-wait to be removed # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
needs-focus == autofocus-after-load.html autofocus-after-load-ref.html
|
||||
fails-if(B2G||Mulet) fuzzy-if(skiaContent,2,5) needs-focus == autofocus-leaves-iframe.html autofocus-leaves-iframe-ref.html # B2G focus difference between test and reference # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
|
|
|
@ -9,19 +9,19 @@ window.addEventListener("Test:DispatchKeyEvents", aEvent => {
|
|||
var keyCode = KeyEvent["DOM_" + aEvent.detail.code];
|
||||
|
||||
document.body.focus();
|
||||
var evt = document.createEvent("KeyEvents");
|
||||
var evt = document.createEvent("KeyboardEvent");
|
||||
evt.initKeyEvent("keydown", true, true, window,
|
||||
false, false, false, false,
|
||||
keyCode, 0);
|
||||
document.body.dispatchEvent(evt);
|
||||
|
||||
evt = document.createEvent("KeyEvents");
|
||||
evt = document.createEvent("KeyboardEvent");
|
||||
evt.initKeyEvent("keypress", true, true, window,
|
||||
false, false, false, false,
|
||||
keyCode, 0);
|
||||
document.body.dispatchEvent(evt);
|
||||
|
||||
evt = document.createEvent("KeyEvents");
|
||||
evt = document.createEvent("KeyboardEvent");
|
||||
evt.initKeyEvent("keyup", true, true, window,
|
||||
false, false, false, false,
|
||||
keyCode, 0);
|
||||
|
|
|
@ -9,6 +9,7 @@ support-files =
|
|||
[test_bug1286509.html]
|
||||
skip-if = os == "android" || appname == "b2g" # up/down arrow keys not supported on android/b2g
|
||||
[test_button_attributes_reflection.html]
|
||||
[test_input_radio_indeterminate.html]
|
||||
[test_input_radio_radiogroup.html]
|
||||
[test_input_radio_required.html]
|
||||
[test_change_event.html]
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=885359
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 885359</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885359">Mozilla Bug 343444</a>
|
||||
<p id="display"></p>
|
||||
<form>
|
||||
<input type="radio" id='radio1'/><br/>
|
||||
|
||||
<input type="radio" id="g1radio1" name="group1"/>
|
||||
<input type="radio" id="g1radio2" name="group1"/></br>
|
||||
<input type="radio" id="g1radio3" name="group1"/></br>
|
||||
|
||||
<input type="radio" id="g2radio1" name="group2"/>
|
||||
<input type="radio" id="g2radio2" name="group2" checked/></br>
|
||||
</form>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var radio1 = document.getElementById("radio1");
|
||||
var g1radio1 = document.getElementById("g1radio1");
|
||||
var g1radio2 = document.getElementById("g1radio2");
|
||||
var g1radio3 = document.getElementById("g1radio3");
|
||||
var g2radio1 = document.getElementById("g2radio1");
|
||||
var g2radio2 = document.getElementById("g2radio2");
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
test();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
function verifyIndeterminateState(aElement, aIsIndeterminate, aMessage) {
|
||||
is(aElement.mozMatchesSelector(':indeterminate'), aIsIndeterminate, aMessage);
|
||||
}
|
||||
|
||||
function test() {
|
||||
// Initial State.
|
||||
verifyIndeterminateState(radio1, true,
|
||||
"Unchecked radio in its own group (no name attribute)");
|
||||
verifyIndeterminateState(g1radio1, true, "No selected radio in its group");
|
||||
verifyIndeterminateState(g1radio2, true, "No selected radio in its group");
|
||||
verifyIndeterminateState(g1radio3, true, "No selected radio in its group");
|
||||
verifyIndeterminateState(g2radio1, false, "Selected radio in its group");
|
||||
verifyIndeterminateState(g2radio2, false, "Selected radio in its group");
|
||||
|
||||
// Selecting radio buttion.
|
||||
g1radio1.checked = true;
|
||||
verifyIndeterminateState(g1radio1, false,
|
||||
"Selecting a radio should affect all radios in the group");
|
||||
verifyIndeterminateState(g1radio2, false,
|
||||
"Selecting a radio should affect all radios in the group");
|
||||
verifyIndeterminateState(g1radio3, false,
|
||||
"Selecting a radio should affect all radios in the group");
|
||||
|
||||
// Changing the selected radio button.
|
||||
g1radio3.checked = true;
|
||||
verifyIndeterminateState(g1radio1, false,
|
||||
"Selecting a radio should affect all radios in the group");
|
||||
verifyIndeterminateState(g1radio2, false,
|
||||
"Selecting a radio should affect all radios in the group");
|
||||
verifyIndeterminateState(g1radio3, false,
|
||||
"Selecting a radio should affect all radios in the group");
|
||||
|
||||
// Deselecting radio button.
|
||||
g2radio2.checked = false;
|
||||
verifyIndeterminateState(g2radio1, true,
|
||||
"Deselecting a radio should affect all radios in the group");
|
||||
verifyIndeterminateState(g2radio2, true,
|
||||
"Deselecting a radio should affect all radios in the group");
|
||||
|
||||
// Move a selected radio button to another group.
|
||||
g1radio3.name = "group2";
|
||||
|
||||
// The radios' state in the original group becomes indeterminated.
|
||||
verifyIndeterminateState(g1radio1, true,
|
||||
"Removing a radio from a group should affect all radios in the group");
|
||||
verifyIndeterminateState(g1radio2, true,
|
||||
"Removing a radio from a group should affect all radios in the group");
|
||||
|
||||
// The radios' state in the new group becomes determinated.
|
||||
verifyIndeterminateState(g1radio3, false,
|
||||
"Adding a radio from a group should affect all radios in the group");
|
||||
verifyIndeterminateState(g2radio1, false,
|
||||
"Adding a radio from a group should affect all radios in the group");
|
||||
verifyIndeterminateState(g2radio2, false,
|
||||
"Adding a radio from a group should affect all radios in the group");
|
||||
|
||||
// Change input type to 'text'.
|
||||
g1radio3.type = "text";
|
||||
verifyIndeterminateState(g1radio3, false,
|
||||
"Input type text does not have an indeterminate state");
|
||||
verifyIndeterminateState(g2radio1, true,
|
||||
"Changing input type should affect all radios in the group");
|
||||
verifyIndeterminateState(g2radio2, true,
|
||||
"Changing input type should affect all radios in the group");
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -8,17 +8,6 @@
|
|||
[builtinclass, scriptable, uuid(8e49f7b0-1f98-4939-bf91-e9c39cd56434)]
|
||||
interface nsITabParent : nsISupports
|
||||
{
|
||||
void injectTouchEvent(in AString aType,
|
||||
[array, size_is(count)] in uint32_t aIdentifiers,
|
||||
[array, size_is(count)] in int32_t aXs,
|
||||
[array, size_is(count)] in int32_t aYs,
|
||||
[array, size_is(count)] in uint32_t aRxs,
|
||||
[array, size_is(count)] in uint32_t aRys,
|
||||
[array, size_is(count)] in float aRotationAngles,
|
||||
[array, size_is(count)] in float aForces,
|
||||
in uint32_t count,
|
||||
in long aModifiers);
|
||||
|
||||
void getChildProcessOffset(out int32_t aCssX, out int32_t aCssY);
|
||||
|
||||
readonly attribute boolean useAsyncPanZoom;
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include "mozilla/dom/GetFilesHelper.h"
|
||||
#include "mozilla/dom/PCrashReporterChild.h"
|
||||
#include "mozilla/dom/ProcessGlobal.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/workers/ServiceWorkerManager.h"
|
||||
#include "mozilla/dom/nsIContentChild.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
|
@ -2263,22 +2262,6 @@ ContentChild::AddRemoteAlertObserver(const nsString& aData,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ContentChild::RecvSystemMemoryAvailable(const uint64_t& aGetterId,
|
||||
const uint32_t& aMemoryAvailable)
|
||||
{
|
||||
RefPtr<Promise> p = dont_AddRef(reinterpret_cast<Promise*>(aGetterId));
|
||||
|
||||
if (!aMemoryAvailable) {
|
||||
p->MaybeReject(NS_ERROR_NOT_AVAILABLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
p->MaybeResolve((int)aMemoryAvailable);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvPreferenceUpdate(const PrefSetting& aPref)
|
||||
{
|
||||
|
|
|
@ -404,9 +404,6 @@ public:
|
|||
// auto remove when alertfinished is received.
|
||||
nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
|
||||
|
||||
virtual bool RecvSystemMemoryAvailable(const uint64_t& aGetterId,
|
||||
const uint32_t& aMemoryAvailable) override;
|
||||
|
||||
virtual bool RecvPreferenceUpdate(const PrefSetting& aPref) override;
|
||||
virtual bool RecvVarUpdate(const GfxVarUpdate& pref) override;
|
||||
|
||||
|
|
|
@ -3909,20 +3909,6 @@ ContentParent::RecvNSSU2FTokenSign(nsTArray<uint8_t>&& aApplication,
|
|||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvGetSystemMemory(const uint64_t& aGetterId)
|
||||
{
|
||||
uint32_t memoryTotal = 0;
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
memoryTotal = mozilla::hal::GetTotalSystemMemoryLevel();
|
||||
#endif
|
||||
|
||||
Unused << SendSystemMemoryAvailable(aGetterId, memoryTotal);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache)
|
||||
{
|
||||
|
|
|
@ -1013,8 +1013,6 @@ private:
|
|||
const bool& aContentOrNormalChannel,
|
||||
const bool& aAnyChannel) override;
|
||||
|
||||
virtual bool RecvGetSystemMemory(const uint64_t& getterId) override;
|
||||
|
||||
virtual bool RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache) override;
|
||||
|
||||
virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue) override;
|
||||
|
|
|
@ -518,8 +518,6 @@ child:
|
|||
|
||||
async NotifyVisited(URIParams uri);
|
||||
|
||||
async SystemMemoryAvailable(uint64_t getterId, uint32_t memoryAvailable);
|
||||
|
||||
async PreferenceUpdate(PrefSetting pref);
|
||||
async VarUpdate(GfxVarUpdate var);
|
||||
|
||||
|
@ -829,8 +827,6 @@ parent:
|
|||
uint8_t[] keyHandle)
|
||||
returns (uint8_t[] signature);
|
||||
|
||||
async GetSystemMemory(uint64_t getterId);
|
||||
|
||||
sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
|
||||
returns (bool isSecureURI);
|
||||
|
||||
|
|
|
@ -2809,71 +2809,6 @@ TabParent::GetLoadContext()
|
|||
return loadContext.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::InjectTouchEvent(const nsAString& aType,
|
||||
uint32_t* aIdentifiers,
|
||||
int32_t* aXs,
|
||||
int32_t* aYs,
|
||||
uint32_t* aRxs,
|
||||
uint32_t* aRys,
|
||||
float* aRotationAngles,
|
||||
float* aForces,
|
||||
uint32_t aCount,
|
||||
int32_t aModifiers)
|
||||
{
|
||||
EventMessage msg;
|
||||
nsContentUtils::GetEventMessageAndAtom(aType, eTouchEventClass, &msg);
|
||||
if (msg != eTouchStart && msg != eTouchMove &&
|
||||
msg != eTouchEnd && msg != eTouchCancel) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
WidgetTouchEvent event(true, msg, widget);
|
||||
event.mModifiers = aModifiers;
|
||||
event.mTime = PR_IntervalNow();
|
||||
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
|
||||
if (!content || !content->OwnerDoc()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsIDocument* doc = content->OwnerDoc();
|
||||
if (!doc || !doc->GetShell()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsPresContext* presContext = doc->GetShell()->GetPresContext();
|
||||
|
||||
event.mTouches.SetCapacity(aCount);
|
||||
for (uint32_t i = 0; i < aCount; ++i) {
|
||||
LayoutDeviceIntPoint pt =
|
||||
LayoutDeviceIntPoint::FromAppUnitsRounded(
|
||||
CSSPoint::ToAppUnits(CSSPoint(aXs[i], aYs[i])),
|
||||
presContext->AppUnitsPerDevPixel());
|
||||
|
||||
LayoutDeviceIntPoint radius =
|
||||
LayoutDeviceIntPoint::FromAppUnitsRounded(
|
||||
CSSPoint::ToAppUnits(CSSPoint(aRxs[i], aRys[i])),
|
||||
presContext->AppUnitsPerDevPixel());
|
||||
|
||||
RefPtr<Touch> t =
|
||||
new Touch(aIdentifiers[i], pt, radius, aRotationAngles[i], aForces[i]);
|
||||
|
||||
// Consider all injected touch events as changedTouches. For more details
|
||||
// about the meaning of changedTouches for each event, see
|
||||
// https://developer.mozilla.org/docs/Web/API/TouchEvent.changedTouches
|
||||
t->mChanged = true;
|
||||
event.mTouches.AppendElement(t);
|
||||
}
|
||||
|
||||
SendRealTouchEvent(event);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom)
|
||||
{
|
||||
|
|
|
@ -2024,8 +2024,6 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
|
|||
}
|
||||
|
||||
SetState(DECODER_STATE_DECODING);
|
||||
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2181,12 +2179,6 @@ MediaDecoderStateMachine::SeekCompleted()
|
|||
FinishDecodeFirstFrame();
|
||||
}
|
||||
|
||||
if (nextState == DECODER_STATE_DECODING) {
|
||||
SetState(DECODER_STATE_DECODING);
|
||||
} else {
|
||||
SetState(nextState);
|
||||
}
|
||||
|
||||
// Ensure timestamps are up to date.
|
||||
UpdatePlaybackPositionInternal(newCurrentTime);
|
||||
|
||||
|
@ -2198,12 +2190,13 @@ MediaDecoderStateMachine::SeekCompleted()
|
|||
// if we need to buffer after the seek.
|
||||
mQuickBuffering = false;
|
||||
|
||||
ScheduleStateMachine();
|
||||
|
||||
if (video) {
|
||||
mMediaSink->Redraw(mInfo.mVideo);
|
||||
mOnPlaybackEvent.Notify(MediaEventType::Invalidate);
|
||||
}
|
||||
|
||||
SetState(nextState);
|
||||
MOZ_ASSERT(IsStateMachineScheduled());
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise>
|
||||
|
|
|
@ -799,7 +799,8 @@ MediaDevice::FitnessDistance(nsString aN,
|
|||
|
||||
uint32_t
|
||||
MediaDevice::GetBestFitnessDistance(
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets)
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
|
||||
bool aIsChrome)
|
||||
{
|
||||
nsString mediaSource;
|
||||
GetMediaSource(mediaSource);
|
||||
|
@ -818,7 +819,11 @@ MediaDevice::GetBestFitnessDistance(
|
|||
// Forward request to underlying object to interrogate per-mode capabilities.
|
||||
// Pass in device's origin-specific id for deviceId constraint comparison.
|
||||
nsString id;
|
||||
GetId(id);
|
||||
if (aIsChrome) {
|
||||
GetRawId(id);
|
||||
} else {
|
||||
GetId(id);
|
||||
}
|
||||
return mSource->GetBestFitnessDistance(aConstraintSets, id);
|
||||
}
|
||||
|
||||
|
@ -1333,6 +1338,7 @@ static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices;
|
|||
already_AddRefed<MediaManager::PledgeChar>
|
||||
MediaManager::SelectSettings(
|
||||
MediaStreamConstraints& aConstraints,
|
||||
bool aIsChrome,
|
||||
RefPtr<Refcountable<UniquePtr<SourceSet>>>& aSources)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -1342,7 +1348,8 @@ MediaManager::SelectSettings(
|
|||
// Algorithm accesses device capabilities code and must run on media thread.
|
||||
// Modifies passed-in aSources.
|
||||
|
||||
MediaManager::PostTask(NewTaskFrom([id, aConstraints, aSources]() mutable {
|
||||
MediaManager::PostTask(NewTaskFrom([id, aConstraints,
|
||||
aSources, aIsChrome]() mutable {
|
||||
auto& sources = **aSources;
|
||||
|
||||
// Since the advanced part of the constraints algorithm needs to know when
|
||||
|
@ -1368,11 +1375,13 @@ MediaManager::SelectSettings(
|
|||
|
||||
if (needVideo && videos.Length()) {
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
NormalizedConstraints(GetInvariant(aConstraints.mVideo)), videos);
|
||||
NormalizedConstraints(GetInvariant(aConstraints.mVideo)), videos,
|
||||
aIsChrome);
|
||||
}
|
||||
if (!badConstraint && needAudio && audios.Length()) {
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
NormalizedConstraints(GetInvariant(aConstraints.mAudio)), audios);
|
||||
NormalizedConstraints(GetInvariant(aConstraints.mAudio)), audios,
|
||||
aIsChrome);
|
||||
}
|
||||
if (!badConstraint &&
|
||||
!needVideo == !videos.Length() &&
|
||||
|
@ -1414,6 +1423,7 @@ public:
|
|||
uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
|
||||
MediaEnginePrefs &aPrefs,
|
||||
const nsCString& aOrigin,
|
||||
bool aIsChrome,
|
||||
MediaManager::SourceSet* aSourceSet)
|
||||
: mConstraints(aConstraints)
|
||||
, mOnSuccess(aOnSuccess)
|
||||
|
@ -1422,6 +1432,7 @@ public:
|
|||
, mListener(aListener)
|
||||
, mPrefs(aPrefs)
|
||||
, mOrigin(aOrigin)
|
||||
, mIsChrome(aIsChrome)
|
||||
, mDeviceChosen(false)
|
||||
, mSourceSet(aSourceSet)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
|
@ -1473,7 +1484,7 @@ public:
|
|||
nsTArray<RefPtr<AudioDevice>> audios;
|
||||
audios.AppendElement(mAudioDevice);
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
NormalizedConstraints(constraints), audios);
|
||||
NormalizedConstraints(constraints), audios, mIsChrome);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1486,7 +1497,7 @@ public:
|
|||
nsTArray<RefPtr<VideoDevice>> videos;
|
||||
videos.AppendElement(mVideoDevice);
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
NormalizedConstraints(constraints), videos);
|
||||
NormalizedConstraints(constraints), videos, mIsChrome);
|
||||
}
|
||||
if (mAudioDevice) {
|
||||
mAudioDevice->Deallocate();
|
||||
|
@ -1517,8 +1528,8 @@ public:
|
|||
|
||||
NS_DispatchToMainThread(do_AddRef(
|
||||
new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID,
|
||||
mListener, mOrigin, mConstraints,
|
||||
mAudioDevice, mVideoDevice,
|
||||
mListener, mOrigin,
|
||||
mConstraints, mAudioDevice, mVideoDevice,
|
||||
peerIdentity)));
|
||||
MOZ_ASSERT(!mOnSuccess);
|
||||
MOZ_ASSERT(!mOnFailure);
|
||||
|
@ -1600,6 +1611,7 @@ private:
|
|||
RefPtr<VideoDevice> mVideoDevice;
|
||||
MediaEnginePrefs mPrefs;
|
||||
nsCString mOrigin;
|
||||
bool mIsChrome;
|
||||
|
||||
bool mDeviceChosen;
|
||||
public:
|
||||
|
@ -1983,17 +1995,6 @@ MediaManager::NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool MediaManager::IsPrivileged()
|
||||
{
|
||||
bool permission = nsContentUtils::IsCallerChrome();
|
||||
|
||||
// Developer preference for turning off permission check.
|
||||
if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
|
||||
permission = true;
|
||||
}
|
||||
return permission;
|
||||
}
|
||||
|
||||
bool MediaManager::IsLoop(nsIURI* aDocURI)
|
||||
{
|
||||
MOZ_ASSERT(aDocURI);
|
||||
|
@ -2114,7 +2115,9 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
bool loop = IsLoop(docURI);
|
||||
bool privileged = IsPrivileged();
|
||||
bool isChrome = nsContentUtils::IsCallerChrome();
|
||||
bool privileged = isChrome ||
|
||||
Preferences::GetBool("media.navigator.permission.disabled", false);
|
||||
bool isHTTPS = false;
|
||||
docURI->SchemeIs("https", &isHTTPS);
|
||||
nsCString host;
|
||||
|
@ -2208,7 +2211,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
|||
false) && !IsVistaOrLater()) ||
|
||||
#endif
|
||||
(!privileged && !HostIsHttps(*docURI)) ||
|
||||
!HostHasPermission(*docURI)) {
|
||||
(!isChrome && !HostHasPermission(*docURI))) {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("NotAllowedError"));
|
||||
|
@ -2378,7 +2381,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
|||
RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType,
|
||||
audioType, fake);
|
||||
p->Then([this, onSuccess, onFailure, windowID, c, listener, askPermission,
|
||||
prefs, isHTTPS, callID, origin](SourceSet*& aDevices) mutable {
|
||||
prefs, isHTTPS, callID, origin, isChrome](SourceSet*& aDevices) mutable {
|
||||
|
||||
RefPtr<Refcountable<UniquePtr<SourceSet>>> devices(
|
||||
new Refcountable<UniquePtr<SourceSet>>(aDevices)); // grab result
|
||||
|
@ -2390,11 +2393,11 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
|||
}
|
||||
|
||||
// Apply any constraints. This modifies the passed-in list.
|
||||
RefPtr<PledgeChar> p2 = SelectSettings(c, devices);
|
||||
RefPtr<PledgeChar> p2 = SelectSettings(c, isChrome, devices);
|
||||
|
||||
p2->Then([this, onSuccess, onFailure, windowID, c,
|
||||
listener, askPermission, prefs, isHTTPS,
|
||||
callID, origin, devices](const char*& badConstraint) mutable {
|
||||
listener, askPermission, prefs, isHTTPS, callID,
|
||||
origin, isChrome, devices](const char*& badConstraint) mutable {
|
||||
|
||||
// Ensure that the captured 'this' pointer and our windowID are still good.
|
||||
auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(windowID);
|
||||
|
@ -2441,6 +2444,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
|||
onFailure.forget(),
|
||||
windowID, listener,
|
||||
prefs, origin,
|
||||
isChrome,
|
||||
devices->release()));
|
||||
// Store the task w/callbacks.
|
||||
mActiveCallbacks.Put(callID, task.forget());
|
||||
|
@ -3489,10 +3493,11 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
|
|||
RefPtr<MediaManager> mgr = MediaManager::GetInstance();
|
||||
uint32_t id = mgr->mOutstandingVoidPledges.Append(*p);
|
||||
uint64_t windowId = aWindow->WindowID();
|
||||
bool isChrome = nsContentUtils::IsCallerChrome();
|
||||
|
||||
MediaManager::PostTask(NewTaskFrom([id, windowId,
|
||||
audioDevice, videoDevice,
|
||||
aConstraints]() mutable {
|
||||
aConstraints, isChrome]() mutable {
|
||||
MOZ_ASSERT(MediaManager::IsInMediaThread());
|
||||
RefPtr<MediaManager> mgr = MediaManager::GetInstance();
|
||||
const char* badConstraint = nullptr;
|
||||
|
@ -3504,7 +3509,7 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
|
|||
nsTArray<RefPtr<AudioDevice>> audios;
|
||||
audios.AppendElement(audioDevice);
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
NormalizedConstraints(aConstraints), audios);
|
||||
NormalizedConstraints(aConstraints), audios, isChrome);
|
||||
}
|
||||
} else {
|
||||
rv = videoDevice->Restart(aConstraints, mgr->mPrefs, &badConstraint);
|
||||
|
@ -3512,7 +3517,7 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
|
|||
nsTArray<RefPtr<VideoDevice>> videos;
|
||||
videos.AppendElement(videoDevice);
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
NormalizedConstraints(aConstraints), videos);
|
||||
NormalizedConstraints(aConstraints), videos, isChrome);
|
||||
}
|
||||
}
|
||||
NS_DispatchToMainThread(NewRunnableFrom([id, windowId, rv,
|
||||
|
|
|
@ -72,7 +72,8 @@ public:
|
|||
void SetId(const nsAString& aID);
|
||||
void SetRawId(const nsAString& aID);
|
||||
virtual uint32_t GetBestFitnessDistance(
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets);
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
|
||||
bool aIsChrome);
|
||||
virtual Source* GetSource() = 0;
|
||||
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
|
||||
const MediaEnginePrefs &aPrefs,
|
||||
|
@ -268,7 +269,6 @@ private:
|
|||
typedef media::Pledge<const char*, dom::MediaStreamError*> PledgeChar;
|
||||
typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
|
||||
|
||||
static bool IsPrivileged();
|
||||
static bool IsLoop(nsIURI* aDocURI);
|
||||
static nsresult GenerateUUID(nsAString& aResult);
|
||||
static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey);
|
||||
|
@ -289,6 +289,7 @@ private:
|
|||
already_AddRefed<PledgeChar>
|
||||
SelectSettings(
|
||||
dom::MediaStreamConstraints& aConstraints,
|
||||
bool aIsChrome,
|
||||
RefPtr<media::Refcountable<UniquePtr<SourceSet>>>& aSources);
|
||||
|
||||
StreamListeners* AddWindowID(uint64_t aWindowId);
|
||||
|
|
|
@ -441,7 +441,8 @@ MediaConstraintsHelper::FindBadConstraint(
|
|||
mDeviceId(MockDevice::HasThreadSafeRefCnt::value ? aDeviceId : nsString()) {}
|
||||
|
||||
uint32_t GetBestFitnessDistance(
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets)
|
||||
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
|
||||
bool aIsChrome)
|
||||
{
|
||||
return mMediaEngineSource->GetBestFitnessDistance(aConstraintSets,
|
||||
mDeviceId);
|
||||
|
|
|
@ -310,7 +310,7 @@ protected:
|
|||
|
||||
MOZ_ASSERT(aDevices.Length());
|
||||
for (auto& device : aDevices) {
|
||||
if (device->GetBestFitnessDistance(sets) != UINT32_MAX) {
|
||||
if (device->GetBestFitnessDistance(sets, false) != UINT32_MAX) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +323,8 @@ public:
|
|||
template<class DeviceType>
|
||||
static const char*
|
||||
SelectSettings(const NormalizedConstraints &aConstraints,
|
||||
nsTArray<RefPtr<DeviceType>>& aDevices)
|
||||
nsTArray<RefPtr<DeviceType>>& aDevices,
|
||||
bool aIsChrome)
|
||||
{
|
||||
auto& c = aConstraints;
|
||||
|
||||
|
@ -339,7 +340,8 @@ public:
|
|||
std::multimap<uint32_t, RefPtr<DeviceType>> ordered;
|
||||
|
||||
for (uint32_t i = 0; i < aDevices.Length();) {
|
||||
uint32_t distance = aDevices[i]->GetBestFitnessDistance(aggregateConstraints);
|
||||
uint32_t distance = aDevices[i]->GetBestFitnessDistance(aggregateConstraints,
|
||||
aIsChrome);
|
||||
if (distance == UINT32_MAX) {
|
||||
unsatisfactory.AppendElement(aDevices[i]);
|
||||
aDevices.RemoveElementAt(i);
|
||||
|
@ -365,7 +367,8 @@ public:
|
|||
aggregateConstraints.AppendElement(&c.mAdvanced[i]);
|
||||
nsTArray<RefPtr<DeviceType>> rejects;
|
||||
for (uint32_t j = 0; j < aDevices.Length();) {
|
||||
if (aDevices[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) {
|
||||
if (aDevices[j]->GetBestFitnessDistance(aggregateConstraints,
|
||||
aIsChrome) == UINT32_MAX) {
|
||||
rejects.AppendElement(aDevices[j]);
|
||||
aDevices.RemoveElementAt(j);
|
||||
} else {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# basic sanity checking
|
||||
random-if(!haveTestPlugin) != plugin-sanity.html about:blank
|
||||
fails-if(!haveTestPlugin) == plugin-sanity.html div-sanity.html
|
||||
fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,160000) == plugin-alpha-zindex.html div-alpha-zindex.html
|
||||
fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,164000) == plugin-alpha-opacity.html div-alpha-opacity.html
|
||||
fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-alpha-zindex.html div-alpha-zindex.html
|
||||
fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,164000) == plugin-alpha-opacity.html div-alpha-opacity.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1-ref.html # bug 631832
|
||||
# fuzzy because of anti-aliasing in dashed border
|
||||
fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1-ref.html # bug 629430
|
||||
|
@ -11,14 +11,14 @@ fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) skip-if(!have
|
|||
# The following two "pluginproblemui-direction" tests are unreliable on all platforms. They should be re-written or replaced.
|
||||
#random-if(cocoaWidget||d2d||/^Windows\x20NT\x205\.1/.test(http.oscpu)) fails-if(!haveTestPlugin&&!Android) == pluginproblemui-direction-1.html pluginproblemui-direction-1-ref.html # bug 567367
|
||||
#random-if(cocoaWidget) fails-if(!haveTestPlugin&&!Android) == pluginproblemui-direction-2.html pluginproblemui-direction-2-ref.html
|
||||
fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,160000) == plugin-canvas-alpha-zindex.html div-alpha-zindex.html
|
||||
fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,160000) == plugin-transform-alpha-zindex.html div-alpha-zindex.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,160000) == plugin-busy-alpha-zindex.html div-alpha-zindex.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,32400) == plugin-background.html plugin-background-ref.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,32400) == plugin-background-1-step.html plugin-background-ref.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,32400) == plugin-background-2-step.html plugin-background-ref.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,32400) == plugin-background-5-step.html plugin-background-ref.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent,1,32400) == plugin-background-10-step.html plugin-background-ref.html
|
||||
fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-canvas-alpha-zindex.html div-alpha-zindex.html
|
||||
fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-transform-alpha-zindex.html div-alpha-zindex.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-busy-alpha-zindex.html div-alpha-zindex.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background.html plugin-background-ref.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-1-step.html plugin-background-ref.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-2-step.html plugin-background-ref.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-5-step.html plugin-background-ref.html
|
||||
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-10-step.html plugin-background-ref.html
|
||||
random-if(!haveTestPlugin) == plugin-transform-1.html plugin-transform-1-ref.html
|
||||
fails-if(!haveTestPlugin) == plugin-transform-2.html plugin-transform-2-ref.html
|
||||
skip-if(!haveTestPlugin) == shrink-1.html shrink-1-ref.html
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#include "AvailabilityCollection.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "PresentationAvailability.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/* static */
|
||||
StaticAutoPtr<AvailabilityCollection>
|
||||
AvailabilityCollection::sSingleton;
|
||||
static bool gOnceAliveNowDead = false;
|
||||
|
||||
/* static */ AvailabilityCollection*
|
||||
AvailabilityCollection::GetSingleton()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sSingleton && !gOnceAliveNowDead) {
|
||||
sSingleton = new AvailabilityCollection();
|
||||
ClearOnShutdown(&sSingleton);
|
||||
}
|
||||
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
AvailabilityCollection::AvailabilityCollection()
|
||||
{
|
||||
MOZ_COUNT_CTOR(AvailabilityCollection);
|
||||
}
|
||||
|
||||
AvailabilityCollection::~AvailabilityCollection()
|
||||
{
|
||||
MOZ_COUNT_DTOR(AvailabilityCollection);
|
||||
gOnceAliveNowDead = true;
|
||||
}
|
||||
|
||||
void
|
||||
AvailabilityCollection::Add(PresentationAvailability* aAvailability)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!aAvailability) {
|
||||
return;
|
||||
}
|
||||
|
||||
WeakPtr<PresentationAvailability> availability = aAvailability;
|
||||
if (mAvailabilities.Contains(aAvailability)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mAvailabilities.AppendElement(aAvailability);
|
||||
}
|
||||
|
||||
void
|
||||
AvailabilityCollection::Remove(PresentationAvailability* aAvailability)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!aAvailability) {
|
||||
return;
|
||||
}
|
||||
|
||||
WeakPtr<PresentationAvailability> availability = aAvailability;
|
||||
mAvailabilities.RemoveElement(availability);
|
||||
}
|
||||
|
||||
already_AddRefed<PresentationAvailability>
|
||||
AvailabilityCollection::Find(const uint64_t aWindowId, const nsAString& aUrl)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Loop backwards to allow removing elements in the loop.
|
||||
for (int i = mAvailabilities.Length() - 1; i >= 0; --i) {
|
||||
WeakPtr<PresentationAvailability> availability = mAvailabilities[i];
|
||||
if (!availability) {
|
||||
// The availability object was destroyed. Remove it from the list.
|
||||
mAvailabilities.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (availability->Equals(aWindowId, aUrl)) {
|
||||
RefPtr<PresentationAvailability> matchedAvailability = availability.get();
|
||||
return matchedAvailability.forget();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_AvailabilityCollection_h
|
||||
#define mozilla_dom_AvailabilityCollection_h
|
||||
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationAvailability;
|
||||
|
||||
class AvailabilityCollection final
|
||||
{
|
||||
public:
|
||||
static AvailabilityCollection* GetSingleton();
|
||||
|
||||
void Add(PresentationAvailability* aAvailability);
|
||||
|
||||
void Remove(PresentationAvailability* aAvailability);
|
||||
|
||||
already_AddRefed<PresentationAvailability>
|
||||
Find(const uint64_t aWindowId, const nsAString& aUrl);
|
||||
|
||||
private:
|
||||
friend class StaticAutoPtr<AvailabilityCollection>;
|
||||
|
||||
AvailabilityCollection();
|
||||
virtual ~AvailabilityCollection();
|
||||
|
||||
static StaticAutoPtr<AvailabilityCollection> sSingleton;
|
||||
nsTArray<WeakPtr<PresentationAvailability>> mAvailabilities;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_AvailabilityCollection_h
|
|
@ -4,12 +4,15 @@
|
|||
* 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/. */
|
||||
|
||||
#include "PresentationAvailability.h"
|
||||
|
||||
#include "mozilla/dom/PresentationAvailabilityBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIPresentationDeviceManager.h"
|
||||
#include "nsIPresentationService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "PresentationAvailability.h"
|
||||
#include "PresentationLog.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -17,9 +20,11 @@ using namespace mozilla::dom;
|
|||
NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationAvailability)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromises)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromises);
|
||||
tmp->Shutdown();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
@ -31,15 +36,21 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationAvailability)
|
|||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
/* static */ already_AddRefed<PresentationAvailability>
|
||||
PresentationAvailability::Create(nsPIDOMWindowInner* aWindow)
|
||||
PresentationAvailability::Create(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aUrl,
|
||||
RefPtr<Promise>& aPromise)
|
||||
{
|
||||
RefPtr<PresentationAvailability> availability = new PresentationAvailability(aWindow);
|
||||
return NS_WARN_IF(!availability->Init()) ? nullptr : availability.forget();
|
||||
RefPtr<PresentationAvailability> availability =
|
||||
new PresentationAvailability(aWindow, aUrl);
|
||||
return NS_WARN_IF(!availability->Init(aPromise)) ? nullptr
|
||||
: availability.forget();
|
||||
}
|
||||
|
||||
PresentationAvailability::PresentationAvailability(nsPIDOMWindowInner* aWindow)
|
||||
PresentationAvailability::PresentationAvailability(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aUrl)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mIsAvailable(false)
|
||||
, mUrl(aUrl)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -49,7 +60,7 @@ PresentationAvailability::~PresentationAvailability()
|
|||
}
|
||||
|
||||
bool
|
||||
PresentationAvailability::Init()
|
||||
PresentationAvailability::Init(RefPtr<Promise>& aPromise)
|
||||
{
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
|
@ -59,21 +70,30 @@ PresentationAvailability::Init()
|
|||
|
||||
nsresult rv = service->RegisterAvailabilityListener(this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
// If the user agent is unable to monitor available device,
|
||||
// Resolve promise with |value| set to false.
|
||||
mIsAvailable = false;
|
||||
aPromise->MaybeResolve(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationDeviceManager> deviceManager =
|
||||
do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID);
|
||||
if (NS_WARN_IF(!deviceManager)) {
|
||||
return false;
|
||||
EnqueuePromise(aPromise);
|
||||
|
||||
AvailabilityCollection* collection = AvailabilityCollection::GetSingleton();
|
||||
if (collection) {
|
||||
collection->Add(this);
|
||||
}
|
||||
deviceManager->GetDeviceAvailable(&mIsAvailable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PresentationAvailability::Shutdown()
|
||||
{
|
||||
AvailabilityCollection* collection = AvailabilityCollection::GetSingleton();
|
||||
if (collection ) {
|
||||
collection->Remove(this);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
|
@ -98,6 +118,32 @@ PresentationAvailability::WrapObject(JSContext* aCx,
|
|||
return PresentationAvailabilityBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationAvailability::Equals(const uint64_t aWindowID,
|
||||
const nsAString& aUrl) const
|
||||
{
|
||||
if (GetOwner() && GetOwner()->WindowID() == aWindowID &&
|
||||
mUrl.Equals(aUrl)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationAvailability::IsCachedValueReady()
|
||||
{
|
||||
// All pending promises will be solved when cached value is ready and
|
||||
// no promise should be enqueued afterward.
|
||||
return mPromises.IsEmpty();
|
||||
}
|
||||
|
||||
void
|
||||
PresentationAvailability::EnqueuePromise(RefPtr<Promise>& aPromise)
|
||||
{
|
||||
mPromises.AppendElement(aPromise);
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationAvailability::Value() const
|
||||
{
|
||||
|
@ -114,8 +160,28 @@ PresentationAvailability::NotifyAvailableChange(bool aIsAvailable)
|
|||
}
|
||||
|
||||
void
|
||||
PresentationAvailability::UpdateAvailabilityAndDispatchEvent(bool aIsAvailable) {
|
||||
PresentationAvailability::UpdateAvailabilityAndDispatchEvent(bool aIsAvailable)
|
||||
{
|
||||
PRES_DEBUG("%s:id[%s]\n", __func__,
|
||||
NS_ConvertUTF16toUTF8(mUrl).get());
|
||||
bool isChanged = (aIsAvailable != mIsAvailable);
|
||||
|
||||
mIsAvailable = aIsAvailable;
|
||||
|
||||
NS_WARN_IF(NS_FAILED(DispatchTrustedEvent(NS_LITERAL_STRING("change"))));
|
||||
if (!mPromises.IsEmpty()) {
|
||||
// Use the first availability change notification to resolve promise.
|
||||
do {
|
||||
nsTArray<RefPtr<Promise>> promises = Move(mPromises);
|
||||
for (auto& promise : promises) {
|
||||
promise->MaybeResolve(this);
|
||||
}
|
||||
// more promises may have been added to mPromises, at least in theory
|
||||
} while (!mPromises.IsEmpty());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isChanged) {
|
||||
NS_WARN_IF(NS_FAILED(DispatchTrustedEvent(NS_LITERAL_STRING("change"))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,44 +9,62 @@
|
|||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsIPresentationListener.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
|
||||
class PresentationAvailability final : public DOMEventTargetHelper
|
||||
, public nsIPresentationAvailabilityListener
|
||||
, public SupportsWeakPtr<PresentationAvailability>
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationAvailability,
|
||||
DOMEventTargetHelper)
|
||||
NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PresentationAvailability)
|
||||
|
||||
static already_AddRefed<PresentationAvailability>
|
||||
Create(nsPIDOMWindowInner* aWindow);
|
||||
Create(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aUrl,
|
||||
RefPtr<Promise>& aPromise);
|
||||
|
||||
virtual void DisconnectFromOwner() override;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
bool Equals(const uint64_t aWindowID, const nsAString& aUrl) const;
|
||||
|
||||
bool IsCachedValueReady();
|
||||
|
||||
void EnqueuePromise(RefPtr<Promise>& aPromise);
|
||||
|
||||
// WebIDL (public APIs)
|
||||
bool Value() const;
|
||||
|
||||
IMPL_EVENT_HANDLER(change);
|
||||
|
||||
private:
|
||||
explicit PresentationAvailability(nsPIDOMWindowInner* aWindow);
|
||||
explicit PresentationAvailability(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aUrl);
|
||||
|
||||
~PresentationAvailability();
|
||||
virtual ~PresentationAvailability();
|
||||
|
||||
bool Init();
|
||||
bool Init(RefPtr<Promise>& aPromise);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
void UpdateAvailabilityAndDispatchEvent(bool aIsAvailable);
|
||||
|
||||
bool mIsAvailable;
|
||||
|
||||
nsTArray<RefPtr<Promise>> mPromises;
|
||||
|
||||
nsString mUrl;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "PresentationRequest.h"
|
||||
|
||||
#include "AvailabilityCollection.h"
|
||||
#include "ControllerConnectionCollection.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/dom/PresentationRequestBinding.h"
|
||||
|
@ -23,13 +24,11 @@
|
|||
#include "nsServiceManagerUtils.h"
|
||||
#include "PresentationAvailability.h"
|
||||
#include "PresentationCallbacks.h"
|
||||
#include "PresentationLog.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(PresentationRequest, DOMEventTargetHelper,
|
||||
mAvailability)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(PresentationRequest, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(PresentationRequest, DOMEventTargetHelper)
|
||||
|
||||
|
@ -107,11 +106,6 @@ PresentationRequest::~PresentationRequest()
|
|||
bool
|
||||
PresentationRequest::Init()
|
||||
{
|
||||
mAvailability = PresentationAvailability::Create(GetOwner());
|
||||
if (NS_WARN_IF(!mAvailability)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -317,6 +311,8 @@ PresentationRequest::FindOrCreatePresentationConnection(
|
|||
already_AddRefed<Promise>
|
||||
PresentationRequest::GetAvailability(ErrorResult& aRv)
|
||||
{
|
||||
PRES_DEBUG("%s:id[%s]\n", __func__,
|
||||
NS_ConvertUTF16toUTF8(mUrl).get());
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
if (NS_WARN_IF(!global)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
|
@ -345,10 +341,52 @@ PresentationRequest::GetAvailability(ErrorResult& aRv)
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
promise->MaybeResolve(mAvailability);
|
||||
FindOrCreatePresentationAvailability(promise);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
PresentationRequest::FindOrCreatePresentationAvailability(RefPtr<Promise>& aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
|
||||
if (NS_WARN_IF(!GetOwner())) {
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
AvailabilityCollection* collection = AvailabilityCollection::GetSingleton();
|
||||
if (NS_WARN_IF(!collection)) {
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<PresentationAvailability> availability =
|
||||
collection->Find(GetOwner()->WindowID(), mUrl);
|
||||
|
||||
if (!availability) {
|
||||
availability = PresentationAvailability::Create(GetOwner(), mUrl, aPromise);
|
||||
} else {
|
||||
PRES_DEBUG(">resolve with same object:id[%s]\n",
|
||||
NS_ConvertUTF16toUTF8(mUrl).get());
|
||||
|
||||
// Fetching cached available devices is asynchronous in our implementation,
|
||||
// we need to ensure the promise is resolved in order.
|
||||
if (availability->IsCachedValueReady()) {
|
||||
aPromise->MaybeResolve(availability);
|
||||
return;
|
||||
}
|
||||
|
||||
availability->EnqueuePromise(aPromise);
|
||||
}
|
||||
|
||||
if (!availability) {
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationRequest::DispatchConnectionAvailableEvent(PresentationConnection* aConnection)
|
||||
{
|
||||
|
|
|
@ -22,8 +22,6 @@ class PresentationRequest final : public DOMEventTargetHelper
|
|||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationRequest,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
static already_AddRefed<PresentationRequest> Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aUrl,
|
||||
|
@ -58,6 +56,8 @@ private:
|
|||
void FindOrCreatePresentationConnection(const nsAString& aPresentationId,
|
||||
Promise* aPromise);
|
||||
|
||||
void FindOrCreatePresentationAvailability(RefPtr<Promise>& aPromise);
|
||||
|
||||
// Implement https://w3c.github.io/webappsec-mixed-content/#categorize-settings-object
|
||||
bool IsProhibitMixedSecurityContexts(nsIDocument* aDocument);
|
||||
|
||||
|
@ -65,7 +65,6 @@ private:
|
|||
bool IsPrioriAuthenticatedURL(const nsAString& aUrl);
|
||||
|
||||
nsString mUrl;
|
||||
RefPtr<PresentationAvailability> mAvailability;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -617,8 +617,8 @@ PresentationService::StartSession(const nsAString& aUrl,
|
|||
return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMutableArray> presentationUrls
|
||||
= do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
nsCOMPtr<nsIMutableArray> presentationUrls =
|
||||
do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!presentationUrls) {
|
||||
return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
|
@ -806,11 +806,14 @@ PresentationService::RegisterAvailabilityListener(nsIPresentationAvailabilityLis
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_WARN_IF(mAvailabilityListeners.Contains(aListener))) {
|
||||
return NS_OK;
|
||||
if (!mAvailabilityListeners.Contains(aListener)) {
|
||||
mAvailabilityListeners.AppendElement(aListener);
|
||||
}
|
||||
|
||||
mAvailabilityListeners.AppendElement(aListener);
|
||||
// Leverage availablility change notification to assign
|
||||
// the initial value of availability object.
|
||||
NS_WARN_IF(NS_FAILED(aListener->NotifyAvailableChange(mIsAvailable)));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -198,7 +198,7 @@ PresentationIPCService::UnregisterAvailabilityListener(nsIPresentationAvailabili
|
|||
MOZ_ASSERT(aListener);
|
||||
|
||||
mAvailabilityListeners.RemoveElement(aListener);
|
||||
if (sPresentationChild) {
|
||||
if (mAvailabilityListeners.IsEmpty() && sPresentationChild) {
|
||||
NS_WARN_IF(!sPresentationChild->SendUnregisterAvailabilityHandler());
|
||||
}
|
||||
return NS_OK;
|
||||
|
|
|
@ -32,6 +32,7 @@ EXPORTS.mozilla.dom += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AvailabilityCollection.cpp',
|
||||
'ControllerConnectionCollection.cpp',
|
||||
'DCPresentationChannelDescription.cpp',
|
||||
'ipc/PresentationBuilderChild.cpp',
|
||||
|
|
|
@ -73,3 +73,4 @@ skip-if = toolkit == 'android'
|
|||
[test_presentation_sandboxed_presentation.html]
|
||||
[test_presentation_reconnect.html]
|
||||
[test_presentation_mixed_security_contexts.html]
|
||||
[test_presentation_availability.html]
|
||||
|
|
|
@ -86,18 +86,19 @@ function testCreateRequest() {
|
|||
info('Sender: --- testCreateRequest ---');
|
||||
request = new PresentationRequest(receiverUrl);
|
||||
request.getAvailability().then((aAvailability) => {
|
||||
is(aAvailability.value, false, "Sender: should have no available device after setup");
|
||||
aAvailability.onchange = function() {
|
||||
aAvailability.onchange = null;
|
||||
ok(aAvailability.value, "Sender: Device should be available.");
|
||||
aResolve();
|
||||
}
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
}).catch((aError) => {
|
||||
ok(false, "Sender: Error occurred when getting availability: " + aError);
|
||||
teardown();
|
||||
aReject();
|
||||
});
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -89,18 +89,19 @@ function testCreateRequest() {
|
|||
info('Sender: --- testCreateRequest ---');
|
||||
request = new PresentationRequest("file_presentation_1ua_receiver.html");
|
||||
request.getAvailability().then((aAvailability) => {
|
||||
is(aAvailability.value, false, "Sender: should have no available device after setup");
|
||||
aAvailability.onchange = function() {
|
||||
aAvailability.onchange = null;
|
||||
ok(aAvailability.value, "Sender: Device should be available.");
|
||||
aResolve();
|
||||
}
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
}).catch((aError) => {
|
||||
ok(false, "Sender: Error occurred when getting availability: " + aError);
|
||||
teardown();
|
||||
aReject();
|
||||
});
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for PresentationAvailability</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1228508">Test PresentationAvailability</a>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
"use strict";
|
||||
|
||||
var testDevice = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
type: 'type',
|
||||
};
|
||||
|
||||
var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationDeviceInfoChromeScript.js'));
|
||||
var request;
|
||||
var availability;
|
||||
|
||||
function testSetup() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('setup-complete', function() {
|
||||
aResolve();
|
||||
});
|
||||
gScript.sendAsyncMessage('setup');
|
||||
});
|
||||
}
|
||||
|
||||
function testInitialUnavailable() {
|
||||
request = new PresentationRequest("https://example.com");
|
||||
|
||||
return request.getAvailability().then(function(aAvailability) {
|
||||
is(aAvailability.value, false, "Should have no available device after setup");
|
||||
aAvailability.onchange = function() {
|
||||
aAvailability.onchange = null;
|
||||
ok(aAvailability.value, "Device should be available.");
|
||||
}
|
||||
availability = aAvailability;
|
||||
gScript.sendAsyncMessage('trigger-device-add', testDevice);
|
||||
}).catch(function(aError) {
|
||||
ok(false, "Error occurred when getting availability: " + aError);
|
||||
teardown();
|
||||
});
|
||||
}
|
||||
|
||||
function testInitialAvailable() {
|
||||
let anotherRequest = new PresentationRequest("https://example.net");
|
||||
return anotherRequest.getAvailability().then(function(aAvailability) {
|
||||
is(aAvailability.value, true, "Should have available device initially");
|
||||
isnot(aAvailability, availability, "Should get different availability object for different request URL");
|
||||
}).catch(function(aError) {
|
||||
ok(false, "Error occurred when getting availability: " + aError);
|
||||
teardown();
|
||||
});
|
||||
}
|
||||
|
||||
function testSameObject() {
|
||||
let sameUrlRequest = new PresentationRequest("https://example.com");
|
||||
return sameUrlRequest.getAvailability().then(function(aAvailability) {
|
||||
is(aAvailability, availability, "Should get same availability object for same request URL");
|
||||
}).catch(function(aError) {
|
||||
ok(false, "Error occurred when getting availability: " + aError);
|
||||
teardown();
|
||||
});
|
||||
}
|
||||
|
||||
function testOnChangeEvent() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
availability.onchange = function() {
|
||||
availability.onchange = null;
|
||||
is(availability.value, false, "Should have no available device after device removed");
|
||||
aResolve();
|
||||
}
|
||||
gScript.sendAsyncMessage('trigger-device-remove');
|
||||
});
|
||||
}
|
||||
|
||||
function testConsecutiveGetAvailability() {
|
||||
let request = new PresentationRequest("https://example.org");
|
||||
let firstAvailabilityResolved = false;
|
||||
return Promise.all([
|
||||
request.getAvailability().then(function() {
|
||||
firstAvailabilityResolved = true;
|
||||
}),
|
||||
request.getAvailability().then(function() {
|
||||
ok(firstAvailabilityResolved, "getAvailability() should be resolved in sequence");
|
||||
})
|
||||
]).catch(function(aError) {
|
||||
ok(false, "Error occurred when getting availability: " + aError);
|
||||
teardown();
|
||||
});
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
request = null;
|
||||
availability = null;
|
||||
gScript.sendAsyncMessage('teardown');
|
||||
gScript.destroy();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
testSetup().then(testInitialUnavailable)
|
||||
.then(testInitialAvailable)
|
||||
.then(testSameObject)
|
||||
.then(testOnChangeEvent)
|
||||
.then(testConsecutiveGetAvailability)
|
||||
.then(teardown);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: "presentation-device-manage", allow: false, context: document},
|
||||
], function() {
|
||||
SpecialPowers.pushPrefEnv({ "set": [["dom.presentation.enabled", true],
|
||||
["dom.presentation.controller.enabled", true],
|
||||
["dom.presentation.session_transport.data_channel.enable", false]]},
|
||||
runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -26,11 +26,14 @@ function testSetup() {
|
|||
|
||||
request.getAvailability().then(
|
||||
function(aAvailability) {
|
||||
is(aAvailability.value, false, "Sender: should have no available device after setup");
|
||||
aAvailability.onchange = function() {
|
||||
aAvailability.onchange = null;
|
||||
ok(aAvailability.value, "Device should be available.");
|
||||
aResolve();
|
||||
}
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when getting availability: " + aError);
|
||||
|
@ -38,8 +41,6 @@ function testSetup() {
|
|||
aReject();
|
||||
}
|
||||
);
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -48,11 +48,14 @@ function testSetup() {
|
|||
|
||||
request.getAvailability().then(
|
||||
function(aAvailability) {
|
||||
is(aAvailability.value, false, "Sender: should have no available device after setup");
|
||||
aAvailability.onchange = function() {
|
||||
aAvailability.onchange = null;
|
||||
ok(aAvailability.value, "Device should be available.");
|
||||
aResolve();
|
||||
}
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when getting availability: " + aError);
|
||||
|
@ -60,8 +63,6 @@ function testSetup() {
|
|||
aReject();
|
||||
}
|
||||
);
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -24,11 +24,14 @@ function testSetup() {
|
|||
|
||||
request.getAvailability().then(
|
||||
function(aAvailability) {
|
||||
is(aAvailability.value, false, "Sender: should have no available device after setup");
|
||||
aAvailability.onchange = function() {
|
||||
aAvailability.onchange = null;
|
||||
ok(aAvailability.value, "Device should be available.");
|
||||
aResolve();
|
||||
}
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when getting availability: " + aError);
|
||||
|
@ -36,8 +39,6 @@ function testSetup() {
|
|||
aReject();
|
||||
}
|
||||
);
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -24,11 +24,14 @@ function testSetup() {
|
|||
|
||||
request.getAvailability().then(
|
||||
function(aAvailability) {
|
||||
is(aAvailability.value, false, "Sender: should have no available device after setup");
|
||||
aAvailability.onchange = function() {
|
||||
aAvailability.onchange = null;
|
||||
ok(aAvailability.value, "Device should be available.");
|
||||
aResolve();
|
||||
}
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when getting availability: " + aError);
|
||||
|
@ -36,8 +39,6 @@ function testSetup() {
|
|||
aReject();
|
||||
}
|
||||
);
|
||||
|
||||
gScript.sendAsyncMessage('trigger-device-add');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче