зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound. a=merge
This commit is contained in:
Коммит
5103656a34
|
@ -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
|
||||
|
|
2
CLOBBER
2
CLOBBER
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']);
|
||||
|
||||
|
|
Двоичный файл не отображается.
|
@ -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());
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче