Merge m-c to b2g-inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2016-01-17 15:09:28 -05:00
Родитель 782fe3dd3f 0a405f4d7d
Коммит 5103656a34
491 изменённых файлов: 13897 добавлений и 7474 удалений

Просмотреть файл

@ -169,12 +169,10 @@ toolkit/content/contentAreaUtils.js
toolkit/content/widgets/videocontrols.xml
toolkit/components/jsdownloads/src/DownloadIntegration.jsm
toolkit/components/search/nsSearchService.js
toolkit/components/telemetry/healthreport-prefs.js
toolkit/components/url-classifier/**
toolkit/components/urlformatter/nsURLFormatter.js
toolkit/identity/FirefoxAccounts.jsm
toolkit/modules/AppConstants.jsm
toolkit/modules/SessionRecorder.jsm
toolkit/mozapps/downloads/nsHelperAppDlg.js
toolkit/mozapps/extensions/internal/AddonConstants.jsm
toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js

Просмотреть файл

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1133073 - Use PR_DuplicateEnvironment from NSPR and remove interim mozglue wrappers.
Bug 1228641 - Remove initializer_list from config/stl-headers

Просмотреть файл

@ -111,6 +111,7 @@ function addWindowTab(window, tabElement) {
if (window)
addListItem(window.tabs, tab);
addListItem(allTabs, tab);
emit(allTabs, "open", tab);
}
// Find tabs in already open windows

Просмотреть файл

@ -1241,6 +1241,21 @@ exports["test ready event after window.open"] = function (assert, done) {
});
}
// related to bug #939496
exports["test tab open event for new window"] = function(assert, done) {
// ensure popups open in a new window and disable popup blocker
setPref(OPEN_IN_NEW_WINDOW_PREF, 2);
setPref(DISABLE_POPUP_PREF, false);
tabs.once('open', function onOpen(window) {
assert.pass("tab open has occured");
window.close(done);
});
// open window to trigger observers
browserWindows.open("about:logo");
};
after(exports, function*(name, assert) {
resetPopupPrefs();
yield cleanUI();

Просмотреть файл

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1452200089000">
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1452810773000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
@ -2532,6 +2532,12 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i1077" id="helper@vidscrab.com">
<versionRange minVersion="0" maxVersion="*" severity="1">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i15" id="personas@christopher.beard">
<versionRange minVersion="1.6" maxVersion="1.6">

Просмотреть файл

@ -157,7 +157,7 @@ function messageIsCSSError(msg) {
return false;
}
add_task(function checkAllTheCSS() {
add_task(function* checkAllTheCSS() {
let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
// This asynchronously produces a list of URLs (sadly, mostly sync on our
// test infrastructure because it runs against jarfiles there, and
@ -236,4 +236,6 @@ add_task(function checkAllTheCSS() {
doc.head.innerHTML = '';
doc = null;
iframe = null;
windowless.close();
windowless = null;
});

Просмотреть файл

@ -10,6 +10,9 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Components.utils.import("resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
@ -454,15 +457,10 @@ function gatherTextUnder ( root )
return text;
}
// This function exists for legacy reasons.
function getShellService()
{
var shell = null;
try {
shell = Components.classes["@mozilla.org/browser/shell-service;1"]
.getService(Components.interfaces.nsIShellService);
} catch (e) {
}
return shell;
return ShellService;
}
function isBidiEnabled() {

Просмотреть файл

@ -1342,7 +1342,9 @@ var CustomizableUIInternal = {
}
let tooltip = this.getLocalizedProperty(aWidget, "tooltiptext", additionalTooltipArguments);
node.setAttribute("tooltiptext", tooltip);
if (tooltip) {
node.setAttribute("tooltiptext", tooltip);
}
node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional");
let commandHandler = this.handleWidgetCommand.bind(this, aWidget, node);
@ -1385,6 +1387,8 @@ var CustomizableUIInternal = {
},
getLocalizedProperty: function(aWidget, aProp, aFormatArgs, aDef) {
const kReqStringProps = ["label"];
if (typeof aWidget == "string") {
aWidget = gPalette.get(aWidget);
}
@ -1395,7 +1399,7 @@ var CustomizableUIInternal = {
// Let widgets pass their own string identifiers or strings, so that
// we can use strings which aren't the default (in case string ids change)
// and so that non-builtin-widgets can also provide labels, tooltips, etc.
if (aWidget[aProp]) {
if (aWidget[aProp] != null) {
name = aWidget[aProp];
// By using this as the default, if a widget provides a full string rather
// than a string ID for localization, we will fall back to that string
@ -1412,7 +1416,9 @@ var CustomizableUIInternal = {
}
return gWidgetsBundle.GetStringFromName(name) || def;
} catch(ex) {
if (!def) {
// If an empty string was explicitly passed, treat it as an actual
// value rather than a missing property.
if (!def && (name != "" || kReqStringProps.includes(aProp))) {
ERROR("Could not localize property '" + name + "'.");
}
}
@ -2336,6 +2342,7 @@ var CustomizableUIInternal = {
this.wrapWidgetEventHandler("onBeforeCreated", widget);
this.wrapWidgetEventHandler("onClick", widget);
this.wrapWidgetEventHandler("onCreated", widget);
this.wrapWidgetEventHandler("onDestroyed", widget);
if (widget.type == "button") {
widget.onCommand = typeof aData.onCommand == "function" ?
@ -2439,6 +2446,9 @@ var CustomizableUIInternal = {
}
}
}
if (widgetNode && widget.onDestroyed) {
widget.onDestroyed(window.document);
}
}
gPalette.delete(aWidgetId);
@ -3172,6 +3182,11 @@ this.CustomizableUI = {
* - onCreated(aNode): Attached to all widgets; a function that will be invoked
* whenever the widget has a DOM node constructed, passing the
* constructed node as an argument.
* - onDestroyed(aDoc): Attached to all non-custom widgets; a function that
* will be invoked after the widget has a DOM node destroyed,
* passing the document from which it was removed. This is
* useful especially for 'view' type widgets that need to
* cleanup after views that were constructed on the fly.
* - onCommand(aEvt): Only useful for button widgets; a function that will be
* invoked when the user activates the button.
* - onClick(aEvt): Attached to all widgets; a function that will be invoked

Просмотреть файл

@ -329,6 +329,7 @@ const PanelUI = {
evt.initCustomEvent("ViewShowing", true, true, viewNode);
viewNode.dispatchEvent(evt);
if (evt.defaultPrevented) {
aAnchor.open = false;
return;
}

Просмотреть файл

@ -326,6 +326,12 @@
]]></body>
</method>
<method name="_shouldSetPosition">
<body><![CDATA[
return this.getAttribute("nosubviews") == "true";
]]></body>
</method>
<method name="_shouldSetHeight">
<body><![CDATA[
return this.getAttribute("nosubviews") != "true";
@ -345,6 +351,19 @@
this.ignoreMutations = false;
]]></body>
</method>
<method name="_adjustContainerHeight">
<body><![CDATA[
if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) {
let height;
if (this.showingSubViewAsMainView) {
height = this._heightOfSubview(this._mainView);
} else {
height = this._mainView.scrollHeight;
}
this._viewContainer.style.height = height + "px";
}
]]></body>
</method>
<method name="_syncContainerWithSubView">
<body><![CDATA[
// Check that this panel is still alive:
@ -361,18 +380,16 @@
<method name="_syncContainerWithMainView">
<body><![CDATA[
// Check that this panel is still alive:
if (!this._panel || !this._panel.parentNode || !this._shouldSetHeight()) {
if (!this._panel || !this._panel.parentNode) {
return;
}
if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) {
let height;
if (this.showingSubViewAsMainView) {
height = this._heightOfSubview(this._mainView);
} else {
height = this._mainView.scrollHeight;
}
this._viewContainer.style.height = height + "px";
if (this._shouldSetPosition()) {
this._panel.adjustArrowPosition();
}
if (this._shouldSetHeight()) {
this._adjustContainerHeight();
}
]]></body>
</method>

Просмотреть файл

@ -2,13 +2,14 @@
"extends": "../../../toolkit/components/extensions/.eslintrc",
"globals": {
"AllWindowEvents": true,
"currentWindow": true,
"EventEmitter": true,
"IconDetails": true,
"openPanel": true,
"makeWidgetId": true,
"PanelPopup": true,
"TabContext": true,
"AllWindowEvents": true,
"ViewPopup": true,
"WindowEventManager": true,
"WindowListManager": true,
"WindowManager": true,

Просмотреть файл

@ -13,6 +13,8 @@ var {
runSafe,
} = ExtensionUtils;
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
// WeakMap[Extension -> BrowserAction]
var browserActionMap = new WeakMap();
@ -24,7 +26,10 @@ function browserActionOf(extension) {
// as the associated popup.
function BrowserAction(options, extension) {
this.extension = extension;
this.id = makeWidgetId(extension.id) + "-browser-action";
let widgetId = makeWidgetId(extension.id);
this.id = `${widgetId}-browser-action`;
this.viewId = `PanelUI-webext-${widgetId}-browser-action-view`;
this.widget = null;
this.tabManager = TabManager.for(extension);
@ -37,7 +42,7 @@ function BrowserAction(options, extension) {
this.defaults = {
enabled: true,
title: title,
title: title || extension.name,
badgeText: "",
badgeBackgroundColor: null,
icon: IconDetails.normalize({ path: options.default_icon }, extension,
@ -55,31 +60,60 @@ BrowserAction.prototype = {
build() {
let widget = CustomizableUI.createWidget({
id: this.id,
type: "custom",
viewId: this.viewId,
type: "view",
removable: true,
label: this.defaults.title || this.extension.name,
tooltiptext: this.defaults.title || "",
defaultArea: CustomizableUI.AREA_NAVBAR,
onBuild: document => {
let node = document.createElement("toolbarbutton");
node.id = this.id;
node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional badged-button");
onBeforeCreated: document => {
let view = document.createElementNS(XUL_NS, "panelview");
view.id = this.viewId;
view.setAttribute("flex", "1");
document.getElementById("PanelUI-multiView").appendChild(view);
},
onDestroyed: document => {
let view = document.getElementById(this.viewId);
if (view) {
view.remove();
}
},
onCreated: node => {
node.classList.add("badged-button");
node.setAttribute("constrain-size", "true");
this.updateButton(node, this.defaults);
},
onViewShowing: event => {
let document = event.target.ownerDocument;
let tabbrowser = document.defaultView.gBrowser;
node.addEventListener("command", event => { // eslint-disable-line mozilla/balanced-listeners
let tab = tabbrowser.selectedTab;
let popup = this.getProperty(tab, "popup");
this.tabManager.addActiveTabPermission(tab);
if (popup) {
this.togglePopup(node, popup);
} else {
this.emit("click");
}
});
let tab = tabbrowser.selectedTab;
let popupURL = this.getProperty(tab, "popup");
this.tabManager.addActiveTabPermission(tab);
return node;
// If the widget has a popup URL defined, we open a popup, but do not
// dispatch a click event to the extension.
// If it has no popup URL defined, we dispatch a click event, but do not
// open a popup.
if (popupURL) {
try {
new ViewPopup(this.extension, event.target, popupURL);
} catch (e) {
Cu.reportError(e);
event.preventDefault();
}
} else {
// This isn't not a hack, but it seems to provide the correct behavior
// with the fewest complications.
event.preventDefault();
this.emit("click");
}
},
});
@ -89,22 +123,12 @@ BrowserAction.prototype = {
this.widget = widget;
},
togglePopup(node, popupResource) {
openPanel(node, popupResource, this.extension);
},
// Update the toolbar button |node| with the tab context data
// in |tabData|.
updateButton(node, tabData) {
if (tabData.title) {
node.setAttribute("tooltiptext", tabData.title);
node.setAttribute("label", tabData.title);
node.setAttribute("aria-label", tabData.title);
} else {
node.removeAttribute("tooltiptext");
node.removeAttribute("label");
node.removeAttribute("aria-label");
}
let title = tabData.title || this.extension.name;
node.setAttribute("tooltiptext", title);
node.setAttribute("label", title);
if (tabData.badgeText) {
node.setAttribute("badge", tabData.badgeText);
@ -162,8 +186,10 @@ BrowserAction.prototype = {
setProperty(tab, prop, value) {
if (tab == null) {
this.defaults[prop] = value;
} else {
} else if (value != null) {
this.tabContext.get(tab)[prop] = value;
} else {
delete this.tabContext.get(tab)[prop];
}
this.updateOnChange(tab);
@ -226,7 +252,13 @@ extensions.registerSchemaAPI("browserAction", null, (extension, context) => {
setTitle: function(details) {
let tab = details.tabId !== null ? TabManager.getTab(details.tabId) : null;
browserActionOf(extension).setProperty(tab, "title", details.title);
let title = details.title;
// Clear the tab-specific title when given a null string.
if (tab && title == "") {
title = null;
}
browserActionOf(extension).setProperty(tab, "title", title);
},
getTitle: function(details, callback) {

Просмотреть файл

@ -28,7 +28,7 @@ function PageAction(options, extension) {
this.defaults = {
show: false,
title: title,
title: title || extension.name,
icon: IconDetails.normalize({ path: options.default_icon }, extension,
null, true),
popup: popup && extension.baseURI.resolve(popup),
@ -58,7 +58,12 @@ PageAction.prototype = {
// If |tab| is currently selected, updates the page action button to
// reflect the new value.
setProperty(tab, prop, value) {
this.tabContext.get(tab)[prop] = value;
if (value != null) {
this.tabContext.get(tab)[prop] = value;
} else {
delete this.tabContext.get(tab)[prop];
}
if (tab.selected) {
this.updateButton(tab.ownerDocument.defaultView);
}
@ -84,13 +89,9 @@ PageAction.prototype = {
if (tabData.show) {
// Update the title and icon only if the button is visible.
if (tabData.title) {
button.setAttribute("tooltiptext", tabData.title);
button.setAttribute("aria-label", tabData.title);
} else {
button.removeAttribute("tooltiptext");
button.removeAttribute("aria-label");
}
let title = tabData.title || this.extension.name;
button.setAttribute("tooltiptext", title);
button.setAttribute("aria-label", title);
let icon = IconDetails.getURL(tabData.icon, window, this.extension);
button.setAttribute("src", icon);
@ -137,12 +138,16 @@ PageAction.prototype = {
// the any click listeners in the add-on.
handleClick(window) {
let tab = window.gBrowser.selectedTab;
let popup = this.tabContext.get(tab).popup;
let popupURL = this.tabContext.get(tab).popup;
this.tabManager.addActiveTabPermission(tab);
if (popup) {
openPanel(this.getButton(window), popup, this.extension);
// If the widget has a popup URL defined, we open a popup, but do not
// dispatch a click event to the extension.
// If it has no popup URL defined, we dispatch a click event, but do not
// open a popup.
if (popupURL) {
new PanelPopup(this.extension, this.getButton(window), popupURL);
} else {
this.emit("click", tab);
}
@ -213,7 +218,9 @@ extensions.registerSchemaAPI("pageAction", null, (extension, context) => {
setTitle(details) {
let tab = TabManager.getTab(details.tabId);
PageAction.for(extension).setProperty(tab, "title", details.title);
// Clear the tab-specific title when given a null string.
PageAction.for(extension).setProperty(tab, "title", details.title || null);
},
getTitle(details, callback) {

Просмотреть файл

@ -2,12 +2,16 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
Cu.import("resource://gre/modules/AddonManager.jsm");
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const INTEGER = /^[1-9]\d*$/;
var {
@ -126,103 +130,203 @@ global.makeWidgetId = id => {
return id.replace(/[^a-z0-9_-]/g, "_");
};
// Open a panel anchored to the given node, containing a browser opened
// to the given URL, owned by the given extension. If |popupURL| is not
// an absolute URL, it is resolved relative to the given extension's
// base URL.
global.openPanel = (node, popupURL, extension) => {
let document = node.ownerDocument;
class BasePopup {
constructor(extension, viewNode, popupURL) {
let popupURI = Services.io.newURI(popupURL, null, extension.baseURI);
let popupURI = Services.io.newURI(popupURL, null, extension.baseURI);
Services.scriptSecurityManager.checkLoadURIWithPrincipal(
extension.principal, popupURI,
Services.scriptSecurityManager.DISALLOW_SCRIPT);
Services.scriptSecurityManager.checkLoadURIWithPrincipal(
extension.principal, popupURI,
Services.scriptSecurityManager.DISALLOW_SCRIPT);
this.extension = extension;
this.popupURI = popupURI;
this.viewNode = viewNode;
this.window = viewNode.ownerDocument.defaultView;
let panel = document.createElement("panel");
panel.setAttribute("id", makeWidgetId(extension.id) + "-panel");
panel.setAttribute("class", "browser-extension-panel");
panel.setAttribute("type", "arrow");
panel.setAttribute("role", "group");
this.contentReady = new Promise(resolve => {
this._resolveContentReady = resolve;
});
let anchor;
if (node.localName == "toolbarbutton") {
// Toolbar buttons are a special case. The panel becomes a child of
// the button, and is anchored to the button's icon.
node.appendChild(panel);
anchor = document.getAnonymousElementByAttribute(node, "class", "toolbarbutton-icon");
} else {
// In all other cases, the panel is anchored to the target node
// itself, and is a child of a popupset node.
document.getElementById("mainPopupSet").appendChild(panel);
anchor = node;
this.viewNode.addEventListener(this.DESTROY_EVENT, this);
this.browser = null;
this.browserReady = this.createBrowser(viewNode, popupURI);
}
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let browser = document.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
panel.appendChild(browser);
destroy() {
this.browserReady.then(() => {
this.browser.removeEventListener("load", this, true);
this.browser.removeEventListener("DOMTitleChanged", this, true);
this.browser.removeEventListener("DOMWindowClose", this, true);
let titleChangedListener = () => {
panel.setAttribute("aria-label", browser.contentTitle);
};
this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
let context;
let popuphidden = () => {
panel.removeEventListener("popuphidden", popuphidden);
browser.removeEventListener("DOMTitleChanged", titleChangedListener, true);
context.unload();
panel.remove();
};
panel.addEventListener("popuphidden", popuphidden);
this.context.unload();
this.browser.remove();
let loadListener = () => {
panel.removeEventListener("load", loadListener);
context = new ExtensionPage(extension, {
type: "popup",
contentWindow: browser.contentWindow,
uri: popupURI,
docShell: browser.docShell,
this.browser = null;
this.viewNode = null;
this.context = null;
});
GlobalManager.injectInDocShell(browser.docShell, extension, context);
browser.setAttribute("src", context.uri.spec);
}
let contentLoadListener = event => {
if (event.target != browser.contentDocument) {
return;
}
browser.removeEventListener("load", contentLoadListener, true);
// Returns the name of the event fired on `viewNode` when the popup is being
// destroyed. This must be implemented by every subclass.
get DESTROY_EVENT() {
throw new Error("Not implemented");
}
let contentViewer = browser.docShell.contentViewer;
let width = {}, height = {};
try {
contentViewer.getContentSize(width, height);
[width, height] = [width.value, height.value];
} catch (e) {
// getContentSize can throw
[width, height] = [400, 400];
}
handleEvent(event) {
switch (event.type) {
case this.DESTROY_EVENT:
this.destroy();
break;
let window = document.defaultView;
width /= window.devicePixelRatio;
height /= window.devicePixelRatio;
width = Math.min(width, 800);
height = Math.min(height, 800);
case "DOMWindowClose":
if (event.target === this.browser.contentWindow) {
event.preventDefault();
this.closePopup();
}
break;
browser.setAttribute("width", width);
browser.setAttribute("height", height);
case "DOMTitleChanged":
this.viewNode.setAttribute("aria-label", this.browser.contentTitle);
break;
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
};
browser.addEventListener("load", contentLoadListener, true);
case "load":
// We use a capturing listener, so we get this event earlier than any
// load listeners in the content page. Resizing after a timeout ensures
// that we calculate the size after the entire event cycle has completed
// (unless someone spins the event loop, anyway), and hopefully after
// the content has made any modifications.
//
// In the future, to match Chrome's behavior, we'll need to update this
// dynamically, probably in response to MozScrolledAreaChanged events.
this.window.setTimeout(() => this.resizeBrowser(), 0);
break;
}
}
browser.addEventListener("DOMTitleChanged", titleChangedListener, true);
};
panel.addEventListener("load", loadListener);
createBrowser(viewNode, popupURI) {
let document = viewNode.ownerDocument;
return panel;
this.browser = document.createElementNS(XUL_NS, "browser");
this.browser.setAttribute("type", "content");
this.browser.setAttribute("disableglobalhistory", "true");
// Note: When using noautohide panels, the popup manager will add width and
// height attributes to the panel, breaking our resize code, if the browser
// starts out smaller than 30px by 10px. This isn't an issue now, but it
// will be if and when we popup debugging.
// This overrides the content's preferred size when displayed in a
// fixed-size, slide-in panel.
this.browser.setAttribute("flex", "1");
viewNode.appendChild(this.browser);
return new Promise(resolve => {
// The first load event is for about:blank.
// We can't finish setting up the browser until the binding has fully
// initialized. Waiting for the first load event guarantees that it has.
let loadListener = event => {
this.browser.removeEventListener("load", loadListener, true);
resolve();
};
this.browser.addEventListener("load", loadListener, true);
}).then(() => {
let { contentWindow } = this.browser;
contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.allowScriptsToClose();
this.context = new ExtensionPage(this.extension, {
type: "popup",
contentWindow,
uri: popupURI,
docShell: this.browser.docShell,
});
GlobalManager.injectInDocShell(this.browser.docShell, this.extension, this.context);
this.browser.setAttribute("src", this.context.uri.spec);
this.browser.addEventListener("load", this, true);
this.browser.addEventListener("DOMTitleChanged", this, true);
this.browser.addEventListener("DOMWindowClose", this, true);
});
}
// Resizes the browser to match the preferred size of the content.
resizeBrowser() {
let width, height;
try {
let w = {}, h = {};
this.browser.docShell.contentViewer.getContentSize(w, h);
width = w.value / this.window.devicePixelRatio;
height = h.value / this.window.devicePixelRatio;
// The width calculation is imperfect, and is often a fraction of a pixel
// too narrow, even after taking the ceiling, which causes lines of text
// to wrap.
width += 1;
} catch (e) {
// getContentSize can throw
[width, height] = [400, 400];
}
width = Math.ceil(Math.min(width, 800));
height = Math.ceil(Math.min(height, 600));
this.browser.style.width = `${width}px`;
this.browser.style.height = `${height}px`;
this._resolveContentReady();
}
}
global.PanelPopup = class PanelPopup extends BasePopup {
constructor(extension, imageNode, popupURL) {
let document = imageNode.ownerDocument;
let panel = document.createElement("panel");
panel.setAttribute("id", makeWidgetId(extension.id) + "-panel");
panel.setAttribute("class", "browser-extension-panel");
panel.setAttribute("type", "arrow");
panel.setAttribute("role", "group");
document.getElementById("mainPopupSet").appendChild(panel);
super(extension, panel, popupURL);
this.contentReady.then(() => {
panel.openPopup(imageNode, "bottomcenter topright", 0, 0, false, false);
});
}
get DESTROY_EVENT() {
return "popuphidden";
}
destroy() {
super.destroy();
this.viewNode.remove();
}
closePopup() {
this.viewNode.hidePopup();
}
};
global.ViewPopup = class ViewPopup extends BasePopup {
get DESTROY_EVENT() {
return "ViewHiding";
}
closePopup() {
CustomizableUI.hidePanelForNode(this.viewNode);
}
};
// Manages tab-specific context data, and dispatching tab select events

Просмотреть файл

@ -10,6 +10,9 @@
"XPCOMUtils": true,
"Task": true,
// Browser window globals.
"PanelUI": false,
// Test harness globals
"ExtensionTestUtils": false,

Просмотреть файл

@ -2,8 +2,141 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* testTabSwitchContext() {
function* runTests(options) {
function background(getTests) {
// Gets the current details of the browser action, and returns a
// promise that resolves to an object containing them.
function getDetails(tabId) {
return Promise.all([
new Promise(resolve => browser.browserAction.getTitle({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getPopup({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getBadgeText({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getBadgeBackgroundColor({tabId}, resolve))]
).then(details => {
return Promise.resolve({ title: details[0],
popup: details[1],
badge: details[2],
badgeBackgroundColor: details[3] });
});
}
function checkDetails(expecting, tabId) {
return getDetails(tabId).then(details => {
browser.test.assertEq(expecting.title, details.title,
"expected value from getTitle");
browser.test.assertEq(expecting.popup, details.popup,
"expected value from getPopup");
browser.test.assertEq(expecting.badge, details.badge,
"expected value from getBadge");
browser.test.assertEq(String(expecting.badgeBackgroundColor),
String(details.badgeBackgroundColor),
"expected value from getBadgeBackgroundColor");
});
}
let expectDefaults = expecting => {
return checkDetails(expecting);
};
let tabs = [];
let tests = getTests(tabs, expectDefaults);
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(expecting => {
// Check that the API returns the expected values, and then
// run the next test.
new Promise(resolve => {
return browser.tabs.query({ active: true, currentWindow: true }, resolve);
}).then(tabs => {
return checkDetails(expecting, tabs[0].id);
}).then(() => {
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, tests.length);
});
});
}
browser.test.onMessage.addListener((msg) => {
if (msg != "runNextTest") {
browser.test.fail("Expecting 'runNextTest' message");
}
nextTest();
});
browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
tabs[0] = resultTabs[0].id;
nextTest();
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: options.manifest,
background: `(${background})(${options.getTests})`,
});
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
function checkDetails(details) {
let button = document.getElementById(browserActionId);
ok(button, "button exists");
let title = details.title || options.manifest.name;
is(button.getAttribute("image"), details.icon, "icon URL is correct");
is(button.getAttribute("tooltiptext"), title, "image title is correct");
is(button.getAttribute("label"), title, "image label is correct");
is(button.getAttribute("badge"), details.badge, "badge text is correct");
is(button.getAttribute("disabled") == "true", Boolean(details.disabled), "disabled state is correct");
if (details.badge && details.badgeBackgroundColor) {
let badge = button.ownerDocument.getAnonymousElementByAttribute(
button, "class", "toolbarbutton-badge");
let badgeColor = window.getComputedStyle(badge).backgroundColor;
let color = details.badgeBackgroundColor;
let expectedColor = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
is(badgeColor, expectedColor, "badge color is correct");
}
// TODO: Popup URL.
}
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else {
resolve();
}
});
});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
}
add_task(function* testTabSwitchContext() {
yield runTests({
manifest: {
"browser_action": {
"default_icon": "default.png",
@ -13,7 +146,7 @@ add_task(function* testTabSwitchContext() {
"permissions": ["tabs"],
},
background: function() {
getTests(tabs, expectDefaults) {
let details = [
{ "icon": browser.runtime.getURL("default.png"),
"popup": browser.runtime.getURL("default.html"),
@ -50,10 +183,7 @@ add_task(function* testTabSwitchContext() {
"badgeBackgroundColor": [0, 0xff, 0, 0xff] },
];
let tabs = [];
let expectDefaults;
let tests = [
return [
expect => {
browser.test.log("Initial state, expect default properties.");
expectDefaults(details[0]).then(() => {
@ -157,124 +287,82 @@ add_task(function* testTabSwitchContext() {
});
},
];
// Gets the current details of the browser action, and returns a
// promise that resolves to an object containing them.
function getDetails(tabId) {
return Promise.all([
new Promise(resolve => browser.browserAction.getTitle({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getPopup({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getBadgeText({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getBadgeBackgroundColor({tabId}, resolve))]
).then(details => {
return Promise.resolve({ title: details[0],
popup: details[1],
badge: details[2],
badgeBackgroundColor: details[3] });
});
}
function checkDetails(expecting, tabId) {
return getDetails(tabId).then(details => {
browser.test.assertEq(expecting.title, details.title,
"expected value from getTitle");
browser.test.assertEq(expecting.popup, details.popup,
"expected value from getPopup");
browser.test.assertEq(expecting.badge, details.badge,
"expected value from getBadge");
browser.test.assertEq(String(expecting.badgeBackgroundColor),
String(details.badgeBackgroundColor),
"expected value from getBadgeBackgroundColor");
});
}
expectDefaults = expecting => {
return checkDetails(expecting);
};
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(expecting => {
// Check that the API returns the expected values, and then
// run the next test.
new Promise(resolve => {
return browser.tabs.query({ active: true, currentWindow: true }, resolve);
}).then(tabs => {
return checkDetails(expecting, tabs[0].id);
}).then(() => {
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, tests.length);
});
});
}
browser.test.onMessage.addListener((msg) => {
if (msg != "runNextTest") {
browser.test.fail("Expecting 'runNextTest' message");
}
nextTest();
});
browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
tabs[0] = resultTabs[0].id;
nextTest();
});
},
});
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
function checkDetails(details) {
let button = document.getElementById(browserActionId);
ok(button, "button exists");
is(button.getAttribute("image"), details.icon, "icon URL is correct");
is(button.getAttribute("tooltiptext"), details.title, "image title is correct");
is(button.getAttribute("label"), details.title, "image label is correct");
is(button.getAttribute("aria-label"), details.title, "image aria-label is correct");
is(button.getAttribute("badge"), details.badge, "badge text is correct");
is(button.getAttribute("disabled") == "true", Boolean(details.disabled), "disabled state is correct");
if (details.badge && details.badgeBackgroundColor) {
let badge = button.ownerDocument.getAnonymousElementByAttribute(
button, "class", "toolbarbutton-badge");
let badgeColor = window.getComputedStyle(badge).backgroundColor;
let color = details.badgeBackgroundColor;
let expectedColor = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
is(badgeColor, expectedColor, "badge color is correct");
}
// TODO: Popup URL.
}
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else {
resolve();
}
});
});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
});
add_task(function* testDefaultTitle() {
yield runTests({
manifest: {
"name": "Foo Extension",
"browser_action": {
"default_icon": "icon.png",
},
"permissions": ["tabs"],
},
getTests(tabs, expectDefaults) {
let details = [
{ "title": "Foo Extension",
"popup": "",
"badge": "",
"badgeBackgroundColor": null,
"icon": browser.runtime.getURL("icon.png") },
{ "title": "Foo Title",
"popup": "",
"badge": "",
"badgeBackgroundColor": null,
"icon": browser.runtime.getURL("icon.png") },
{ "title": "Bar Title",
"popup": "",
"badge": "",
"badgeBackgroundColor": null,
"icon": browser.runtime.getURL("icon.png") },
{ "title": "",
"popup": "",
"badge": "",
"badgeBackgroundColor": null,
"icon": browser.runtime.getURL("icon.png") },
];
return [
expect => {
browser.test.log("Initial state. Expect extension title as default title.");
expectDefaults(details[0]).then(() => {
expect(details[0]);
});
},
expect => {
browser.test.log("Change the title. Expect new title.");
browser.browserAction.setTitle({ tabId: tabs[0], title: "Foo Title" });
expectDefaults(details[0]).then(() => {
expect(details[1]);
});
},
expect => {
browser.test.log("Change the default. Expect same properties.");
browser.browserAction.setTitle({ title: "Bar Title" });
expectDefaults(details[2]).then(() => {
expect(details[1]);
});
},
expect => {
browser.test.log("Clear the title. Expect new default title.");
browser.browserAction.setTitle({ tabId: tabs[0], title: "" });
expectDefaults(details[2]).then(() => {
expect(details[2]);
});
},
expect => {
browser.test.log("Set default title to null string. Expect null string from API, extension title in UI.");
browser.browserAction.setTitle({ title: "" });
expectDefaults(details[3]).then(() => {
expect(details[3]);
});
},
];
},
});
});

Просмотреть файл

@ -2,21 +2,7 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
function promisePopupShown(popup) {
return new Promise(resolve => {
if (popup.popupOpen) {
resolve();
} else {
let onPopupShown = event => {
popup.removeEventListener("popupshown", onPopupShown);
resolve();
};
popup.addEventListener("popupshown", onPopupShown);
}
});
}
add_task(function* testPageActionPopup() {
function* testInArea(area) {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"background": {
@ -116,30 +102,34 @@ add_task(function* testPageActionPopup() {
},
});
let panelId = makeWidgetId(extension.id) + "-panel";
extension.onMessage("send-click", () => {
clickBrowserAction(extension);
});
let widget;
extension.onMessage("next-test", Task.async(function* () {
let panel = document.getElementById(panelId);
if (panel) {
yield promisePopupShown(panel);
panel.hidePopup();
panel = document.getElementById(panelId);
is(panel, null, "panel successfully removed from document after hiding");
if (!widget) {
widget = getBrowserActionWidget(extension);
CustomizableUI.addWidgetToArea(widget.id, area);
}
yield closeBrowserAction(extension);
extension.sendMessage("next-test");
}));
yield Promise.all([extension.startup(), extension.awaitFinish("browseraction-tests-done")]);
yield extension.unload();
let panel = document.getElementById(panelId);
is(panel, null, "browserAction panel removed from document");
let view = document.getElementById(widget.viewId);
is(view, null, "browserAction view removed from document");
}
add_task(function* testBrowserActionInToolbar() {
yield testInArea(CustomizableUI.AREA_NAVBAR);
});
add_task(function* testBrowserActionInPanel() {
yield testInArea(CustomizableUI.AREA_PANEL);
});

Просмотреть файл

@ -33,23 +33,13 @@ add_task(function* () {
yield extension.startup();
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(window).node;
// Do this a few times to make sure the pop-up is reloaded each time.
for (let i = 0; i < 3; i++) {
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true,
});
node.dispatchEvent(evt);
clickBrowserAction(extension);
yield extension.awaitMessage("popup");
let panel = node.querySelector("panel");
if (panel) {
panel.hidePopup();
}
closeBrowserAction(extension);
}
yield extension.unload();

Просмотреть файл

@ -110,23 +110,13 @@ add_task(function* () {
yield checkWindow("background", winId2, "win2");
function* triggerPopup(win, callback) {
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true,
});
node.dispatchEvent(evt);
yield clickBrowserAction(extension, win);
yield extension.awaitMessage("popup-ready");
yield callback();
let panel = node.querySelector("panel");
if (panel) {
panel.hidePopup();
}
closeBrowserAction(extension, win);
}
// Set focus to some other window.

Просмотреть файл

@ -116,25 +116,21 @@ add_task(function* () {
yield checkViews("background", 2, 0);
function* triggerPopup(win, callback) {
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true,
});
node.dispatchEvent(evt);
yield clickBrowserAction(extension, win);
yield extension.awaitMessage("popup-ready");
yield callback();
let panel = node.querySelector("panel");
if (panel) {
panel.hidePopup();
}
closeBrowserAction(extension, win);
}
// The popup occasionally closes prematurely if we open it immediately here.
// I'm not sure what causes it to close (it's something internal, and seems to
// be focus-related, but it's not caused by JS calling hidePopup), but even a
// short timeout seems to consistently fix it.
yield new Promise(resolve => win1.setTimeout(resolve, 10));
yield triggerPopup(win1, function*() {
yield checkViews("background", 2, 1);
yield checkViews("popup", 2, 1);

Просмотреть файл

@ -2,18 +2,165 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* testTabSwitchContext() {
function* runTests(options) {
function background(getTests) {
let tabs;
let tests;
// Gets the current details of the page action, and returns a
// promise that resolves to an object containing them.
function getDetails() {
return new Promise(resolve => {
return browser.tabs.query({ active: true, currentWindow: true }, resolve);
}).then(tabs => {
let tabId = tabs[0].id;
return Promise.all([
new Promise(resolve => browser.pageAction.getTitle({tabId}, resolve)),
new Promise(resolve => browser.pageAction.getPopup({tabId}, resolve))]);
}).then(details => {
return Promise.resolve({ title: details[0],
popup: details[1] });
});
}
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(expecting => {
function finish() {
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, tests.length);
}
if (expecting) {
// Check that the API returns the expected values, and then
// run the next test.
getDetails().then(details => {
browser.test.assertEq(expecting.title, details.title,
"expected value from getTitle");
browser.test.assertEq(expecting.popup, details.popup,
"expected value from getPopup");
finish();
});
} else {
finish();
}
});
}
function runTests() {
tabs = [];
tests = getTests(tabs);
browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
tabs[0] = resultTabs[0].id;
nextTest();
});
}
browser.test.onMessage.addListener((msg) => {
if (msg == "runTests") {
runTests();
} else if (msg == "runNextTest") {
nextTest();
} else {
browser.test.fail(`Unexpected message: ${msg}`);
}
});
runTests();
}
let extension = ExtensionTestUtils.loadExtension({
manifest: options.manifest,
background: `(${background})(${options.getTests})`,
});
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let currentWindow = window;
let windows = [];
function checkDetails(details) {
let image = currentWindow.document.getElementById(pageActionId);
if (details == null) {
ok(image == null || image.hidden, "image is hidden");
} else {
ok(image, "image exists");
is(image.src, details.icon, "icon URL is correct");
let title = details.title || options.manifest.name;
is(image.getAttribute("tooltiptext"), title, "image title is correct");
is(image.getAttribute("aria-label"), title, "image aria-label is correct");
// TODO: Popup URL.
}
}
let testNewWindows = 1;
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else if (testNewWindows) {
testNewWindows--;
BrowserTestUtils.openNewBrowserWindow().then(window => {
windows.push(window);
currentWindow = window;
return focusWindow(window);
}).then(() => {
extension.sendMessage("runTests");
});
} else {
resolve();
}
});
});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
let node = document.getElementById(pageActionId);
is(node, null, "pageAction image removed from document");
currentWindow = null;
for (let win of windows.splice(0)) {
node = win.document.getElementById(pageActionId);
is(node, null, "pageAction image removed from second document");
yield BrowserTestUtils.closeWindow(win);
}
}
add_task(function* testTabSwitchContext() {
yield runTests({
manifest: {
"name": "Foo Extension",
"page_action": {
"default_icon": "default.png",
"default_popup": "default.html",
"default_title": "Default Title \u263a",
},
"permissions": ["tabs"],
},
background: function() {
getTests(tabs) {
let details = [
{ "icon": browser.runtime.getURL("default.png"),
"popup": browser.runtime.getURL("default.html"),
@ -24,11 +171,12 @@ add_task(function* testTabSwitchContext() {
{ "icon": browser.runtime.getURL("2.png"),
"popup": browser.runtime.getURL("2.html"),
"title": "Title 2" },
{ "icon": browser.runtime.getURL("2.png"),
"popup": browser.runtime.getURL("2.html"),
"title": "Default Title \u263a" },
];
let tabs;
let tests;
let allTests = [
return [
expect => {
browser.test.log("Initial state. No icon visible.");
expect(null);
@ -60,6 +208,12 @@ add_task(function* testTabSwitchContext() {
expect(details[2]);
},
expect => {
browser.test.log("Clear the title. Expect default title.");
browser.pageAction.setTitle({ tabId: tabs[1], title: "" });
expect(details[3]);
},
expect => {
browser.test.log("Navigate to a new page. Expect icon hidden.");
@ -104,135 +258,53 @@ add_task(function* testTabSwitchContext() {
expect(null);
},
];
// Gets the current details of the page action, and returns a
// promise that resolves to an object containing them.
function getDetails() {
return new Promise(resolve => {
return browser.tabs.query({ active: true, currentWindow: true }, resolve);
}).then(tabs => {
let tabId = tabs[0].id;
return Promise.all([
new Promise(resolve => browser.pageAction.getTitle({tabId}, resolve)),
new Promise(resolve => browser.pageAction.getPopup({tabId}, resolve))]);
}).then(details => {
return Promise.resolve({ title: details[0],
popup: details[1] });
});
}
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(expecting => {
function finish() {
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, tests.length);
}
if (expecting) {
// Check that the API returns the expected values, and then
// run the next test.
getDetails().then(details => {
browser.test.assertEq(expecting.title, details.title,
"expected value from getTitle");
browser.test.assertEq(expecting.popup, details.popup,
"expected value from getPopup");
finish();
});
} else {
finish();
}
});
}
function runTests() {
tabs = [];
tests = allTests.slice();
browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
tabs[0] = resultTabs[0].id;
nextTest();
});
}
browser.test.onMessage.addListener((msg) => {
if (msg == "runTests") {
runTests();
} else if (msg == "runNextTest") {
nextTest();
} else {
browser.test.fail(`Unexpected message: ${msg}`);
}
});
runTests();
},
});
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let currentWindow = window;
let windows = [];
function checkDetails(details) {
let image = currentWindow.document.getElementById(pageActionId);
if (details == null) {
ok(image == null || image.hidden, "image is hidden");
} else {
ok(image, "image exists");
is(image.src, details.icon, "icon URL is correct");
is(image.getAttribute("tooltiptext"), details.title, "image title is correct");
is(image.getAttribute("aria-label"), details.title, "image aria-label is correct");
// TODO: Popup URL.
}
}
let testNewWindows = 1;
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else if (testNewWindows) {
testNewWindows--;
BrowserTestUtils.openNewBrowserWindow().then(window => {
windows.push(window);
currentWindow = window;
return focusWindow(window);
}).then(() => {
extension.sendMessage("runTests");
});
} else {
resolve();
}
});
});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
let node = document.getElementById(pageActionId);
is(node, null, "pageAction image removed from document");
currentWindow = null;
for (let win of windows.splice(0)) {
node = win.document.getElementById(pageActionId);
is(node, null, "pageAction image removed from second document");
yield BrowserTestUtils.closeWindow(win);
}
});
add_task(function* testDefaultTitle() {
yield runTests({
manifest: {
"name": "Foo Extension",
"page_action": {
"default_icon": "icon.png",
},
"permissions": ["tabs"],
},
getTests(tabs) {
let details = [
{ "title": "Foo Extension",
"popup": "",
"icon": browser.runtime.getURL("icon.png") },
{ "title": "Foo Title",
"popup": "",
"icon": browser.runtime.getURL("icon.png") },
];
return [
expect => {
browser.test.log("Initial state. No icon visible.");
expect(null);
},
expect => {
browser.test.log("Show the icon on the first tab, expect extension title as default title.");
browser.pageAction.show(tabs[0]);
expect(details[0]);
},
expect => {
browser.test.log("Change the title. Expect new title.");
browser.pageAction.setTitle({ tabId: tabs[0], title: "Foo Title" });
expect(details[1]);
},
expect => {
browser.test.log("Clear the title. Expect extension title.");
browser.pageAction.setTitle({ tabId: tabs[0], title: "" });
expect(details[0]);
},
];
},
});
});

Просмотреть файл

@ -2,21 +2,9 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
function promisePopupShown(popup) {
return new Promise(resolve => {
if (popup.popupOpen) {
resolve();
} else {
let onPopupShown = event => {
popup.removeEventListener("popupshown", onPopupShown);
resolve();
};
popup.addEventListener("popupshown", onPopupShown);
}
});
}
add_task(function* testPageActionPopup() {
let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}"></script></head></html>`;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"background": {
@ -28,17 +16,17 @@ add_task(function* testPageActionPopup() {
},
files: {
"popup-a.html": `<script src="popup-a.js"></script>`,
"popup-a.html": scriptPage("popup-a.js"),
"popup-a.js": function() {
browser.runtime.sendMessage("from-popup-a");
},
"data/popup-b.html": `<script src="popup-b.js"></script>`,
"data/popup-b.html": scriptPage("popup-b.js"),
"data/popup-b.js": function() {
browser.runtime.sendMessage("from-popup-b");
},
"data/background.html": `<script src="background.js"></script>`,
"data/background.html": scriptPage("background.js"),
"data/background.js": function() {
let tabId;
@ -202,3 +190,5 @@ add_task(function* testPageActionSecurity() {
SimpleTest.endMonitorConsole();
yield waitForConsole;
});
add_task(forceGC);

Просмотреть файл

@ -42,19 +42,6 @@ add_task(function* testPageActionPopup() {
},
});
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let pageActionId = makeWidgetId(extension.id) + "-page-action";
function openPopup(buttonId) {
let button = document.getElementById(buttonId);
if (buttonId == pageActionId) {
// TODO: I don't know why a proper synthesized event doesn't work here.
button.dispatchEvent(new MouseEvent("click", {}));
} else {
EventUtils.synthesizeMouseAtCenter(button, {}, window);
}
}
let promiseConsoleMessage = pattern => new Promise(resolve => {
Services.console.registerListener(function listener(msg) {
if (pattern.test(msg.message)) {
@ -72,21 +59,25 @@ add_task(function* testPageActionPopup() {
// BrowserAction:
let awaitMessage = promiseConsoleMessage(/WebExt Privilege Escalation: BrowserAction/);
SimpleTest.expectUncaughtException();
openPopup(browserActionId);
yield clickBrowserAction(extension);
let message = yield awaitMessage;
ok(message.includes("WebExt Privilege Escalation: BrowserAction: typeof(browser) = undefined"),
`No BrowserAction API injection`);
yield closeBrowserAction(extension);
// PageAction
awaitMessage = promiseConsoleMessage(/WebExt Privilege Escalation: PageAction/);
SimpleTest.expectUncaughtException();
openPopup(pageActionId);
yield clickPageAction(extension);
message = yield awaitMessage;
ok(message.includes("WebExt Privilege Escalation: PageAction: typeof(browser) = undefined"),
`No PageAction API injection: ${message}`);
yield closePageAction(extension);
SimpleTest.expectUncaughtException(false);
@ -95,12 +86,13 @@ add_task(function* testPageActionPopup() {
yield extension.awaitMessage("ok");
// Check that unprivileged documents don't get the API.
openPopup(browserActionId);
yield clickBrowserAction(extension);
yield extension.awaitMessage("from-popup-a");
yield closeBrowserAction(extension);
openPopup(pageActionId);
yield clickPageAction(extension);
yield extension.awaitMessage("from-popup-b");
yield closePageAction(extension);
yield extension.unload();
});

Просмотреть файл

@ -110,3 +110,5 @@ add_task(function* testBadPermissions() {
// new page, and no longer matches our expected state. This involves
// intentionally trying to trigger a race condition, and is probably not
// even worth attempting until we have proper |executeScript| callbacks.
add_task(forceGC);

Просмотреть файл

@ -48,6 +48,11 @@ function* testHasPermission(params) {
extension.sendMessage("execute-script");
yield extension.awaitFinish("executeScript");
if (params.tearDown) {
yield params.tearDown(extension);
}
yield extension.unload();
}
@ -82,6 +87,7 @@ add_task(function* testGoodPermissions() {
return Promise.resolve();
},
setup: clickBrowserAction,
tearDown: closeBrowserAction,
});
info("Test activeTab permission with a page action click");
@ -99,6 +105,7 @@ add_task(function* testGoodPermissions() {
});
},
setup: clickPageAction,
tearDown: closePageAction,
});
info("Test activeTab permission with a browser action w/popup click");
@ -108,6 +115,7 @@ add_task(function* testGoodPermissions() {
"browser_action": { "default_popup": "_blank.html" },
},
setup: clickBrowserAction,
tearDown: closeBrowserAction,
});
info("Test activeTab permission with a page action w/popup click");
@ -125,6 +133,7 @@ add_task(function* testGoodPermissions() {
});
},
setup: clickPageAction,
tearDown: closePageAction,
});
info("Test activeTab permission with a context menu click");
@ -155,3 +164,5 @@ add_task(function* testGoodPermissions() {
yield BrowserTestUtils.removeTab(tab);
});
add_task(forceGC);

Просмотреть файл

@ -63,6 +63,7 @@ add_task(function* () {
clickBrowserAction(extension);
yield extension.awaitMessage("popup-finished");
yield closeBrowserAction(extension);
yield extension.unload();
});

Просмотреть файл

@ -172,3 +172,5 @@ add_task(function* test_url() {
});
});
});
add_task(forceGC);

Просмотреть файл

@ -2,10 +2,27 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* exported CustomizableUI makeWidgetId focusWindow clickBrowserAction clickPageAction */
/* exported CustomizableUI makeWidgetId focusWindow forceGC
* getBrowserActionWidget
* clickBrowserAction clickPageAction
* getBrowserActionPopup getPageActionPopup
* closeBrowserAction closePageAction
* promisePopupShown
*/
var {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm");
var {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm");
// Bug 1239884: Our tests occasionally hit a long GC pause at unpredictable
// times in debug builds, which results in intermittent timeouts. Until we have
// a better solution, we force a GC after certain strategic tests, which tend to
// accumulate a high number of unreaped windows.
function forceGC() {
if (AppConstants.DEBUG) {
Cu.forceGC();
}
}
function makeWidgetId(id) {
id = id.toLowerCase();
return id.replace(/[^a-z0-9_-]/g, "_");
@ -28,12 +45,58 @@ var focusWindow = Task.async(function* focusWindow(win) {
yield promise;
});
function clickBrowserAction(extension, win = window) {
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let elem = win.document.getElementById(browserActionId);
function promisePopupShown(popup) {
return new Promise(resolve => {
if (popup.state == "open") {
resolve();
} else {
let onPopupShown = event => {
popup.removeEventListener("popupshown", onPopupShown);
resolve();
};
popup.addEventListener("popupshown", onPopupShown);
}
});
}
EventUtils.synthesizeMouseAtCenter(elem, {}, win);
return new Promise(SimpleTest.executeSoon);
function getBrowserActionWidget(extension) {
return CustomizableUI.getWidget(makeWidgetId(extension.id) + "-browser-action");
}
function getBrowserActionPopup(extension, win = window) {
let group = getBrowserActionWidget(extension);
if (group.areaType == CustomizableUI.TYPE_TOOLBAR) {
return win.document.getElementById("customizationui-widget-panel");
}
return null;
}
var clickBrowserAction = Task.async(function* (extension, win = window) {
let group = getBrowserActionWidget(extension);
let widget = group.forWindow(win);
if (group.areaType == CustomizableUI.TYPE_TOOLBAR) {
ok(!widget.overflowed, "Expect widget not to be overflowed");
} else if (group.areaType == CustomizableUI.TYPE_MENU_PANEL) {
yield win.PanelUI.show();
}
EventUtils.synthesizeMouseAtCenter(widget.node, {}, win);
});
function closeBrowserAction(extension, win = window) {
let group = getBrowserActionWidget(extension);
let node = win.document.getElementById(group.viewId);
CustomizableUI.hidePanelForNode(node);
return Promise.resolve();
}
function getPageActionPopup(extension, win = window) {
let panelId = makeWidgetId(extension.id) + "-panel";
return win.document.getElementById(panelId);
}
function clickPageAction(extension, win = window) {
@ -52,3 +115,13 @@ function clickPageAction(extension, win = window) {
EventUtils.synthesizeMouseAtCenter(elem, {}, win);
return new Promise(SimpleTest.executeSoon);
}
function closePageAction(extension, win = window) {
let node = getPageActionPopup(extension, win);
if (node) {
node.hidePopup();
}
return Promise.resolve();
}

Просмотреть файл

@ -12,6 +12,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
@ -660,9 +662,7 @@ nsBrowserContentHandler.prototype = {
var url = Services.urlFormatter.formatURLPref("app.support.baseURL") +
"win10-default-browser";
if (urlParam == url) {
var shellSvc = Components.classes["@mozilla.org/browser/shell-service;1"]
.getService(Components.interfaces.nsIShellService);
isDefault = shellSvc.isDefaultBrowser(false, false);
isDefault = ShellService.isDefaultBrowser(false, false);
}
} catch (ex) {}
if (isDefault) {

Просмотреть файл

@ -140,16 +140,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
"resource:///modules/ContentCrashHandlers.jsm");
#endif
XPCOMUtils.defineLazyGetter(this, "ShellService", function() {
try {
return Cc["@mozilla.org/browser/shell-service;1"].
getService(Ci.nsIShellService);
}
catch(ex) {
return null;
}
});
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
return Services.strings.createBundle('chrome://branding/locale/brand.properties');
});
@ -180,6 +170,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");

Просмотреть файл

@ -5,6 +5,7 @@
Components.utils.import("resource://gre/modules/Downloads.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/Task.jsm");
Components.utils.import("resource:///modules/ShellService.jsm");
Components.utils.import("resource:///modules/TransientPrefs.jsm");
#ifdef E10S_TESTING_ONLY
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",

Просмотреть файл

@ -0,0 +1,86 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["ShellService"];
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/**
* Internal functionality to save and restore the docShell.allow* properties.
*/
let ShellServiceInternal = {
/**
* Used to determine whether or not to offer "Set as desktop background"
* functionality. Even if shell service is available it is not
* guaranteed that it is able to set the background for every desktop
* which is especially true for Linux with its many different desktop
* environments.
*/
get canSetDesktopBackground() {
if (AppConstants.platform == "win" ||
AppConstants.platform == "macosx") {
return true;
}
if (AppConstants.platform == "linux") {
if (this.shellService) {
let linuxShellService = this.shellService
.QueryInterface(Ci.nsIGNOMEShellService);
return linuxShellService.canSetDesktopBackground;
}
}
return false;
},
/**
* Used to determine whether or not to show a "Set Default Browser"
* query dialog. This attribute is true if the application is starting
* up and "browser.shell.checkDefaultBrowser" is true, otherwise it
* is false.
*/
_checkedThisSession: false,
get shouldCheckDefaultBrowser() {
// If we've already checked, the browser has been started and this is a
// new window open, and we don't want to check again.
if (this._checkedThisSession) {
return false;
}
return Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser");
},
set shouldCheckDefaultBrowser(shouldCheck) {
Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck);
},
isDefaultBrowser(startupCheck, forAllTypes) {
// If this is the first browser window, maintain internal state that we've
// checked this session (so that subsequent window opens don't show the
// default browser dialog).
if (startupCheck) {
this._checkedThisSession = true;
}
if (this.shellService) {
return this.shellService.isDefaultBrowser(startupCheck, forAllTypes);
}
}
};
XPCOMUtils.defineLazyServiceGetter(ShellServiceInternal, "shellService",
"@mozilla.org/browser/shell-service;1", Ci.nsIShellService);
/**
* The external API exported by this module.
*/
this.ShellService = new Proxy(ShellServiceInternal, {
get(target, name) {
return name in target ? target[name] :
target.shellService[name];
}
});

Просмотреть файл

@ -21,6 +21,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
XPIDL_SOURCES += [
'nsIMacShellService.idl',
]
elif CONFIG['MOZ_WIDGET_GTK']:
XPIDL_SOURCES += [
'nsIGNOMEShellService.idl',
]
XPIDL_MODULE = 'shellservice'
@ -45,6 +49,10 @@ EXTRA_COMPONENTS += [
'nsSetDefaultBrowser.manifest',
]
EXTRA_JS_MODULES += [
'ShellService.jsm',
]
for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION'):
DEFINES[var] = '"%s"' % CONFIG[var]

Просмотреть файл

@ -117,7 +117,7 @@ nsGNOMEShellService::Init()
return appPath->GetNativePath(mAppPath);
}
NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIShellService)
NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIGNOMEShellService, nsIShellService)
bool
nsGNOMEShellService::GetAppPathFromLauncher()
@ -201,8 +201,6 @@ nsGNOMEShellService::IsDefaultBrowser(bool aStartupCheck,
bool* aIsDefaultBrowser)
{
*aIsDefaultBrowser = false;
if (aStartupCheck)
mCheckedThisSession = true;
nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
@ -325,37 +323,6 @@ nsGNOMEShellService::SetDefaultBrowser(bool aClaimAllTypes,
return NS_OK;
}
NS_IMETHODIMP
nsGNOMEShellService::GetShouldCheckDefaultBrowser(bool* aResult)
{
// If we've already checked, the browser has been started and this is a
// new window open, and we don't want to check again.
if (mCheckedThisSession) {
*aResult = false;
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
}
NS_IMETHODIMP
nsGNOMEShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
}
NS_IMETHODIMP
nsGNOMEShellService::GetCanSetDesktopBackground(bool* aResult)
{

Просмотреть файл

@ -6,17 +6,18 @@
#ifndef nsgnomeshellservice_h____
#define nsgnomeshellservice_h____
#include "nsIShellService.h"
#include "nsIGNOMEShellService.h"
#include "nsStringAPI.h"
#include "mozilla/Attributes.h"
class nsGNOMEShellService final : public nsIShellService
class nsGNOMEShellService final : public nsIGNOMEShellService
{
public:
nsGNOMEShellService() : mCheckedThisSession(false), mAppIsInPath(false) { }
nsGNOMEShellService() : mAppIsInPath(false) { }
NS_DECL_ISUPPORTS
NS_DECL_NSISHELLSERVICE
NS_DECL_NSIGNOMESHELLSERVICE
nsresult Init();
@ -27,7 +28,6 @@ private:
bool CheckHandlerMatchesAppName(const nsACString& handler) const;
bool GetAppPathFromLauncher();
bool mCheckedThisSession;
bool mUseLocaleFilenames;
nsCString mAppPath;
bool mAppIsInPath;

Просмотреть файл

@ -0,0 +1,19 @@
/* 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 "nsIShellService.idl"
[scriptable, uuid(2ce5c803-edcd-443d-98eb-ceba86d02d13)]
interface nsIGNOMEShellService : nsIShellService
{
/**
* Used to determine whether or not to offer "Set as desktop background"
* functionality. Even if shell service is available it is not
* guaranteed that it is able to set the background for every desktop
* which is especially true for Linux with its many different desktop
* environments.
*/
readonly attribute boolean canSetDesktopBackground;
};

Просмотреть файл

@ -5,7 +5,7 @@
#include "nsIShellService.idl"
[scriptable, uuid(291a27cd-ef4c-46c6-a2f8-83182498167e)]
[scriptable, uuid(387fdc80-0077-4b60-a0d9-d9e80a83ba64)]
interface nsIMacShellService : nsIShellService
{
const long APPLICATION_KEYCHAIN_ACCESS = 2;

Просмотреть файл

@ -8,7 +8,7 @@
interface nsIDOMElement;
interface nsIFile;
[scriptable, uuid(53f4bc4a-5b86-4643-8e67-4907ecbab34c)]
[scriptable, uuid(2d1a95e4-5bd8-4eeb-b0a8-c1455fd2a357)]
interface nsIShellService : nsISupports
{
/**
@ -38,23 +38,6 @@ interface nsIShellService : nsISupports
*/
void setDefaultBrowser(in boolean aClaimAllTypes, in boolean aForAllUsers);
/**
* Used to determine whether or not to show a "Set Default Browser"
* query dialog. This attribute is true if the application is starting
* up and "browser.shell.checkDefaultBrowser" is true, otherwise it
* is false.
*/
attribute boolean shouldCheckDefaultBrowser;
/**
* Used to determine whether or not to offer "Set as desktop background"
* functionality. Even if shell service is available it is not
* guaranteed that it is able to set the background for every desktop
* which is especially true for Linux with its many different desktop
* environments.
*/
readonly attribute boolean canSetDesktopBackground;
/**
* Flags for positioning/sizing of the Desktop Background image.
*/

Просмотреть файл

@ -5,7 +5,7 @@
#include "nsIShellService.idl"
[scriptable, uuid(13f20725-4fd5-431f-90a1-525ab31755b1)]
[scriptable, uuid(f8a26b94-49e5-4441-8fbc-315e0b4f22ef)]
interface nsIWindowsShellService : nsIShellService
{
/**

Просмотреть файл

@ -57,12 +57,6 @@ nsMacShellService::IsDefaultBrowser(bool aStartupCheck,
::CFRelease(defaultBrowserID);
}
// If this is the first browser window, maintain internal state that we've
// checked this session (so that subsequent window opens don't show the
// default browser dialog).
if (aStartupCheck)
mCheckedThisSession = true;
return NS_OK;
}
@ -103,44 +97,6 @@ nsMacShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::GetShouldCheckDefaultBrowser(bool* aResult)
{
// If we've already checked, the browser has been started and this is a
// new window open, and we don't want to check again.
if (mCheckedThisSession) {
*aResult = false;
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
}
NS_IMETHODIMP
nsMacShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
}
NS_IMETHODIMP
nsMacShellService::GetCanSetDesktopBackground(bool* aResult)
{
*aResult = true;
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::SetDesktopBackground(nsIDOMElement* aElement,
int32_t aPosition)

Просмотреть файл

@ -15,7 +15,7 @@ class nsMacShellService : public nsIMacShellService,
public nsIWebProgressListener
{
public:
nsMacShellService() : mCheckedThisSession(false) {};
nsMacShellService() {};
NS_DECL_ISUPPORTS
NS_DECL_NSISHELLSERVICE
@ -27,8 +27,6 @@ protected:
private:
nsCOMPtr<nsIFile> mBackgroundFile;
bool mCheckedThisSession;
};
#endif // nsmacshellservice_h____

Просмотреть файл

@ -2,13 +2,14 @@
* 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/. */
/*
/*
* --setDefaultBrowser commandline handler
* Makes the current executable the "default browser".
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
Components.utils.import("resource:///modules/ShellService.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function nsSetDefaultBrowser() {}
@ -16,9 +17,7 @@ function nsSetDefaultBrowser() {}
nsSetDefaultBrowser.prototype = {
handle: function nsSetDefault_handle(aCmdline) {
if (aCmdline.handleFlag("setDefaultBrowser", false)) {
var shell = Cc["@mozilla.org/browser/shell-service;1"].
getService(Ci.nsIShellService);
shell.setDefaultBrowser(true, true);
ShellService.setDefaultBrowser(true, true);
}
},

Просмотреть файл

@ -608,12 +608,6 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
bool aForAllTypes,
bool* aIsDefaultBrowser)
{
// If this is the first browser window, maintain internal state that we've
// checked this session (so that subsequent window opens don't show the
// default browser dialog).
if (aStartupCheck)
mCheckedThisSession = true;
// Assume we're the default unless one of the several checks below tell us
// otherwise.
*aIsDefaultBrowser = true;
@ -807,13 +801,6 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::GetCanSetDesktopBackground(bool* aResult)
{
*aResult = true;
return NS_OK;
}
static nsresult
DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
{
@ -984,39 +971,6 @@ nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
return rv;
}
NS_IMETHODIMP
nsWindowsShellService::GetShouldCheckDefaultBrowser(bool* aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
// If we've already checked, the browser has been started and this is a
// new window open, and we don't want to check again.
if (mCheckedThisSession) {
*aResult = false;
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
}
NS_IMETHODIMP
nsWindowsShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
}
static nsresult
WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
{
@ -1358,8 +1312,7 @@ nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor)
return regKey->Close();
}
nsWindowsShellService::nsWindowsShellService() :
mCheckedThisSession(false)
nsWindowsShellService::nsWindowsShellService()
{
}

Просмотреть файл

@ -31,9 +31,6 @@ protected:
nsresult LaunchModernSettingsDialogDefaultApps();
nsresult InvokeHTTPOpenAsVerb();
nsresult LaunchHTTPHandlerPane();
private:
bool mCheckedThisSession;
};
#endif // nswindowsshellservice_h____

Просмотреть файл

@ -1,15 +1,7 @@
Components.utils.import("resource:///modules/ShellService.jsm");
function test() {
let osString = Cc["@mozilla.org/xre/app-info;1"].
getService(Ci.nsIXULRuntime).OS;
// this test is Linux-specific
if (osString != "Linux")
return;
let shell = Cc["@mozilla.org/browser/shell-service;1"].
getService(Ci.nsIShellService);
shell.setDefaultBrowser(true, false);
ok(shell.isDefaultBrowser(true, false), "we got here and are the default browser");
ok(shell.isDefaultBrowser(true, true), "we got here and are the default browser");
ShellService.setDefaultBrowser(true, false);
ok(ShellService.isDefaultBrowser(true, false), "we got here and are the default browser");
ok(ShellService.isDefaultBrowser(true, true), "we got here and are the default browser");
}

Просмотреть файл

@ -250,6 +250,11 @@ panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .pan
max-width: @standaloneSubviewWidth@;
}
/* Give WebExtension stand-alone panels extra width for Chrome compatibility */
.cui-widget-panel[viewId^=PanelUI-webext-] .panel-mainview {
max-width: 800px;
}
panelview:not([mainview]) .toolbarbutton-text,
.cui-widget-panel toolbarbutton > .toolbarbutton-text {
text-align: start;

Просмотреть файл

@ -21,7 +21,8 @@ AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [
fi
else
case "$target" in
*-mingw*)
dnl Oy (Frame-Pointer Omission) is only support on x86 compilers
*-mingw32*)
MOZ_ENABLE_FRAME_PTR="-Oy-"
MOZ_DISABLE_FRAME_PTR="-Oy"
;;

Просмотреть файл

@ -66,19 +66,14 @@ SEARCH_PATHS = [
'dom/bindings/parser',
'layout/tools/reftest',
'other-licenses/ply',
'xpcom/idl-parser',
'testing',
'testing/tools/autotry',
'testing/taskcluster',
'testing/xpcshell',
'testing/web-platform',
'testing/web-platform/harness',
'testing/web-platform/tests/tools/wptserve',
'testing/firefox-ui/harness',
'testing/firefox-ui/tests',
'testing/luciddream',
'testing/marionette/client',
'testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py',
'testing/marionette/transport',
'testing/marionette/driver',
'testing/luciddream',
'testing/mozbase/mozcrash',
'testing/mozbase/mozdebug',
'testing/mozbase/mozdevice',
@ -97,6 +92,13 @@ SEARCH_PATHS = [
'testing/mozbase/moztest',
'testing/mozbase/mozversion',
'testing/mozbase/manifestparser',
'testing/puppeteer/firefox',
'testing/taskcluster',
'testing/tools/autotry',
'testing/web-platform',
'testing/web-platform/harness',
'testing/web-platform/tests/tools/wptserve',
'testing/xpcshell',
'xpcom/idl-parser',
]
@ -115,14 +117,14 @@ MACH_MODULES = [
'python/mozbuild/mozbuild/compilation/codecomplete.py',
'python/mozbuild/mozbuild/frontend/mach_commands.py',
'services/common/tests/mach_commands.py',
'testing/firefox-ui/mach_commands.py',
'testing/luciddream/mach_commands.py',
'testing/mach_commands.py',
'testing/taskcluster/mach_commands.py',
'testing/marionette/mach_commands.py',
'testing/mochitest/mach_commands.py',
'testing/mozharness/mach_commands.py',
'testing/xpcshell/mach_commands.py',
'testing/talos/mach_commands.py',
'testing/taskcluster/mach_commands.py',
'testing/web-platform/mach_commands.py',
'testing/xpcshell/mach_commands.py',
'tools/docs/mach_commands.py',

Просмотреть файл

@ -467,7 +467,7 @@ nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
unsigned lineNum = 0;
NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
JS::AutoFilename scriptFilename;
JS::UniqueChars scriptFilename;
if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum)) {
if (const char *file = scriptFilename.get()) {
CopyUTF8toUTF16(nsDependentCString(file), fileName);

Просмотреть файл

@ -20,7 +20,6 @@ new
algorithm
atomic
deque
initializer_list
ios
iosfwd
iostream

Просмотреть файл

@ -75,7 +75,7 @@ GNOMEUI_VERSION=2.2.0
GCONF_VERSION=1.2.1
STARTUP_NOTIFICATION_VERSION=0.8
DBUS_VERSION=0.60
SQLITE_VERSION=3.9.1
SQLITE_VERSION=3.10.1
MSMANIFEST_TOOL=
@ -1457,6 +1457,7 @@ if test "$GNU_CC"; then
_WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wpointer-arith"
_WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wtype-limits"
# -Wclass-varargs - catches objects passed by value to variadic functions.
# -Wnon-literal-null-conversion - catches expressions used as a null pointer constant
# -Wsometimes-initialized - catches some uninitialized values
# -Wunreachable-code-aggressive - catches lots of dead code
@ -1468,6 +1469,8 @@ if test "$GNU_CC"; then
# -Werror=non-literal-null-conversion, but we only do that when
# --enable-warnings-as-errors is specified so that no unexpected fatal
# warnings are produced.
MOZ_C_SUPPORTS_WARNING(-W, class-varargs, ac_c_has_wclass_varargs)
if test "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then
MOZ_C_SUPPORTS_WARNING(-Werror=, non-literal-null-conversion, ac_c_has_non_literal_null_conversion)
fi
@ -1541,6 +1544,7 @@ if test "$GNU_CXX"; then
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wpointer-arith"
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wtype-limits"
# -Wclass-varargs - catches objects passed by value to variadic functions.
# -Wnon-literal-null-conversion - catches expressions used as a null pointer constant
# -Wrange-loop-analysis - catches copies during range-based for loops.
# -Wsometimes-initialized - catches some uninitialized values
@ -1554,6 +1558,8 @@ if test "$GNU_CXX"; then
# -Werror=non-literal-null-conversion, but we only do that when
# --enable-warnings-as-errors is specified so that no unexpected fatal
# warnings are produced.
MOZ_CXX_SUPPORTS_WARNING(-W, class-varargs, ac_cxx_has_wclass_varargs)
if test "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then
MOZ_CXX_SUPPORTS_WARNING(-Werror=, non-literal-null-conversion, ac_cxx_has_non_literal_null_conversion)
fi

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -111,9 +111,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.9.1"
#define SQLITE_VERSION_NUMBER 3009001
#define SQLITE_SOURCE_ID "2015-10-16 17:31:12 767c1727fec4ce11b83f25b3f1bfcfe68a2c8b02"
#define SQLITE_VERSION "3.10.1"
#define SQLITE_VERSION_NUMBER 3010001
#define SQLITE_SOURCE_ID "2016-01-13 21:41:56 254419c36766225ca542ae873ed38255e3fb8588"
/*
** CAPI3REF: Run-Time Library Version Numbers
@ -478,6 +478,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_exec(
#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
@ -793,8 +794,13 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_FILE_POINTER]]
** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer
** to the [sqlite3_file] object associated with a particular database
** connection. See the [sqlite3_file_control()] documentation for
** additional information.
** connection. See also [SQLITE_FCNTL_JOURNAL_POINTER].
**
** <li>[[SQLITE_FCNTL_JOURNAL_POINTER]]
** The [SQLITE_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer
** to the [sqlite3_file] object associated with the journal file (either
** the [rollback journal] or the [write-ahead log]) for a particular database
** connection. See also [SQLITE_FCNTL_FILE_POINTER].
**
** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
** No longer in use.
@ -881,6 +887,15 @@ struct sqlite3_io_methods {
** pointer in case this file-control is not implemented. This file-control
** is intended for diagnostic use only.
**
** <li>[[SQLITE_FCNTL_VFS_POINTER]]
** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
** [VFSes] currently in use. ^(The argument X in
** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be
** of type "[sqlite3_vfs] **". This opcodes will set *X
** to a pointer to the top-level VFS.)^
** ^When there are multiple VFS shims in the stack, this opcode finds the
** upper-most shim only.
**
** <li>[[SQLITE_FCNTL_PRAGMA]]
** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
** file control is sent to the open [sqlite3_file] object corresponding
@ -999,6 +1014,8 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_WAL_BLOCK 24
#define SQLITE_FCNTL_ZIPVFS 25
#define SQLITE_FCNTL_RBU 26
#define SQLITE_FCNTL_VFS_POINTER 27
#define SQLITE_FCNTL_JOURNAL_POINTER 28
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@ -1598,29 +1615,34 @@ struct sqlite3_mem_methods {
** </dd>
**
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a static memory buffer
** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a memory pool
** that SQLite can use for the database page cache with the default page
** cache implementation.
** This configuration should not be used if an application-define page
** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2]
** configuration option.
** This configuration option is a no-op if an application-define page
** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2].
** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to
** 8-byte aligned
** memory, the size of each page buffer (sz), and the number of pages (N).
** 8-byte aligned memory (pMem), the size of each page cache line (sz),
** and the number of cache lines (N).
** The sz argument should be the size of the largest database page
** (a power of two between 512 and 65536) plus some extra bytes for each
** page header. ^The number of extra bytes needed by the page header
** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option
** to [sqlite3_config()].
** can be determined using [SQLITE_CONFIG_PCACHE_HDRSZ].
** ^It is harmless, apart from the wasted memory,
** for the sz parameter to be larger than necessary. The first
** argument should pointer to an 8-byte aligned block of memory that
** is at least sz*N bytes of memory, otherwise subsequent behavior is
** undefined.
** ^SQLite will use the memory provided by the first argument to satisfy its
** memory needs for the first N pages that it adds to cache. ^If additional
** page cache memory is needed beyond what is provided by this option, then
** SQLite goes to [sqlite3_malloc()] for the additional storage space.</dd>
** for the sz parameter to be larger than necessary. The pMem
** argument must be either a NULL pointer or a pointer to an 8-byte
** aligned block of memory of at least sz*N bytes, otherwise
** subsequent behavior is undefined.
** ^When pMem is not NULL, SQLite will strive to use the memory provided
** to satisfy page cache needs, falling back to [sqlite3_malloc()] if
** a page cache line is larger than sz bytes or if all of the pMem buffer
** is exhausted.
** ^If pMem is NULL and N is non-zero, then each database connection
** does an initial bulk allocation for page cache memory
** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or
** of -1024*N bytes if N is negative, . ^If additional
** page cache memory is needed beyond what is provided by the initial
** allocation, then SQLite goes to [sqlite3_malloc()] separately for each
** additional cache line. </dd>
**
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
@ -4389,8 +4411,8 @@ SQLITE_API unsigned int SQLITE_STDCALL sqlite3_value_subtype(sqlite3_value*);
** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
** then sqlite3_value_free(V) is a harmless no-op.
*/
SQLITE_API SQLITE_EXPERIMENTAL sqlite3_value *SQLITE_STDCALL sqlite3_value_dup(const sqlite3_value*);
SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_value_free(sqlite3_value*);
SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_value_dup(const sqlite3_value*);
SQLITE_API void SQLITE_STDCALL sqlite3_value_free(sqlite3_value*);
/*
** CAPI3REF: Obtain Aggregate Function Context
@ -5609,6 +5631,17 @@ struct sqlite3_module {
** ^Information about the ORDER BY clause is stored in aOrderBy[].
** ^Each term of aOrderBy records a column of the ORDER BY clause.
**
** The colUsed field indicates which columns of the virtual table may be
** required by the current scan. Virtual table columns are numbered from
** zero in the order in which they appear within the CREATE TABLE statement
** passed to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62),
** the corresponding bit is set within the colUsed mask if the column may be
** required by SQLite. If the table has at least 64 columns and any column
** to the right of the first 63 is required, then bit 63 of colUsed is also
** set. In other words, column iCol may be required if the expression
** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to
** non-zero.
**
** The [xBestIndex] method must fill aConstraintUsage[] with information
** about what parameters to pass to xFilter. ^If argvIndex>0 then
** the right-hand side of the corresponding aConstraint[] is evaluated
@ -5688,6 +5721,8 @@ struct sqlite3_index_info {
sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
/* Fields below are only available in SQLite 3.9.0 and later */
int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
/* Fields below are only available in SQLite 3.10.0 and later */
sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
};
/*
@ -5703,12 +5738,15 @@ struct sqlite3_index_info {
** an operator that is part of a constraint term in the wHERE clause of
** a query that uses a [virtual table].
*/
#define SQLITE_INDEX_CONSTRAINT_EQ 2
#define SQLITE_INDEX_CONSTRAINT_GT 4
#define SQLITE_INDEX_CONSTRAINT_LE 8
#define SQLITE_INDEX_CONSTRAINT_LT 16
#define SQLITE_INDEX_CONSTRAINT_GE 32
#define SQLITE_INDEX_CONSTRAINT_MATCH 64
#define SQLITE_INDEX_CONSTRAINT_EQ 2
#define SQLITE_INDEX_CONSTRAINT_GT 4
#define SQLITE_INDEX_CONSTRAINT_LE 8
#define SQLITE_INDEX_CONSTRAINT_LT 16
#define SQLITE_INDEX_CONSTRAINT_GE 32
#define SQLITE_INDEX_CONSTRAINT_MATCH 64
#define SQLITE_INDEX_CONSTRAINT_LIKE 65
#define SQLITE_INDEX_CONSTRAINT_GLOB 66
#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
/*
** CAPI3REF: Register A Virtual Table Implementation
@ -6572,7 +6610,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_status64(
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
** <dd>This parameter records the deepest parser stack. It is only
** <dd>The *pHighwater parameter records the deepest parser stack.
** The *pCurrent value is undefined. The *pHighwater value is only
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
** </dl>
**
@ -7358,18 +7397,43 @@ SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *, const char *, int);
/*
** CAPI3REF: String Globbing
*
** ^The [sqlite3_strglob(P,X)] interface returns zero if string X matches
** the glob pattern P, and it returns non-zero if string X does not match
** the glob pattern P. ^The definition of glob pattern matching used in
** ^The [sqlite3_strglob(P,X)] interface returns zero if and only if
** string X matches the [GLOB] pattern P.
** ^The definition of [GLOB] pattern matching used in
** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the
** SQL dialect used by SQLite. ^The sqlite3_strglob(P,X) function is case
** sensitive.
** SQL dialect understood by SQLite. ^The [sqlite3_strglob(P,X)] function
** is case sensitive.
**
** Note that this routine returns zero on a match and non-zero if the strings
** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()].
**
** See also: [sqlite3_strlike()].
*/
SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlob, const char *zStr);
/*
** CAPI3REF: String LIKE Matching
*
** ^The [sqlite3_strlike(P,X,E)] interface returns zero if and only if
** string X matches the [LIKE] pattern P with escape character E.
** ^The definition of [LIKE] pattern matching used in
** [sqlite3_strlike(P,X,E)] is the same as for the "X LIKE P ESCAPE E"
** operator in the SQL dialect understood by SQLite. ^For "X LIKE P" without
** the ESCAPE clause, set the E parameter of [sqlite3_strlike(P,X,E)] to 0.
** ^As with the LIKE operator, the [sqlite3_strlike(P,X,E)] function is case
** insensitive - equivalent upper and lower case ASCII characters match
** one another.
**
** ^The [sqlite3_strlike(P,X,E)] function matches Unicode characters, though
** only ASCII characters are case folded.
**
** Note that this routine returns zero on a match and non-zero if the strings
** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()].
**
** See also: [sqlite3_strglob()].
*/
SQLITE_API int SQLITE_STDCALL sqlite3_strlike(const char *zGlob, const char *zStr, unsigned int cEsc);
/*
** CAPI3REF: Error Logging Interface
**
@ -7790,6 +7854,129 @@ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_scanstatus(
*/
SQLITE_API void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
/*
** CAPI3REF: Flush caches to disk mid-transaction
**
** ^If a write-transaction is open on [database connection] D when the
** [sqlite3_db_cacheflush(D)] interface invoked, any dirty
** pages in the pager-cache that are not currently in use are written out
** to disk. A dirty page may be in use if a database cursor created by an
** active SQL statement is reading from it, or if it is page 1 of a database
** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)]
** interface flushes caches for all schemas - "main", "temp", and
** any [attached] databases.
**
** ^If this function needs to obtain extra database locks before dirty pages
** can be flushed to disk, it does so. ^If those locks cannot be obtained
** immediately and there is a busy-handler callback configured, it is invoked
** in the usual manner. ^If the required lock still cannot be obtained, then
** the database is skipped and an attempt made to flush any dirty pages
** belonging to the next (if any) database. ^If any databases are skipped
** because locks cannot be obtained, but no other error occurs, this
** function returns SQLITE_BUSY.
**
** ^If any other error occurs while flushing dirty pages to disk (for
** example an IO error or out-of-memory condition), then processing is
** abandoned and an SQLite [error code] is returned to the caller immediately.
**
** ^Otherwise, if no error occurs, [sqlite3_db_cacheflush()] returns SQLITE_OK.
**
** ^This function does not set the database handle error code or message
** returned by the [sqlite3_errcode()] and [sqlite3_errmsg()] functions.
*/
SQLITE_API int SQLITE_STDCALL sqlite3_db_cacheflush(sqlite3*);
/*
** CAPI3REF: Database Snapshot
** KEYWORDS: {snapshot}
** EXPERIMENTAL
**
** An instance of the snapshot object records the state of a [WAL mode]
** database for some specific point in history.
**
** In [WAL mode], multiple [database connections] that are open on the
** same database file can each be reading a different historical version
** of the database file. When a [database connection] begins a read
** transaction, that connection sees an unchanging copy of the database
** as it existed for the point in time when the transaction first started.
** Subsequent changes to the database from other connections are not seen
** by the reader until a new read transaction is started.
**
** The sqlite3_snapshot object records state information about an historical
** version of the database file so that it is possible to later open a new read
** transaction that sees that historical version of the database rather than
** the most recent version.
**
** The constructor for this object is [sqlite3_snapshot_get()]. The
** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer
** to an historical snapshot (if possible). The destructor for
** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
*/
typedef struct sqlite3_snapshot sqlite3_snapshot;
/*
** CAPI3REF: Record A Database Snapshot
** EXPERIMENTAL
**
** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a
** new [sqlite3_snapshot] object that records the current state of
** schema S in database connection D. ^On success, the
** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
** ^If schema S of [database connection] D is not a [WAL mode] database
** that is in a read transaction, then [sqlite3_snapshot_get(D,S,P)]
** leaves the *P value unchanged and returns an appropriate [error code].
**
** The [sqlite3_snapshot] object returned from a successful call to
** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()]
** to avoid a memory leak.
**
** The [sqlite3_snapshot_get()] interface is only available when the
** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
*/
SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_snapshot_get(
sqlite3 *db,
const char *zSchema,
sqlite3_snapshot **ppSnapshot
);
/*
** CAPI3REF: Start a read transaction on an historical snapshot
** EXPERIMENTAL
**
** ^The [sqlite3_snapshot_open(D,S,P)] interface attempts to move the
** read transaction that is currently open on schema S of
** [database connection] D so that it refers to historical [snapshot] P.
** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success
** or an appropriate [error code] if it fails.
**
** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be
** the first operation, apart from other sqlite3_snapshot_open() calls,
** following the [BEGIN] that starts a new read transaction.
** ^A [snapshot] will fail to open if it has been overwritten by a
** [checkpoint].
**
** The [sqlite3_snapshot_open()] interface is only available when the
** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
*/
SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_snapshot_open(
sqlite3 *db,
const char *zSchema,
sqlite3_snapshot *pSnapshot
);
/*
** CAPI3REF: Destroy a snapshot
** EXPERIMENTAL
**
** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P.
** The application must eventually free every [sqlite3_snapshot] object
** using this routine to avoid a memory leak.
**
** The [sqlite3_snapshot_free()] interface is only available when the
** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
*/
SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_snapshot_free(sqlite3_snapshot*);
/*
** Undo the hack that converts floating point types to integer for

Просмотреть файл

@ -8,7 +8,7 @@
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&title;</title>
<title>&animationInspectorTitle;</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="chrome://devtools/skin/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/animationinspector.css" type="text/css"/>

Просмотреть файл

@ -9,7 +9,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&title;</title>
<title>&fontInspectorTitle;</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="chrome://devtools/skin/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/fonts.css" type="text/css"/>

Просмотреть файл

@ -10,7 +10,7 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head>
<title>&title;</title>
<title>&layoutViewTitle;</title>
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"/>

Просмотреть файл

@ -11,8 +11,9 @@
tools. A good criteria is the language in which you'd find the best
documentation on web development on the web. -->
<!-- LOCALIZATION NOTE (title): This is the label shown in the sidebar tab -->
<!ENTITY title "Animations">
<!-- LOCALIZATION NOTE (animationInspectorTitle): This is the label shown in the
sidebar tab -->
<!ENTITY animationInspectorTitle "Animations">
<!-- LOCALIZATION NOTE (invalidElement): This is the label shown in the panel
when an invalid node is currently selected in the inspector. -->

Просмотреть файл

@ -5,7 +5,7 @@
<!-- LOCALIZATION NOTE : FILE This file contains the Font Inspector strings.
- The Font Inspector is the panel accessible in the Inspector sidebar. -->
<!ENTITY title "Fonts">
<!ENTITY fontInspectorTitle "Fonts">
<!ENTITY showAllFonts "See all the fonts used in the page">
<!ENTITY usedAs "Used as: ">
<!ENTITY system "system">

Просмотреть файл

@ -16,7 +16,7 @@
- The text appears on the bottom right corner of the layout view when
- the corresponding box is hovered. -->
<!ENTITY title "Box Model">
<!ENTITY layoutViewTitle "Box Model">
<!ENTITY margin.tooltip "margin">
<!ENTITY border.tooltip "border">
<!ENTITY padding.tooltip "padding">

Просмотреть файл

@ -1185,6 +1185,8 @@ Messages.Extended.prototype = Heritage.extend(Messages.Simple.prototype,
* grip. This is typically set to true when the object needs to be
* displayed in an array preview, or as a property value in object
* previews, etc.
* - shorten - boolean that tells the renderer to display a truncated
* grip.
* @return DOMElement
* The DOM element that displays the given grip.
*/
@ -1209,10 +1211,15 @@ Messages.Extended.prototype = Heritage.extend(Messages.Simple.prototype,
}
}
let unshortenedGrip = grip;
if (options.shorten) {
grip = this.shortenValueGrip(grip)
}
let result = this.document.createElementNS(XHTML_NS, "span");
if (isPrimitive) {
if (Widgets.URLString.prototype.containsURL.call(Widgets.URLString.prototype, grip)) {
let widget = new Widgets.URLString(this, grip, options).render();
let widget = new Widgets.URLString(this, grip, unshortenedGrip).render();
return widget.element;
}
@ -2199,11 +2206,14 @@ Widgets.MessageTimestamp.prototype = Heritage.extend(Widgets.BaseWidget.prototyp
* The owning message.
* @param string str
* The string, which contains at least one valid URL.
* @param string unshortenedStr
* The unshortened form of the string, if it was shortened.
*/
Widgets.URLString = function(message, str)
Widgets.URLString = function(message, str, unshortenedStr)
{
Widgets.BaseWidget.call(this, message);
this.str = str;
this.unshortenedStr = unshortenedStr;
};
Widgets.URLString.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
@ -2228,16 +2238,23 @@ Widgets.URLString.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
this.element.appendChild(this._renderText("\""));
// As we walk through the tokens of the source string, we make sure to preserve
// the original whitespace that seperated the tokens.
// the original whitespace that separated the tokens.
let tokens = this.str.split(/\s+/);
let textStart = 0;
let tokenStart;
for (let token of tokens) {
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
let unshortenedToken;
tokenStart = this.str.indexOf(token, textStart);
if (this._isURL(token)) {
// The last URL in the string might be shortened. If so, get the
// real URL so the rendered link can point to it.
if (i === tokens.length - 1 && this.unshortenedStr) {
unshortenedToken = this.unshortenedStr.slice(tokenStart).split(/\s+/, 1)[0];
}
this.element.appendChild(this._renderText(this.str.slice(textStart, tokenStart)));
textStart = tokenStart + token.length;
this.element.appendChild(this._renderURL(token));
this.element.appendChild(this._renderURL(token, unshortenedToken));
}
}
@ -2289,15 +2306,18 @@ Widgets.URLString.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
*
* @param string url
* The string to be rendered as a url.
* @param string fullUrl
* The unshortened form of the URL, if it was shortened.
* @return DOMElement
* An element containing the rendered string.
*/
_renderURL: function(url)
_renderURL: function(url, fullUrl)
{
let unshortened = fullUrl || url;
let result = this.el("a", {
class: "url",
title: url,
href: url,
title: unshortened,
href: unshortened,
draggable: false
}, url);
this.message._addLinkCallback(result);
@ -2414,8 +2434,7 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
if (valueIsText) {
this._text(value);
} else {
let shortVal = this.message.shortenValueGrip(value);
let valueElem = this.message._renderValueGrip(shortVal, { concise: true });
let valueElem = this.message._renderValueGrip(value, { concise: true, shorten: true });
container.appendChild(valueElem);
}
},
@ -2808,8 +2827,7 @@ Widgets.ObjectRenderers.add({
emptySlots = 0;
}
let shortVal = this.message.shortenValueGrip(item);
let elem = this.message._renderValueGrip(shortVal, { concise: true });
let elem = this.message._renderValueGrip(item, { concise: true, shorten: true });
this.element.appendChild(elem);
}
}

Просмотреть файл

@ -73,6 +73,27 @@ var inputTests = [
output: "foo://example.com",
},
// 9: Shortened URL in an array
{
input: "['http://example.com/abcdefghijabcdefghij some other text']",
output: "Array [ \"http://example.com/abcdefghijabcdef\u2026\" ]",
printOutput: "http://example.com/abcdefghijabcdefghij some other text",
expectedTab: "http://example.com/abcdefghijabcdefghij",
getClickableNode: (msg) => msg.querySelectorAll("a")[1],
},
// 10: Shortened URL in an object
{
input: "{test: 'http://example.com/abcdefghijabcdefghij some other text'}",
output: "Object { test: \"http://example.com/abcdefghijabcdef\u2026\" }",
printOutput: "[object Object]",
evalOutput: "http://example.com/abcdefghijabcdefghij some other text",
noClick: true,
consoleLogClick: true,
expectedTab: "http://example.com/abcdefghijabcdefghij",
getClickableNode: (msg) => msg.querySelectorAll("a")[1],
},
];
function test() {

Просмотреть файл

@ -1454,8 +1454,13 @@ function checkOutputForInputs(hud, inputTests) {
}],
});
let msg = [...result.matched][0];
if (entry.consoleLogClick) {
yield checkObjectClick(entry, msg);
}
if (typeof entry.inspectorIcon == "boolean") {
let msg = [...result.matched][0];
info("Checking Inspector Link: " + entry.input);
yield checkLinkToInspector(entry.inspectorIcon, msg);
}
@ -1483,11 +1488,13 @@ function checkOutputForInputs(hud, inputTests) {
hud.jsterm.clearOutput();
hud.jsterm.execute(entry.input);
let evalOutput = entry.evalOutput || entry.output;
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
name: "JS eval output: " + entry.output,
text: entry.output,
name: "JS eval output: " + entry.evalOutput,
text: entry.evalOutput,
category: CATEGORY_OUTPUT,
}],
});
@ -1504,8 +1511,13 @@ function checkOutputForInputs(hud, inputTests) {
function* checkObjectClick(entry, msg) {
info("Clicking: " + entry.input);
let body = msg.querySelector(".message-body a") ||
msg.querySelector(".message-body");
let body;
if (entry.getClickableNode) {
body = entry.getClickableNode(msg);
} else {
body = msg.querySelector(".message-body a") ||
msg.querySelector(".message-body");
}
ok(body, "the message body");
let deferredVariablesView = promise.defer();

Просмотреть файл

@ -120,16 +120,15 @@ Concrete<DeserializedNode>::allocationStack() const
}
UniquePtr<EdgeRange>
js::UniquePtr<EdgeRange>
Concrete<DeserializedNode>::edges(JSRuntime* rt, bool) const
{
UniquePtr<DeserializedEdgeRange, JS::DeletePolicy<DeserializedEdgeRange>> range(
js_new<DeserializedEdgeRange>(get()));
js::UniquePtr<DeserializedEdgeRange> range(js_new<DeserializedEdgeRange>(get()));
if (!range)
return nullptr;
return UniquePtr<EdgeRange>(range.release());
return js::UniquePtr<EdgeRange>(range.release());
}
StackFrame

Просмотреть файл

@ -7,10 +7,10 @@
#define mozilla_devtools_DeserializedNode__
#include "js/UbiNode.h"
#include "js/UniquePtr.h"
#include "mozilla/devtools/CoreDump.pb.h"
#include "mozilla/Maybe.h"
#include "mozilla/Move.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Vector.h"
// `Deserialized{Node,Edge}` translate protobuf messages from our core dump
@ -242,7 +242,6 @@ namespace ubi {
using mozilla::devtools::DeserializedNode;
using mozilla::devtools::DeserializedStackFrame;
using mozilla::UniquePtr;
template<>
struct Concrete<DeserializedNode> : public Base
@ -273,7 +272,7 @@ public:
// We ignore the `bool wantNames` parameter because we can't control whether
// the core dump was serialized with edge names or not.
UniquePtr<EdgeRange> edges(JSRuntime* rt, bool) const override;
js::UniquePtr<EdgeRange> edges(JSRuntime* rt, bool) const override;
};
template<>

Просмотреть файл

@ -17,9 +17,9 @@
#include "mozilla/dom/ChromeUtils.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/Move.h"
#include "mozilla/UniquePtr.h"
#include "js/Principals.h"
#include "js/UbiNode.h"
#include "js/UniquePtr.h"
using namespace mozilla;
using namespace mozilla::devtools;
@ -173,8 +173,6 @@ public:
namespace JS {
namespace ubi {
using mozilla::UniquePtr;
template<>
class Concrete<FakeNode> : public Base
{
@ -182,8 +180,8 @@ class Concrete<FakeNode> : public Base
return concreteTypeName;
}
UniquePtr<EdgeRange> edges(JSRuntime*, bool) const override {
return UniquePtr<EdgeRange>(js_new<PreComputedEdgeRange>(get().edges));
js::UniquePtr<EdgeRange> edges(JSRuntime*, bool) const override {
return js::UniquePtr<EdgeRange>(js_new<PreComputedEdgeRange>(get().edges));
}
Size size(mozilla::MallocSizeOf) const override {

Просмотреть файл

@ -805,6 +805,7 @@ nsDocShell::nsDocShell()
, mBlankTiming(false)
, mFrameType(eFrameTypeRegular)
, mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID)
, mUserContextId(nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID)
, mParentCharsetSource(0)
, mJSRunToCompletionDepth(0)
{
@ -14073,7 +14074,7 @@ nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
if (timelines && timelines->HasConsumer(this)) {
timelines->AddMarkerForDocShell(this, Move(
MakeUnique<JavascriptTimelineMarker>(
mozilla::MakeUnique<JavascriptTimelineMarker>(
aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START,
aAsyncStack, aAsyncCause)));
}

Просмотреть файл

@ -21,15 +21,21 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=846906
.getService(Components.interfaces.nsIAppShellService);
ok(appShellService, "Should be able to get app shell service");
var webNavigation = appShellService.createWindowlessBrowser();
ok(webNavigation, "Should be able to create windowless browser");
var windowlessBrowser = appShellService.createWindowlessBrowser();
ok(windowlessBrowser, "Should be able to create windowless browser");
var interfaceRequestor = webNavigation.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
ok(windowlessBrowser instanceof Components.interfaces.nsIWindowlessBrowser,
"Windowless browser should implement nsIWindowlessBrowser");
var webNavigation = windowlessBrowser.QueryInterface(Components.interfaces.nsIWebNavigation);
ok(webNavigation, "Windowless browser should implement nsIWebNavigation");
var interfaceRequestor = windowlessBrowser.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
ok(interfaceRequestor, "Should be able to query interface requestor interface");
var docShell = interfaceRequestor.getInterface(Components.interfaces.nsIDocShell);
ok(docShell, "Should be able to get doc shell interface");
var document = webNavigation.document;
ok(document, "Should be able to get document");
@ -55,12 +61,30 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=846906
is(rect.width, 1024);
is(rect.height, 768);
windowlessBrowser.close();
// Once the browser is closed, nsIWebNavigation and
// nsIInterfaceRequestor methods should no longer be accessible.
try {
windowlessBrowser.getInterface(Components.interfaces.nsIDocShell);
ok(false);
} catch (e) {
is(e.result, Components.results.NS_ERROR_NULL_POINTER);
}
try {
windowlessBrowser.document;
ok(false);
} catch (e) {
is(e.result, Components.results.NS_ERROR_NULL_POINTER);
}
SimpleTest.finish();
};
iframe.setAttribute("src", "http://mochi.test:8888/chrome/docshell/test/chrome/bug846906.html");
};
document.documentElement.appendChild(iframe);
]]>
</script>

Просмотреть файл

@ -19,6 +19,7 @@
#include "nsIPresShell.h"
#include "nsLayoutUtils.h"
#include "nsRuleNode.h" // For nsRuleNode::ComputePropertiesOverridingAnimation
#include "nsRuleProcessorData.h" // For ElementRuleProcessorData etc.
#include "nsTArray.h"
#include "RestyleManager.h"
@ -236,22 +237,44 @@ EffectCompositor::GetAnimationRule(dom::Element* aElement,
nsCSSPseudoElements::Type aPseudoType,
CascadeLevel aCascadeLevel)
{
// NOTE: We need to be careful about early returns in this method where
// we *don't* update mElementsToRestyle. When we get a call to
// RequestRestyle that results in a call to PostRestyleForAnimation, we
// will set a bool flag in mElementsToRestyle indicating that we've
// called PostRestyleForAnimation so we don't need to call it again
// until that restyle happens. During that restyle, if we arrive here
// and *don't* update mElementsToRestyle we'll continue to skip calling
// PostRestyleForAnimation from RequestRestyle.
if (!mPresContext || !mPresContext->IsDynamic()) {
// For print or print preview, ignore animations.
return nullptr;
}
EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
if (!effectSet) {
return nullptr;
}
if (mPresContext->RestyleManager()->SkipAnimationRules()) {
// We don't need to worry about updating mElementsToRestyle in this case
// since this is not the animation restyle we requested when we called
// PostRestyleForAnimation (see comment at start of this method).
return nullptr;
}
MaybeUpdateAnimationRule(aElement, aPseudoType, aCascadeLevel);
#ifdef DEBUG
{
auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
PseudoElementHashKey key = { aElement, aPseudoType };
MOZ_ASSERT(!elementsToRestyle.Contains(key),
"Element should no longer require a restyle after its "
"animation rule has been updated");
}
#endif
EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
if (!effectSet) {
return nullptr;
}
return effectSet->AnimationRule(aCascadeLevel);
}
@ -680,4 +703,110 @@ EffectCompositor::GetPresContext(Element* aElement)
return shell->GetPresContext();
}
// ---------------------------------------------------------
//
// Nested class: AnimationStyleRuleProcessor
//
// ---------------------------------------------------------
NS_IMPL_ISUPPORTS(EffectCompositor::AnimationStyleRuleProcessor,
nsIStyleRuleProcessor)
nsRestyleHint
EffectCompositor::AnimationStyleRuleProcessor::HasStateDependentStyle(
StateRuleProcessorData* aData)
{
return nsRestyleHint(0);
}
nsRestyleHint
EffectCompositor::AnimationStyleRuleProcessor::HasStateDependentStyle(
PseudoElementStateRuleProcessorData* aData)
{
return nsRestyleHint(0);
}
bool
EffectCompositor::AnimationStyleRuleProcessor::HasDocumentStateDependentStyle(
StateRuleProcessorData* aData)
{
return false;
}
nsRestyleHint
EffectCompositor::AnimationStyleRuleProcessor::HasAttributeDependentStyle(
AttributeRuleProcessorData* aData,
RestyleHintData& aRestyleHintDataResult)
{
return nsRestyleHint(0);
}
bool
EffectCompositor::AnimationStyleRuleProcessor::MediumFeaturesChanged(
nsPresContext* aPresContext)
{
return false;
}
void
EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
ElementRuleProcessorData* aData)
{
nsIStyleRule *rule =
mCompositor->GetAnimationRule(aData->mElement,
nsCSSPseudoElements::ePseudo_NotPseudoElement,
mCascadeLevel);
if (rule) {
aData->mRuleWalker->Forward(rule);
aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();
}
}
void
EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
PseudoElementRuleProcessorData* aData)
{
if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
return;
}
nsIStyleRule *rule =
mCompositor->GetAnimationRule(aData->mElement,
aData->mPseudoType,
mCascadeLevel);
if (rule) {
aData->mRuleWalker->Forward(rule);
aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();
}
}
void
EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
AnonBoxRuleProcessorData* aData)
{
}
#ifdef MOZ_XUL
void
EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
XULTreeRuleProcessorData* aData)
{
}
#endif
size_t
EffectCompositor::AnimationStyleRuleProcessor::SizeOfExcludingThis(
MallocSizeOf aMallocSizeOf) const
{
return 0;
}
size_t
EffectCompositor::AnimationStyleRuleProcessor::SizeOfIncludingThis(
MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
} // namespace mozilla

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/EnumeratedArray.h"
#include "mozilla/Maybe.h"
#include "mozilla/OwningNonNull.h"
#include "mozilla/Pair.h"
#include "mozilla/PseudoElementHashEntry.h"
#include "mozilla/RefPtr.h"
@ -16,6 +17,7 @@
#include "nsCSSPseudoElements.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDataHashtable.h"
#include "nsIStyleRuleProcessor.h"
#include "nsTArray.h"
class nsCSSPropertySet;
@ -39,7 +41,13 @@ class EffectCompositor
public:
explicit EffectCompositor(nsPresContext* aPresContext)
: mPresContext(aPresContext)
{ }
{
for (size_t i = 0; i < kCascadeLevelCount; i++) {
CascadeLevel cascadeLevel = CascadeLevel(i);
mRuleProcessors[cascadeLevel] =
new AnimationStyleRuleProcessor(this, cascadeLevel);
}
}
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(EffectCompositor)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(EffectCompositor)
@ -123,6 +131,11 @@ public:
// elements.
void AddStyleUpdatesTo(RestyleTracker& aTracker);
nsIStyleRuleProcessor* RuleProcessor(CascadeLevel aCascadeLevel) const
{
return mRuleProcessors[aCascadeLevel];
}
static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
nsCSSProperty aProperty);
@ -214,6 +227,51 @@ private:
EnumeratedArray<CascadeLevel, CascadeLevel(kCascadeLevelCount),
nsDataHashtable<PseudoElementHashEntry, bool>>
mElementsToRestyle;
class AnimationStyleRuleProcessor final : public nsIStyleRuleProcessor
{
public:
AnimationStyleRuleProcessor(EffectCompositor* aCompositor,
CascadeLevel aCascadeLevel)
: mCompositor(aCompositor)
, mCascadeLevel(aCascadeLevel)
{
MOZ_ASSERT(aCompositor);
}
NS_DECL_ISUPPORTS
// nsIStyleRuleProcessor (parts)
nsRestyleHint HasStateDependentStyle(
StateRuleProcessorData* aData) override;
nsRestyleHint HasStateDependentStyle(
PseudoElementStateRuleProcessorData* aData) override;
bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override;
nsRestyleHint HasAttributeDependentStyle(
AttributeRuleProcessorData* aData,
RestyleHintData& aRestyleHintDataResult) override;
bool MediumFeaturesChanged(nsPresContext* aPresContext) override;
void RulesMatching(ElementRuleProcessorData* aData) override;
void RulesMatching(PseudoElementRuleProcessorData* aData) override;
void RulesMatching(AnonBoxRuleProcessorData* aData) override;
#ifdef MOZ_XUL
void RulesMatching(XULTreeRuleProcessorData* aData) override;
#endif
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
const MOZ_MUST_OVERRIDE override;
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
const MOZ_MUST_OVERRIDE override;
private:
~AnimationStyleRuleProcessor() = default;
EffectCompositor* mCompositor;
CascadeLevel mCascadeLevel;
};
EnumeratedArray<CascadeLevel, CascadeLevel(kCascadeLevelCount),
OwningNonNull<AnimationStyleRuleProcessor>>
mRuleProcessors;
};
} // namespace mozilla

Просмотреть файл

@ -108,6 +108,24 @@ EffectSet::GetOrCreateEffectSet(dom::Element* aElement,
return effectSet;
}
/* static */ void
EffectSet::DestroyEffectSet(dom::Element* aElement,
nsCSSPseudoElements::Type aPseudoType)
{
nsIAtom* propName = GetEffectSetPropertyAtom(aPseudoType);
EffectSet* effectSet =
static_cast<EffectSet*>(aElement->GetProperty(propName));
if (!effectSet) {
return;
}
MOZ_ASSERT(!effectSet->IsBeingEnumerated(),
"Should not destroy an effect set while it is being enumerated");
effectSet = nullptr;
aElement->DeleteProperty(propName);
}
void
EffectSet::UpdateAnimationGeneration(nsPresContext* aPresContext)
{

Просмотреть файл

@ -8,6 +8,7 @@
#define mozilla_EffectSet_h
#include "mozilla/AnimValuesStyleRule.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/TimeStamp.h"
@ -32,6 +33,7 @@ public:
EffectSet()
: mCascadeNeedsUpdate(false)
, mAnimationGeneration(0)
, mActiveIterators(0)
#ifdef DEBUG
, mCalledPropertyDtor(false)
#endif
@ -43,6 +45,9 @@ public:
{
MOZ_ASSERT(mCalledPropertyDtor,
"must call destructor through element property dtor");
MOZ_ASSERT(mActiveIterators == 0,
"Effect set should not be destroyed while it is being "
"enumerated");
MOZ_COUNT_DTOR(EffectSet);
}
static void PropertyDtor(void* aObject, nsIAtom* aPropertyName,
@ -56,6 +61,8 @@ public:
static EffectSet* GetEffectSet(const nsIFrame* aFrame);
static EffectSet* GetOrCreateEffectSet(dom::Element* aElement,
nsCSSPseudoElements::Type aPseudoType);
static void DestroyEffectSet(dom::Element* aElement,
nsCSSPseudoElements::Type aPseudoType);
void AddEffect(dom::KeyframeEffectReadOnly& aEffect);
void RemoveEffect(dom::KeyframeEffectReadOnly& aEffect);
@ -74,20 +81,35 @@ public:
class Iterator
{
public:
explicit Iterator(OwningEffectSet::Iterator&& aHashIterator)
: mHashIterator(mozilla::Move(aHashIterator))
, mIsEndIterator(false) { }
Iterator(Iterator&& aOther)
: mHashIterator(mozilla::Move(aOther.mHashIterator))
, mIsEndIterator(aOther.mIsEndIterator) { }
static Iterator EndIterator(OwningEffectSet::Iterator&& aHashIterator)
explicit Iterator(EffectSet& aEffectSet)
: mEffectSet(aEffectSet)
, mHashIterator(mozilla::Move(aEffectSet.mEffects.Iter()))
, mIsEndIterator(false)
{
Iterator result(mozilla::Move(aHashIterator));
mEffectSet.mActiveIterators++;
}
Iterator(Iterator&& aOther)
: mEffectSet(aOther.mEffectSet)
, mHashIterator(mozilla::Move(aOther.mHashIterator))
, mIsEndIterator(aOther.mIsEndIterator)
{
mEffectSet.mActiveIterators++;
}
static Iterator EndIterator(EffectSet& aEffectSet)
{
Iterator result(aEffectSet);
result.mIsEndIterator = true;
return result;
}
~Iterator()
{
MOZ_ASSERT(mEffectSet.mActiveIterators > 0);
mEffectSet.mActiveIterators--;
}
bool operator!=(const Iterator& aOther) const {
if (Done() || aOther.Done()) {
return Done() != aOther.Done();
@ -117,15 +139,19 @@ public:
return mIsEndIterator || mHashIterator.Done();
}
EffectSet& mEffectSet;
OwningEffectSet::Iterator mHashIterator;
bool mIsEndIterator;
};
Iterator begin() { return Iterator(mEffects.Iter()); }
Iterator end()
{
return Iterator::EndIterator(mEffects.Iter());
}
friend class Iterator;
Iterator begin() { return Iterator(*this); }
Iterator end() { return Iterator::EndIterator(*this); }
#ifdef DEBUG
bool IsBeingEnumerated() const { return mActiveIterators != 0; }
#endif
bool IsEmpty() const { return mEffects.IsEmpty(); }
RefPtr<AnimValuesStyleRule>& AnimationRule(EffectCompositor::CascadeLevel
@ -195,6 +221,10 @@ private:
// the animation manager.
uint64_t mAnimationGeneration;
// Track how many iterators are referencing this effect set when we are
// destroyed, we can assert that nothing is still pointing to us.
DebugOnly<uint64_t> mActiveIterators;
#ifdef DEBUG
bool mCalledPropertyDtor;
#endif

Просмотреть файл

@ -180,6 +180,14 @@ KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
RequestRestyle(mTarget, mPseudoType, restyleType,
mAnimation->CascadeLevel());
}
// If we're not relevant, we will have been removed from the EffectSet.
// As a result, when the restyle we requested above is fulfilled, our
// ComposeStyle will not get called and mProgressOnLastCompose will not
// be updated. Instead, we need to manually clear it.
if (!isRelevant) {
mProgressOnLastCompose.SetNull();
}
}
}
@ -613,6 +621,9 @@ KeyframeEffectReadOnly::UpdateTargetRegistration()
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
if (effectSet) {
effectSet->RemoveEffect(*this);
if (effectSet->IsEmpty()) {
EffectSet::DestroyEffectSet(mTarget, mPseudoType);
}
}
}
}

Просмотреть файл

@ -1256,7 +1256,7 @@ WebSocket::Constructor(const GlobalObject& aGlobal,
}
unsigned lineno, column;
JS::AutoFilename file;
JS::UniqueChars file;
if (!JS::DescribeScriptedCaller(aGlobal.Context(), &file, &lineno,
&column)) {
NS_WARNING("Failed to get line number and filename in workers.");
@ -1491,7 +1491,7 @@ WebSocketImpl::Init(JSContext* aCx,
MOZ_ASSERT(aCx);
unsigned lineno, column;
JS::AutoFilename file;
JS::UniqueChars file;
if (JS::DescribeScriptedCaller(aCx, &file, &lineno, &column)) {
mScriptFile = file.get();
mScriptLine = lineno;

Просмотреть файл

@ -6636,6 +6636,48 @@ nsContentUtils::FindInternalContentViewer(const nsACString& aType,
return nullptr;
}
static void
ReportPatternCompileFailure(nsAString& aPattern, nsIDocument* aDocument,
JSContext* cx)
{
MOZ_ASSERT(JS_IsExceptionPending(cx));
JS::RootedValue exn(cx);
if (!JS_GetPendingException(cx, &exn)) {
return;
}
if (!exn.isObject()) {
// If pending exception is not an object, it should be OOM.
return;
}
JS::AutoSaveExceptionState savedExc(cx);
JS::RootedObject exnObj(cx, &exn.toObject());
JS::RootedValue messageVal(cx);
if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
return;
}
MOZ_ASSERT(messageVal.isString());
JS::RootedString messageStr(cx, messageVal.toString());
MOZ_ASSERT(messageStr);
nsAutoString wideMessage;
if (!AssignJSString(cx, wideMessage, messageStr)) {
return;
}
const nsString& pattern = PromiseFlatString(aPattern);
const char16_t *strings[] = { pattern.get(), wideMessage.get() };
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
NS_LITERAL_CSTRING("DOM"),
aDocument,
nsContentUtils::eDOM_PROPERTIES,
"PatternAttributeCompileFailure",
strings, ArrayLength(strings));
savedExc.drop();
}
// static
bool
nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
@ -6662,8 +6704,12 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
JS::Rooted<JSObject*> re(cx,
JS_NewUCRegExpObjectNoStatics(cx,
static_cast<char16_t*>(aPattern.BeginWriting()),
aPattern.Length(), 0));
aPattern.Length(), JSREG_UNICODE));
if (!re) {
// Remove extra patterns added above to report with the original pattern.
aPattern.Cut(0, 4);
aPattern.Cut(aPattern.Length() - 2, 2);
ReportPatternCompileFailure(aPattern, aDocument, cx);
return true;
}

Просмотреть файл

@ -196,6 +196,12 @@ nsDOMAttributeMap::GetSupportedNames(unsigned aFlags,
return;
}
// For HTML elements in HTML documents, only include names that are still the
// same after ASCII-lowercasing, since our named getter will end up
// ASCII-lowercasing the given string.
bool lowercaseNamesOnly =
mContent->IsHTMLElement() && mContent->IsInHTMLDocument();
const uint32_t count = mContent->GetAttrCount();
bool seenNonAtomName = false;
for (uint32_t i = 0; i < count; i++) {
@ -204,9 +210,18 @@ nsDOMAttributeMap::GetSupportedNames(unsigned aFlags,
nsString qualifiedName;
name->GetQualifiedName(qualifiedName);
if (lowercaseNamesOnly &&
nsContentUtils::StringContainsASCIIUpper(qualifiedName)) {
continue;
}
// Omit duplicates. We only need to do this check if we've seen a non-atom
// name, because that's the only way we can have two identical qualified
// names.
if (seenNonAtomName && aNames.Contains(qualifiedName)) {
continue;
}
aNames.AppendElement(qualifiedName);
}
}

Просмотреть файл

@ -2112,6 +2112,7 @@ GK_ATOM(baseURIProperty, "baseURIProperty")
GK_ATOM(lockedStyleStates, "lockedStyleStates")
GK_ATOM(apzCallbackTransform, "apzCallbackTransform")
GK_ATOM(restylableAnonymousNode, "restylableAnonymousNode")
GK_ATOM(paintRequestTime, "PaintRequestTime")
// Languages for lang-specific transforms
GK_ATOM(Japanese, "ja")

Просмотреть файл

@ -10552,7 +10552,7 @@ nsGlobalWindow::ShowSlowScriptDialog()
}
// Check if we should offer the option to debug
JS::AutoFilename filename;
JS::UniqueChars filename;
unsigned lineno;
bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno);

Просмотреть файл

@ -37,7 +37,7 @@ bool
nsJSUtils::GetCallingLocation(JSContext* aContext, nsACString& aFilename,
uint32_t* aLineno, uint32_t* aColumn)
{
JS::AutoFilename filename;
JS::UniqueChars filename;
if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) {
return false;
}
@ -50,7 +50,7 @@ bool
nsJSUtils::GetCallingLocation(JSContext* aContext, nsAString& aFilename,
uint32_t* aLineno, uint32_t* aColumn)
{
JS::AutoFilename filename;
JS::UniqueChars filename;
if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno, aColumn)) {
return false;
}

Просмотреть файл

@ -705,8 +705,7 @@ nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
}
nsObjectLoadingContent::nsObjectLoadingContent()
: mRewrittenYoutubeEmbed(false)
, mType(eType_Loading)
: mType(eType_Loading)
, mFallbackType(eFallbackAlternate)
, mRunID(0)
, mHasRunID(false)
@ -1787,7 +1786,6 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
NS_NOTREACHED("Unrecognized plugin-loading tag");
}
mRewrittenYoutubeEmbed = false;
// Note that the baseURI changing could affect the newURI, even if uriStr did
// not change.
if (!uriStr.IsEmpty()) {
@ -1804,7 +1802,6 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
uriStr,
thisContent->OwnerDoc(),
newBaseURI);
mRewrittenYoutubeEmbed = true;
newMime = NS_LITERAL_CSTRING("text/html");
}
@ -2616,8 +2613,7 @@ nsObjectLoadingContent::GetCapabilities() const
{
return eSupportImages |
eSupportPlugins |
eSupportDocuments |
eSupportSVG;
eSupportDocuments;
}
void
@ -2772,10 +2768,7 @@ nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
return eType_Document;
}
// SVGs load as documents, but are their own capability
bool isSVG = aMIMEType.LowerCaseEqualsLiteral("image/svg+xml");
Capabilities supportType = isSVG ? eSupportSVG : eSupportDocuments;
if ((caps & supportType) && IsSupportedDocument(aMIMEType)) {
if ((caps & eSupportDocuments) && IsSupportedDocument(aMIMEType)) {
return eType_Document;
}

Просмотреть файл

@ -287,15 +287,14 @@ class nsObjectLoadingContent : public nsImageLoadingContent
eSupportDocuments = 1u << 2, // Documents are supported
// (nsIDocumentLoaderFactory)
// This flag always includes SVG
eSupportSVG = 1u << 3, // SVG is supported (image/svg+xml)
eSupportClassID = 1u << 4, // The classid attribute is supported
eSupportClassID = 1u << 3, // The classid attribute is supported
// If possible to get a *plugin* type from the type attribute *or* file
// extension, we can use that type and begin loading the plugin before
// opening a channel.
// A side effect of this is if the channel fails, the plugin is still
// running.
eAllowPluginSkipChannel = 1u << 5
eAllowPluginSkipChannel = 1u << 4
};
/**
@ -331,12 +330,6 @@ class nsObjectLoadingContent : public nsImageLoadingContent
*/
virtual nsContentPolicyType GetContentPolicyType() const = 0;
// True if object represents an object/embed tag pointing to a flash embed
// for a youtube video. When possible (see IsRewritableYoutubeEmbed function
// comments for details), we change these to try to load HTML5 versions of
// videos.
bool mRewrittenYoutubeEmbed : 1;
private:
// Object parameter changes returned by UpdateObjectParameters

Просмотреть файл

@ -4,6 +4,8 @@
webidl_base := $(topsrcdir)/dom/webidl
ifdef COMPILE_ENVIRONMENT
# Generated by moz.build
include webidlsrcs.mk
@ -54,6 +56,8 @@ codegen.pp: $(codegen_dependencies)
compiletests:
$(call SUBMAKE,libs,test)
endif
GARBAGE += \
codegen.pp \
codegen.json \

38
dom/cache/DBSchema.cpp поставляемый
Просмотреть файл

@ -37,7 +37,7 @@ const int32_t kFirstShippedSchemaVersion = 15;
namespace {
// Update this whenever the DB schema is changed.
const int32_t kLatestSchemaVersion = 17;
const int32_t kLatestSchemaVersion = 18;
// ---------
// The following constants define the SQL schema. These are defined in the
@ -204,8 +204,7 @@ static_assert(int(RequestCache::Default) == 0 &&
int(RequestCache::Reload) == 2 &&
int(RequestCache::No_cache) == 3 &&
int(RequestCache::Force_cache) == 4 &&
int(RequestCache::Only_if_cached) == 5 &&
int(RequestCache::EndGuard_) == 6,
int(RequestCache::EndGuard_) == 5,
"RequestCache values are as expected");
static_assert(int(RequestRedirect::Follow) == 0 &&
int(RequestRedirect::Error) == 1 &&
@ -2411,11 +2410,13 @@ struct Migration
// the version by a single increment. Don't skip versions.
nsresult MigrateFrom15To16(mozIStorageConnection* aConn);
nsresult MigrateFrom16To17(mozIStorageConnection* aConn);
nsresult MigrateFrom17To18(mozIStorageConnection* aConn);
// Configure migration functions to run for the given starting version.
Migration sMigrationList[] = {
Migration(15, MigrateFrom15To16),
Migration(16, MigrateFrom16To17),
Migration(17, MigrateFrom17To18),
};
uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration);
@ -2653,6 +2654,37 @@ MigrateFrom16To17(mozIStorageConnection* aConn)
return rv;
}
nsresult
MigrateFrom17To18(mozIStorageConnection* aConn)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
mozStorageTransaction trans(aConn, true,
mozIStorageConnection::TRANSACTION_IMMEDIATE);
// This migration is needed in order to remove "only-if-cached" RequestCache
// values from the database. This enum value was removed from the spec in
// https://github.com/whatwg/fetch/issues/39 but we unfortunately happily
// accepted this value in the Request constructor.
//
// There is no good value to upgrade this to, so we just stick to "default".
static_assert(int(RequestCache::Default) == 0,
"This is where the 0 below comes from!");
nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"UPDATE entries SET request_cache = 0 "
"WHERE request_cache = 5;"
));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = aConn->SetSchemaVersion(18);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return rv;
}
} // anonymous namespace
} // namespace db

3
dom/cache/test/xpcshell/head.js поставляемый
Просмотреть файл

@ -18,6 +18,9 @@ var sts = Cc['@mozilla.org/network/stream-transport-service;1']
var hash = Cc['@mozilla.org/security/hash;1']
.createInstance(Ci.nsICryptoHash);
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
prefs.setBoolPref("dom.requestcache.enabled", true);
// Expose Cache and Fetch symbols on the global
Cu.importGlobalProperties(['caches', 'fetch']);

Двоичные данные
dom/cache/test/xpcshell/schema_15_profile.zip поставляемый

Двоичный файл не отображается.

1
dom/cache/test/xpcshell/test_migration.js поставляемый
Просмотреть файл

@ -20,6 +20,7 @@ function run_test() {
requestList.forEach(function(request) {
ok(request, 'each request in list should be non-null');
ok(request.redirect === 'follow', 'request.redirect should default to "follow"');
ok(request.cache === 'default', 'request.cache should have been updated to "default"' + request.cache);
});
return Promise.all(requestList.map(function(request) {
return cache.match(request);

Просмотреть файл

@ -449,6 +449,9 @@ const kEventConstructors = {
return new RTCPeerConnectionIceEvent(aName, aProps);
},
},
RTCTrackEvent: {
// Difficult to test required arguments.
},
ScrollAreaEvent: { create: function (aName, aProps) {
var e = document.createEvent("scrollareaevent");
e.initScrollAreaEvent(aName, aProps.bubbles, aProps.cancelable,

Просмотреть файл

@ -343,7 +343,7 @@ HTMLSharedObjectElement::GetCapabilities() const
{
uint32_t capabilities = eSupportPlugins | eAllowPluginSkipChannel;
if (mNodeInfo->Equals(nsGkAtoms::embed)) {
capabilities |= eSupportSVG | eSupportImages | eSupportDocuments;
capabilities |= eSupportImages | eSupportDocuments;
}
return capabilities;

Просмотреть файл

@ -121,6 +121,12 @@ function checkInvalidPattern(element, completeCheck)
}
}
function checkSyntaxError(element)
{
ok(!element.validity.patternMismatch,
"On SyntaxError, element should not suffer");
}
function checkPatternValidity(element)
{
element.pattern = "foo";
@ -165,17 +171,6 @@ function checkPatternValidity(element)
element.value = "foo";
checkInvalidPattern(element);
// We need '\\\\' because '\\' will produce '\\' and we want to escape the '\'
// for the regexp.
element.pattern = "foo\\\\bar";
element.value = "foo\\bar";
checkValidPattern(element);
// The same way, we want to escape the ' in the pattern.
element.pattern = "foo\\'bar";
element.value = "foo'bar";
checkValidPattern(element);
// Check for 'i' flag disabled. Should be case sensitive.
element.value = "Foo";
checkInvalidPattern(element);
@ -183,6 +178,20 @@ function checkPatternValidity(element)
// We can't check for the 'g' flag because we only test, we don't execute.
// We can't check for the 'm' flag because .value shouldn't contain line breaks.
// We need '\\\\' because '\\' will produce '\\' and we want to escape the '\'
// for the regexp.
element.pattern = "foo\\\\bar";
element.value = "foo\\bar";
checkValidPattern(element);
// We may want to escape the ' in the pattern, but this is a SyntaxError
// when unicode flag is set.
element.pattern = "foo\\'bar";
element.value = "foo'bar";
checkSyntaxError(element);
element.value = "baz";
checkSyntaxError(element);
// We should check the pattern attribute do not pollute |RegExp.lastParen|.
is(RegExp.lastParen, "", "RegExp.lastParen should be the empty string");
@ -254,6 +263,33 @@ function checkPatternValidity(element)
element.removeAttribute('pattern');
checkValidPattern(element, true);
// Unicode pattern
for (var pattern of ["\\u{1F438}{2}", "\u{1F438}{2}",
"\\uD83D\\uDC38{2}", "\uD83D\uDC38{2}",
"\u{D83D}\u{DC38}{2}"]) {
element.pattern = pattern;
element.value = "\u{1F438}\u{1F438}";
checkValidPattern(element);
element.value = "\uD83D\uDC38\uD83D\uDC38";
checkValidPattern(element);
element.value = "\uD83D\uDC38\uDC38";
checkInvalidPattern(element);
}
element.pattern = "\\u{D83D}\\u{DC38}{2}";
element.value = "\u{1F438}\u{1F438}";
checkInvalidPattern(element);
element.value = "\uD83D\uDC38\uD83D\uDC38";
checkInvalidPattern(element);
element.value = "\uD83D\uDC38\uDC38";
checkInvalidPattern(element);
}
var input = document.getElementById('i');

Просмотреть файл

@ -57,7 +57,8 @@ interface nsIServiceWorkerRegistrationInfo : nsISupports
// Allows to get the related nsIServiceWorkerInfo for a given
// nsIWorkerDebugger. Over time we shouldn't need this anymore,
// and instead always control then nsIWorkerDebugger from
// nsIServiceWorkerInfo and not the other way around.
// nsIServiceWorkerInfo and not the other way around. Returns
// null if the service worker is no longer registered.
nsIServiceWorkerInfo getWorkerByID(in unsigned long long aID);
void addListener(in nsIServiceWorkerRegistrationInfoListener listener);

Просмотреть файл

@ -189,3 +189,4 @@ InterceptionRejectedResponseWithURL=Failed to load '%1$S'. A ServiceWorker passe
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "promise", "FetchEvent.respondWith()", or "Response". %1$S is a URL. %2$S is an error string.
InterceptedNonResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that resolved with non-Response value '%2$S'.
ExecCommandCutCopyDeniedNotInputDriven=document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler.
PatternAttributeCompileFailure=Unable to check <input pattern='%S'> because the pattern is not a valid regexp: %S

Просмотреть файл

@ -405,8 +405,9 @@ RTCPeerConnection.prototype = {
"InvalidStateError");
}
this.makeGetterSetterEH("onaddstream");
this.makeGetterSetterEH("onaddtrack");
this.makeGetterSetterEH("ontrack");
this.makeLegacyGetterSetterEH("onaddstream", "Use peerConnection.ontrack instead.");
this.makeLegacyGetterSetterEH("onaddtrack", "Use peerConnection.ontrack instead.");
this.makeGetterSetterEH("onicecandidate");
this.makeGetterSetterEH("onnegotiationneeded");
this.makeGetterSetterEH("onsignalingstatechange");
@ -663,6 +664,18 @@ RTCPeerConnection.prototype = {
});
},
makeLegacyGetterSetterEH: function(name, msg) {
Object.defineProperty(this, name,
{
get:function() { return this.getEH(name); },
set:function(h) {
this.logWarning(name + " is deprecated! " + msg,
null, 0);
return this.setEH(name, h);
}
});
},
_addIdentityAssertion: function(sdpPromise, origin) {
if (!this._localIdp.enabled) {
return sdpPromise;
@ -1353,15 +1366,16 @@ PeerConnectionObserver.prototype = {
// STUN requests.
handleIceConnectionStateChange: function(iceConnectionState) {
if (this._dompc.iceConnectionState === 'new') {
let pc = this._dompc;
if (pc.iceConnectionState === 'new') {
var checking_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_CHECKING_RATE");
if (iceConnectionState === 'checking') {
checking_histogram.add(true);
} else if (iceConnectionState === 'failed') {
checking_histogram.add(false);
}
} else if (this._dompc.iceConnectionState === 'checking') {
var success_histogram = Services.telemetry.getHistogramById(this._dompc._isLoop ?
} else if (pc.iceConnectionState === 'checking') {
var success_histogram = Services.telemetry.getHistogramById(pc._isLoop ?
"LOOP_ICE_SUCCESS_RATE" : "WEBRTC_ICE_SUCCESS_RATE");
if (iceConnectionState === 'completed' ||
iceConnectionState === 'connected') {
@ -1372,10 +1386,10 @@ PeerConnectionObserver.prototype = {
}
if (iceConnectionState === 'failed') {
this._dompc.logError("ICE failed, see about:webrtc for more details", null, 0);
pc.logError("ICE failed, see about:webrtc for more details", null, 0);
}
this._dompc.changeIceConnectionState(iceConnectionState);
pc.changeIceConnectionState(iceConnectionState);
},
// This method is responsible for updating iceGatheringState. This
@ -1430,11 +1444,11 @@ PeerConnectionObserver.prototype = {
},
onGetStatsSuccess: function(dict) {
let chromeobj = new RTCStatsReport(this._dompc._win, dict);
let webidlobj = this._dompc._win.RTCStatsReport._create(this._dompc._win,
chromeobj);
let pc = this._dompc;
let chromeobj = new RTCStatsReport(pc._win, dict);
let webidlobj = pc._win.RTCStatsReport._create(pc._win, chromeobj);
chromeobj.makeStatsPublic();
this._dompc._onGetStatsSuccess(webidlobj);
pc._onGetStatsSuccess(webidlobj);
},
onGetStatsError: function(code, message) {
@ -1447,20 +1461,34 @@ PeerConnectionObserver.prototype = {
this.dispatchEvent(ev);
},
onRemoveStream: function(stream, type) {
onRemoveStream: function(stream) {
this.dispatchEvent(new this._dompc._win.MediaStreamEvent("removestream",
{ stream: stream }));
},
onAddTrack: function(track) {
let ev = new this._dompc._win.MediaStreamTrackEvent("addtrack",
{ track: track });
onAddTrack: function(track, streams) {
let pc = this._dompc;
let receiver = pc._win.RTCRtpReceiver._create(pc._win,
new RTCRtpReceiver(this,
track));
pc._receivers.push(receiver);
let ev = new pc._win.RTCTrackEvent("track",
{ receiver: receiver,
track: track,
streams: streams });
this.dispatchEvent(ev);
// Fire legacy event as well for a little bit.
ev = new pc._win.MediaStreamTrackEvent("addtrack", { track: track });
this.dispatchEvent(ev);
},
onRemoveTrack: function(track, type) {
this.dispatchEvent(new this._dompc._win.MediaStreamTrackEvent("removetrack",
{ track: track }));
onRemoveTrack: function(track) {
let pc = this._dompc;
let i = pc._receivers.findIndex(receiver => receiver.track == track);
if (i >= 0) {
pc._receivers.splice(i, 1);
}
},
onReplaceTrackSuccess: function() {
@ -1534,7 +1562,7 @@ RTCRtpSender.prototype = {
};
function RTCRtpReceiver(pc, track) {
this.pc = pc;
this._pc = pc;
this.track = track;
}
RTCRtpReceiver.prototype = {

Просмотреть файл

@ -19,6 +19,12 @@
#include <ui/Fence.h>
#endif
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
#include <gui/Surface.h>
#else
#include <gui/SurfaceTextureClient.h>
#endif
#include "mozilla/layers/GrallocTextureClient.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/Preferences.h"
@ -231,11 +237,11 @@ OmxDecoder::AllocateMediaResources()
#endif
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
mNativeWindowClient = new GonkNativeWindowClient(producer);
mNativeWindowClient = new Surface(producer);
#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow->getBufferQueue());
mNativeWindowClient = new Surface(mNativeWindow->getBufferQueue());
#else
mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow);
mNativeWindowClient = new SurfaceTextureClient(mNativeWindow);
#endif
// Experience with OMX codecs is that only the HW decoders are

Просмотреть файл

@ -6,7 +6,6 @@
#include <stagefright/MediaExtractor.h>
#include "GonkNativeWindow.h"
#include "GonkNativeWindowClient.h"
#include "mozilla/layers/FenceUtils.h"
#include "MP3FrameParser.h"
#include "MPAPI.h"
@ -42,7 +41,8 @@ class OmxDecoder : public RefBase {
AbstractMediaDecoder *mDecoder;
sp<GonkNativeWindow> mNativeWindow;
sp<GonkNativeWindowClient> mNativeWindowClient;
sp<ANativeWindow> mNativeWindowClient;
sp<MediaSource> mVideoTrack;
sp<OMXCodecProxy> mVideoSource;
sp<MediaSource> mAudioOffloadTrack;

Просмотреть файл

@ -154,6 +154,12 @@ VorbisDataDecoder::DoDecode(MediaRawData* aSample)
MOZ_ASSERT(mPacketCount >= 3);
if (!mLastFrameTime || mLastFrameTime.ref() != aSample->mTime) {
// We are starting a new block.
mFrames = 0;
mLastFrameTime = Some(aSample->mTime);
}
ogg_packet pkt = InitVorbisPacket(aData, aLength, false, false, -1, mPacketCount++);
bool first_packet = mPacketCount == 4;
@ -197,7 +203,7 @@ VorbisDataDecoder::DoDecode(MediaRawData* aSample)
NS_WARNING("Int overflow converting WebM audio duration");
return -1;
}
CheckedInt64 total_duration = FramesToUsecs(aTotalFrames,
CheckedInt64 total_duration = FramesToUsecs(mFrames,
mVorbisDsp.vi->rate);
if (!total_duration.isValid()) {
NS_WARNING("Int overflow converting WebM audio total_duration");
@ -218,7 +224,7 @@ VorbisDataDecoder::DoDecode(MediaRawData* aSample)
Move(buffer),
mVorbisDsp.vi->channels,
mVorbisDsp.vi->rate));
mFrames += aTotalFrames;
mFrames += frames;
if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) {
return -1;
}
@ -252,7 +258,7 @@ VorbisDataDecoder::Flush()
// aren't fatal and it fails when ResetDecode is called at a
// time when no vorbis data has been read.
vorbis_synthesis_restart(&mVorbisDsp);
mFrames = 0;
mLastFrameTime.reset();
return NS_OK;
}

Просмотреть файл

@ -7,6 +7,7 @@
#define VorbisDecoder_h_
#include "PlatformDecoderModule.h"
#include "mozilla/Maybe.h"
#ifdef MOZ_TREMOR
#include "tremor/ivorbiscodec.h"
@ -52,6 +53,7 @@ private:
int64_t mPacketCount;
int64_t mFrames;
Maybe<int64_t> mLastFrameTime;
};
} // namespace mozilla

Просмотреть файл

@ -9,6 +9,9 @@
#include "FFmpegLog.h"
#include "mozilla/Preferences.h"
#include "mozilla/Types.h"
#include "nsIFile.h"
#include "nsXPCOMPrivate.h" // for XUL_DLL
#include "prmem.h"
#include "prlink.h"
#if defined(XP_WIN)
@ -98,14 +101,43 @@ FFmpegRuntimeLinker::Link()
FFMPEG_LOG(" ]\n");
#ifdef MOZ_FFVPX
// We retrieve the path of the XUL library as this is where mozavcodec and
// mozavutil libs are located.
char* path =
PR_GetLibraryFilePathname(XUL_DLL, (PRFuncPtr)&FFmpegRuntimeLinker::Link);
if (!path) {
return false;
}
nsCOMPtr<nsIFile> xulFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
if (!xulFile ||
NS_FAILED(xulFile->InitWithNativePath(nsDependentCString(path)))) {
PR_Free(path);
return false;
}
PR_Free(path);
nsCOMPtr<nsIFile> rootDir;
if (NS_FAILED(xulFile->GetParent(getter_AddRefs(rootDir))) || !rootDir) {
return false;
}
nsAutoCString rootPath;
if (NS_FAILED(rootDir->GetNativePath(rootPath))) {
return false;
}
char* libname = NULL;
/* Get the platform-dependent library name of the module */
libname = PR_GetLibraryName(nullptr, "mozavutil");
MOZ_ASSERT(libname);
libname = PR_GetLibraryName(rootPath.get(), "mozavutil");
if (!libname) {
return false;
}
sLinkedUtilLib = MozAVLink(libname);
PR_FreeLibraryName(libname);
libname = PR_GetLibraryName(nullptr, "mozavcodec");
MOZ_ASSERT(libname);
libname = PR_GetLibraryName(rootPath.get(), "mozavcodec");
if (!libname) {
Unlink();
return false;
}
sLinkedLib = MozAVLink(libname);
PR_FreeLibraryName(libname);
if (sLinkedLib && sLinkedUtilLib) {

Просмотреть файл

@ -39,6 +39,7 @@ if CONFIG['MOZ_EME']:
DIRS += ['agnostic/eme']
if CONFIG['MOZ_FFMPEG']:
LOCAL_INCLUDES += ['/xpcom/build']
EXPORTS += [
'ffmpeg/FFmpegRuntimeLinker.h',
]

Просмотреть файл

@ -25,7 +25,7 @@ extern mozilla::LogModule* GetPDMLog();
#undef LOG
#endif
#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("GonkOmxPlatformLayer:: " arg, ##__VA_ARGS__))
#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("GonkOmxPlatformLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#define CHECK_ERR(err) \
if (err != OK) { \
@ -314,7 +314,7 @@ GonkBufferData::GetPlatformMediaData()
false,
0,
info.mImage);
LOG("GetMediaData: %p, disp width %d, height %d, pic width %d, height %d, time %ld",
LOG("%p, disp width %d, height %d, pic width %d, height %d, time %ld",
this, info.mDisplay.width, info.mDisplay.height,
info.mImage.width, info.mImage.height, mBuffer->nTimeStamp);
@ -324,7 +324,9 @@ GonkBufferData::GetPlatformMediaData()
->Then(mGonkPlatformLayer->GetTaskQueue(), __func__,
[self] () {
// Waiting for texture to be freed.
self->mTextureClientRecycleHandler->GetTextureClient()->WaitForBufferOwnership();
if (self->mTextureClientRecycleHandler) {
self->mTextureClientRecycleHandler->GetTextureClient()->WaitForBufferOwnership();
}
self->mPromise.ResolveIfExists(self, __func__);
},
[self] () {

Просмотреть файл

@ -13,9 +13,17 @@ extern mozilla::LogModule* GetPDMLog();
#ifdef LOG
#undef LOG
#undef LOGL
#endif
#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxDataDecoder::%s: " arg, __func__, ##__VA_ARGS__))
#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#define LOGL(arg, ...) \
{ \
void* p = self; \
MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, \
("OmxDataDecoder(%p)::%s: " arg, p, __func__, ##__VA_ARGS__)); \
}
#define CHECK_OMX_ERR(err) \
if (err != OMX_ErrorNone) { \
@ -117,7 +125,7 @@ OmxDataDecoder::OmxDataDecoder(const TrackInfo& aTrackInfo,
, mPortSettingsChanged(-1, "OmxDataDecoder::mPortSettingsChanged")
, mCallback(aCallback)
{
LOG("(%p)", this);
LOG("");
mOmxLayer = new OmxPromiseLayer(mOmxTaskQueue, this, aImageContainer);
nsCOMPtr<nsIRunnable> r =
@ -127,7 +135,7 @@ OmxDataDecoder::OmxDataDecoder(const TrackInfo& aTrackInfo,
OmxDataDecoder::~OmxDataDecoder()
{
LOG("(%p)", this);
LOG("");
}
void
@ -140,21 +148,27 @@ OmxDataDecoder::InitializationTask()
void
OmxDataDecoder::EndOfStream()
{
LOG("(%p)", this);
LOG("");
MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
mFlushing = true;
RefPtr<OmxDataDecoder> self = this;
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction([self] () {
self->mCallback->DrainComplete();
});
mReaderTaskQueue->Dispatch(r.forget());
mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
->Then(mReaderTaskQueue, __func__,
[self] () {
self->mFlushing = false;
self->mCallback->DrainComplete();
},
[self] () {
self->mFlushing = false;
self->mCallback->DrainComplete();
});
}
RefPtr<MediaDataDecoder::InitPromise>
OmxDataDecoder::Init()
{
LOG("(%p)", this);
LOG("");
mReaderTaskQueue = AbstractThread::GetCurrent()->AsTaskQueue();
MOZ_ASSERT(mReaderTaskQueue);
@ -181,7 +195,7 @@ OmxDataDecoder::Init()
nsresult
OmxDataDecoder::Input(MediaRawData* aSample)
{
LOG("(%p) sample %p", this, aSample);
LOG("sample %p", aSample);
MOZ_ASSERT(mInitPromise.IsEmpty());
RefPtr<OmxDataDecoder> self = this;
@ -205,7 +219,7 @@ OmxDataDecoder::Input(MediaRawData* aSample)
nsresult
OmxDataDecoder::Flush()
{
LOG("(%p)", this);
LOG("");
mFlushing = true;
@ -227,7 +241,7 @@ OmxDataDecoder::Flush()
nsresult
OmxDataDecoder::Drain()
{
LOG("(%p)", this);
LOG("");
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(this, &OmxDataDecoder::SendEosBuffer);
@ -239,7 +253,7 @@ OmxDataDecoder::Drain()
nsresult
OmxDataDecoder::Shutdown()
{
LOG("(%p)", this);
LOG("");
mShuttingDown = true;
@ -266,7 +280,7 @@ OmxDataDecoder::Shutdown()
void
OmxDataDecoder::DoAsyncShutdown()
{
LOG("(%p)", this);
LOG("");
MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
MOZ_ASSERT(!mFlushing);
@ -278,7 +292,7 @@ OmxDataDecoder::DoAsyncShutdown()
mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
->Then(mOmxTaskQueue, __func__,
[self] () -> RefPtr<OmxCommandPromise> {
LOG("DoAsyncShutdown: flush complete");
LOGL("DoAsyncShutdown: flush complete");
return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
},
[self] () {
@ -298,7 +312,7 @@ OmxDataDecoder::DoAsyncShutdown()
// Here the buffer promises are not resolved due to displaying
// in layer, it needs to wait before the layer returns the
// buffers.
LOG("DoAsyncShutdown: releasing buffers...");
LOGL("DoAsyncShutdown: releasing buffers...");
self->ReleaseBuffers(OMX_DirInput);
self->ReleaseBuffers(OMX_DirOutput);
@ -310,7 +324,7 @@ OmxDataDecoder::DoAsyncShutdown()
->CompletionPromise()
->Then(mOmxTaskQueue, __func__,
[self] () {
LOG("DoAsyncShutdown: OMX_StateLoaded, it is safe to shutdown omx");
LOGL("DoAsyncShutdown: OMX_StateLoaded, it is safe to shutdown omx");
self->mOmxLayer->Shutdown();
self->mWatchManager.Shutdown();
self->mOmxLayer = nullptr;
@ -432,7 +446,7 @@ OmxDataDecoder::EmptyBufferDone(BufferData* aData)
return;
}
LOG("Call InputExhausted()");
LOGL("Call InputExhausted()");
self->mCallback->InputExhausted();
});
@ -521,7 +535,6 @@ OmxDataDecoder::FindAvailableBuffer(OMX_DIRTYPE aType)
if (buf->mStatus == BufferData::BufferStatus::FREE) {
return buf;
}
LOG("buffer is owned by %d, type %d", buf->mStatus, aType);
}
return nullptr;
@ -558,7 +571,7 @@ OmxDataDecoder::GetBuffers(OMX_DIRTYPE aType)
void
OmxDataDecoder::ResolveInitPromise(const char* aMethodName)
{
LOG("Resolved InitPromise");
LOG("called from %s", aMethodName);
RefPtr<OmxDataDecoder> self = this;
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction([self, aMethodName] () {
@ -906,7 +919,7 @@ OmxDataDecoder::PortSettingsChanged()
->CompletionPromise()
->Then(mOmxTaskQueue, __func__,
[self] () {
LOG("PortSettingsChanged: port settings changed complete");
LOGL("PortSettingsChanged: port settings changed complete");
// finish port setting changed.
self->mPortSettingsChanged = -1;
self->FillAndEmptyBuffers();

Просмотреть файл

@ -18,7 +18,7 @@ extern mozilla::LogModule* GetPDMLog();
#undef LOG
#endif
#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxPromiseLayer:: " arg, ##__VA_ARGS__))
#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxPromiseLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
namespace mozilla {
@ -62,7 +62,7 @@ RefPtr<OmxPromiseLayer::OmxBufferPromise>
OmxPromiseLayer::FillBuffer(BufferData* aData)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
LOG("FillBuffer: buffer %p", aData->mBuffer);
LOG("buffer %p", aData->mBuffer);
RefPtr<OmxBufferPromise> p = aData->mPromise.Ensure(__func__);
@ -83,7 +83,7 @@ RefPtr<OmxPromiseLayer::OmxBufferPromise>
OmxPromiseLayer::EmptyBuffer(BufferData* aData)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
LOG("EmptyBuffer: buffer %p, size %d", aData->mBuffer, aData->mBuffer->nFilledLen);
LOG("buffer %p, size %d", aData->mBuffer, aData->mBuffer->nFilledLen);
RefPtr<OmxBufferPromise> p = aData->mPromise.Ensure(__func__);
@ -169,7 +169,7 @@ void
OmxPromiseLayer::EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData* aData)
{
MOZ_ASSERT(!!aData);
LOG("EmptyFillBufferDone: type %d, buffer %p", aType, aData->mBuffer);
LOG("type %d, buffer %p", aType, aData->mBuffer);
if (aData) {
if (aType == OMX_DirOutput) {
aData->mRawData = nullptr;
@ -222,7 +222,7 @@ OmxPromiseLayer::SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1, OMX_PTR aCmd
return OmxCommandPromise::CreateAndReject(failure, __func__);
}
} else {
LOG("SendCommand: OMX_CommandFlush parameter error");
LOG("OMX_CommandFlush parameter error");
OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush);
return OmxCommandPromise::CreateAndReject(failure, __func__);
}
@ -244,7 +244,7 @@ OmxPromiseLayer::SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1, OMX_PTR aCmd
} else if (aCmd == OMX_CommandPortDisable) {
p = mPortDisablePromise.Ensure(__func__);
} else {
LOG("SendCommand: error unsupport command");
LOG("error unsupport command");
MOZ_ASSERT(0);
}
@ -262,7 +262,7 @@ OmxPromiseLayer::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2)
mCommandStatePromise.Resolve(OMX_CommandStateSet, __func__);
} else if (cmd == OMX_CommandFlush) {
MOZ_RELEASE_ASSERT(mFlushCommands.ElementAt(0).type == aData2);
LOG("Event: OMX_CommandFlush completed port type %d", aData2);
LOG("OMX_CommandFlush completed port type %d", aData2);
mFlushCommands.RemoveElementAt(0);
// Sending next flush command.
@ -355,7 +355,7 @@ OmxPromiseLayer::SetParameter(OMX_INDEXTYPE aParamIndex,
nsresult
OmxPromiseLayer::Shutdown()
{
LOG("Shutdown");
LOG("");
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
MOZ_ASSERT(!GetBufferHolders(OMX_DirInput)->Length());
MOZ_ASSERT(!GetBufferHolders(OMX_DirOutput)->Length());

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше