Merge mozilla-central to mozilla-inbound

--HG--
extra : rebase_source : 4b15946d1de3c0e3d7fed8fc0f68b5f653d8836a
This commit is contained in:
Carsten "Tomcat" Book 2016-06-16 08:39:13 +01:00
commit 3b1e93ac5e
26 changed files with 1283 additions and 625 deletions

View File

@ -146,9 +146,25 @@ BrowserAction.prototype = {
badgeNode.style.backgroundColor = color || "";
}
let iconURL = IconDetails.getURL(
tabData.icon, node.ownerDocument.defaultView, this.extension);
node.setAttribute("image", iconURL);
const LEGACY_CLASS = "toolbarbutton-legacy-addon";
node.classList.remove(LEGACY_CLASS);
let win = node.ownerDocument.defaultView;
let {icon, size} = IconDetails.getURL(tabData.icon, win, this.extension);
// If the best available icon size is not divisible by 16, check if we have
// an 18px icon to fall back to, and trim off the padding instead.
if (size % 16 && !icon.endsWith(".svg")) {
let result = IconDetails.getURL(tabData.icon, win, this.extension, 18);
if (result.size % 18 == 0) {
icon = result.icon;
node.classList.add(LEGACY_CLASS);
}
}
node.setAttribute("image", icon);
},
// Update the toolbar button for a given window.

View File

@ -91,7 +91,7 @@ PageAction.prototype = {
button.setAttribute("tooltiptext", title);
button.setAttribute("aria-label", title);
let icon = IconDetails.getURL(tabData.icon, window, this.extension);
let {icon} = IconDetails.getURL(tabData.icon, window, this.extension);
button.setAttribute("src", icon);
}

View File

@ -103,10 +103,29 @@ global.IconDetails = {
// Returns the appropriate icon URL for the given icons object and the
// screen resolution of the given window.
getURL(icons, window, extension, size = 18) {
getURL(icons, window, extension, size = 16) {
const DEFAULT = "chrome://browser/content/extension.svg";
return AddonManager.getPreferredIconURL({icons: icons}, size, window) || DEFAULT;
size *= window.devicePixelRatio;
let bestSize = null;
if (icons[size]) {
bestSize = size;
} else if (icons[2 * size]) {
bestSize = 2 * size;
} else {
let sizes = Object.keys(icons)
.map(key => parseInt(key, 10))
.sort((a, b) => a - b);
bestSize = sizes.find(candidate => candidate > size) || sizes.pop();
}
if (bestSize) {
return {size: bestSize, icon: icons[bestSize]};
}
return {size, icon: DEFAULT};
},
convertImageDataToPNG(imageData, context) {

View File

@ -104,27 +104,56 @@ add_task(function* testDetailsObjects() {
"2": browser.runtime.getURL("data/a.png")}},
// Various resolutions
{details: {"path": {"18": "a.png", "32": "a-x2.png"}},
{details: {"path": {"18": "a.png", "36": "a-x2.png"}},
legacy: true,
resolutions: {
"1": browser.runtime.getURL("data/a.png"),
"2": browser.runtime.getURL("data/a-x2.png")}},
{details: {"path": {"16": "a.png", "30": "a-x2.png"}},
resolutions: {
"1": browser.runtime.getURL("data/a.png"),
"2": browser.runtime.getURL("data/a-x2.png")}},
{details: {"path": {"16": "16.png", "100": "100.png"}},
resolutions: {
"1": browser.runtime.getURL("data/100.png"),
"1": browser.runtime.getURL("data/16.png"),
"2": browser.runtime.getURL("data/100.png")}},
{details: {"path": {"2": "2.png"}},
resolutions: {
"1": browser.runtime.getURL("data/2.png"),
"2": browser.runtime.getURL("data/2.png")}},
{details: {"path": {
"16": "16.svg",
"18": "18.svg"}},
resolutions: {
"1": browser.runtime.getURL("data/16.svg"),
"2": browser.runtime.getURL("data/18.svg")}},
{details: {"path": {
"6": "6.png",
"18": "18.png",
"36": "36.png",
"48": "48.png",
"128": "128.png"}},
legacy: true,
resolutions: {
"1": browser.runtime.getURL("data/18.png"),
"2": browser.runtime.getURL("data/36.png")}},
{details: {"path": {
"16": "16.png",
"18": "18.png",
"32": "32.png",
"48": "48.png",
"128": "128.png"}},
resolutions: {
"1": browser.runtime.getURL("data/18.png"),
"2": browser.runtime.getURL("data/48.png")}},
"1": browser.runtime.getURL("data/16.png"),
"2": browser.runtime.getURL("data/32.png")}},
{details: {"path": {
"18": "18.png",
"32": "32.png",
"48": "48.png",
"128": "128.png"}},
resolutions: {
"1": browser.runtime.getURL("data/32.png"),
"2": browser.runtime.getURL("data/32.png")}},
];
// Allow serializing ImageData objects for logging.
@ -146,7 +175,7 @@ add_task(function* testDetailsObjects() {
browser.browserAction.setIcon(Object.assign({tabId}, details.details));
browser.pageAction.setIcon(Object.assign({tabId}, details.details));
browser.test.sendMessage("imageURL", expectedURL);
browser.test.sendMessage("imageURL", [expectedURL, !!details.legacy]);
});
// Generate a list of tests and resolutions to send back to the test
@ -209,11 +238,14 @@ add_task(function* testDetailsObjects() {
extension.sendMessage("setIcon", test);
let imageURL = yield extension.awaitMessage("imageURL");
let [imageURL, legacy] = yield extension.awaitMessage("imageURL");
let browserActionButton = document.getElementById(browserActionId);
is(browserActionButton.getAttribute("image"), imageURL, "browser action has the correct image");
let isLegacy = browserActionButton.classList.contains("toolbarbutton-legacy-addon");
is(isLegacy, legacy, "Legacy class should be present?");
let pageActionImage = document.getElementById(pageActionId);
is(pageActionImage.src, imageURL, "page action has the correct image");
}

View File

@ -576,8 +576,8 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
max-width: 16px;
}
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-icon,
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@) > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon,
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon) > .toolbarbutton-icon,
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon) > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon,
#bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
max-width: 18px;
}
@ -606,9 +606,9 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
transition-duration: 150ms;
}
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon,
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-stack,
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon)) > .toolbarbutton-icon,
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon)) > .toolbarbutton-badge-stack,
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
padding: 3px 7px;
}

View File

@ -601,8 +601,8 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
margin: 1px;
}
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-icon,
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@) > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon {
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon) > .toolbarbutton-icon,
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon) > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon {
max-width: 18px;
margin: 0;
}

View File

@ -681,8 +681,8 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
max-width: 16px;
}
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-icon,
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@) > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon,
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon) > .toolbarbutton-icon,
:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon) > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon,
#bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
max-width: 18px;
}
@ -793,9 +793,9 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
border-inline-end-style: none;
}
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-stack,
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon)) > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon)) > .toolbarbutton-badge-stack,
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
padding: calc(var(--toolbarbutton-vertical-inner-padding) + 1px) 7px;
}

View File

@ -144,8 +144,9 @@ ToolSidebar.prototype = {
// Add menuitems to the alltabs menu if there are already tabs in the
// sidebar
for (let [id, tab] of this._tabs) {
if (!tab.hidden) {
this._addItemToAllTabsMenu(id, tab, tab.hasAttribute("selected"));
let item = this._addItemToAllTabsMenu(id, tab, tab.hasAttribute("selected"));
if (tab.hidden) {
item.hidden = true;
}
}
},

View File

@ -20,6 +20,7 @@ const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/u
const {createChild} = require("devtools/client/inspector/shared/utils");
const {gDevTools} = require("devtools/client/framework/devtools");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
loader.lazyRequireGetter(this, "overlays",
"devtools/client/inspector/shared/style-inspector-overlays");
@ -148,7 +149,8 @@ function CssComputedView(inspector, document, pageStyle) {
this.propertyViews = [];
this._outputParser = new OutputParser(document);
let cssProperties = getCssProperties(inspector.toolbox);
this._outputParser = new OutputParser(document, cssProperties.supportsType);
let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIXULChromeRegistry);

View File

@ -410,7 +410,7 @@ InspectorPanel.prototype = {
if (Services.prefs.getBoolPref("devtools.fontinspector.enabled") &&
this.canGetUsedFontFaces) {
this.fontInspector = new FontInspector(this, this.panelWin);
this.panelDoc.getElementById("sidebar-tab-fontinspector").hidden = false;
this.sidebar.toggleTab(true, "fontinspector");
}
this.layoutview = new LayoutView(this, this.panelWin);

View File

@ -14,7 +14,6 @@ const {TextProperty} =
require("devtools/client/inspector/rules/models/text-property");
const {promiseWarn} = require("devtools/client/inspector/shared/utils");
const {parseDeclarations} = require("devtools/shared/css-parsing-utils");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "osString", function () {
@ -55,8 +54,7 @@ function Rule(elementStyle, options) {
this.mediaText = this.domRule.mediaText;
}
const toolbox = this.elementStyle.ruleView.inspector.toolbox;
this.cssProperties = getCssProperties(toolbox);
this.cssProperties = this.elementStyle.ruleView.cssProperties;
// Populate the text properties with the style's current authoredText
// value, and add in any disabled properties from the store.

View File

@ -16,16 +16,13 @@ const {Tools} = require("devtools/client/definitions");
const {CssLogic} = require("devtools/shared/inspector/css-logic");
const {ELEMENT_STYLE} = require("devtools/server/actors/styles");
const {OutputParser} = require("devtools/client/shared/output-parser");
const {PrefObserver, PREF_ORIG_SOURCES} =
require("devtools/client/styleeditor/utils");
const {ElementStyle} =
require("devtools/client/inspector/rules/models/element-style");
const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
const {ElementStyle} = require("devtools/client/inspector/rules/models/element-style");
const {Rule} = require("devtools/client/inspector/rules/models/rule");
const {RuleEditor} =
require("devtools/client/inspector/rules/views/rule-editor");
const {createChild, promiseWarn} =
require("devtools/client/inspector/shared/utils");
const {RuleEditor} = require("devtools/client/inspector/rules/views/rule-editor");
const {createChild, promiseWarn} = require("devtools/client/inspector/shared/utils");
const {gDevTools} = require("devtools/client/framework/devtools");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
loader.lazyRequireGetter(this, "overlays",
"devtools/client/inspector/shared/style-inspector-overlays");
@ -161,7 +158,9 @@ function CssRuleView(inspector, document, store, pageStyle) {
this.store = store || {};
this.pageStyle = pageStyle;
this._outputParser = new OutputParser(document);
this.cssProperties = getCssProperties(inspector.toolbox);
this._outputParser = new OutputParser(document, this.cssProperties.supportsType);
this._onAddRule = this._onAddRule.bind(this);
this._onContextMenu = this._onContextMenu.bind(this);

View File

@ -9,7 +9,8 @@
// This is more of a unit test than a mochitest-browser test, but can't be
// tested with an xpcshell test as the output-parser requires the DOM to work.
var {OutputParser} = require("devtools/client/shared/output-parser");
const {OutputParser} = require("devtools/client/shared/output-parser");
const {initCssProperties, getCssProperties} = require("devtools/shared/fronts/css-properties");
const COLOR_CLASS = "color-class";
const URL_CLASS = "url-class";
@ -296,7 +297,12 @@ const TEST_DATA = [
];
add_task(function* () {
let parser = new OutputParser(document);
// Mock the toolbox that initCssProperties expect so we get the fallback css properties.
let toolbox = {target: {client: {}, hasActor: () => false}};
yield initCssProperties(toolbox);
let cssProperties = getCssProperties(toolbox);
let parser = new OutputParser(document, cssProperties.supportsType);
for (let i = 0; i < TEST_DATA.length; i++) {
let data = TEST_DATA[i];
info("Output-parser test data " + i + ". {" + data.name + " : " +

View File

@ -9,38 +9,15 @@ const {angleUtils} = require("devtools/client/shared/css-angle");
const {colorUtils} = require("devtools/client/shared/css-color");
const {getCSSLexer} = require("devtools/shared/css-lexer");
const EventEmitter = require("devtools/shared/event-emitter");
const {
ANGLE_TAKING_FUNCTIONS,
BEZIER_KEYWORDS,
COLOR_TAKING_FUNCTIONS,
CSS_TYPES
} = require("devtools/shared/css-properties-db");
const HTML_NS = "http://www.w3.org/1999/xhtml";
const BEZIER_KEYWORDS = ["linear", "ease-in-out", "ease-in", "ease-out",
"ease"];
// Functions that accept a color argument.
const COLOR_TAKING_FUNCTIONS = ["linear-gradient",
"-moz-linear-gradient",
"repeating-linear-gradient",
"-moz-repeating-linear-gradient",
"radial-gradient",
"-moz-radial-gradient",
"repeating-radial-gradient",
"-moz-repeating-radial-gradient",
"drop-shadow"];
// Functions that accept an angle argument.
const ANGLE_TAKING_FUNCTIONS = ["linear-gradient",
"-moz-linear-gradient",
"repeating-linear-gradient",
"-moz-repeating-linear-gradient",
"rotate",
"rotateX",
"rotateY",
"rotateZ",
"rotate3d",
"skew",
"skewX",
"skewY",
"hue-rotate"];
loader.lazyGetter(this, "DOMUtils", function () {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
});
@ -57,13 +34,20 @@ loader.lazyGetter(this, "DOMUtils", function () {
* Cu.import("resource://devtools/shared/Loader.jsm", {});
* const {OutputParser} = require("devtools/client/shared/output-parser");
*
* let parser = new OutputParser(document);
* let parser = new OutputParser(document, supportsType);
*
* parser.parseCssProperty("color", "red"); // Returns document fragment.
*
* @param {Document} document Used to create DOM nodes.
* @param {Function} supportsTypes A function that returns a boolean when asked if a css
* property name supports a given css type.
* The function is executed like supportsType("color", CSS_TYPES.COLOR) where CSS_TYPES is
* defined in devtools/shared/css-properties-db.js
*/
function OutputParser(document) {
function OutputParser(document, supportsType) {
this.parsed = [];
this.doc = document;
this.supportsType = supportsType;
this.colorSwatches = new WeakMap();
this.angleSwatches = new WeakMap();
this._onColorSwatchMouseDown = this._onColorSwatchMouseDown.bind(this);
@ -89,12 +73,10 @@ OutputParser.prototype = {
parseCssProperty: function (name, value, options = {}) {
options = this._mergeOptions(options);
options.expectCubicBezier =
safeCssPropertySupportsType(name, DOMUtils.TYPE_TIMING_FUNCTION);
options.expectCubicBezier = this.supportsType(name, CSS_TYPES.TIMING_FUNCTION);
options.expectFilter = name === "filter";
options.supportsColor =
safeCssPropertySupportsType(name, DOMUtils.TYPE_COLOR) ||
safeCssPropertySupportsType(name, DOMUtils.TYPE_GRADIENT);
options.supportsColor = this.supportsType(name, CSS_TYPES.COLOR) ||
this.supportsType(name, CSS_TYPES.GRADIENT);
// The filter property is special in that we want to show the
// swatch even if the value is invalid, because this way the user
@ -681,20 +663,3 @@ OutputParser.prototype = {
return defaults;
}
};
/**
* A wrapper for DOMUtils.cssPropertySupportsType that ignores invalid
* properties.
*
* @param {String} name The property name.
* @param {number} type The type tested for support.
* @return {Boolean} Whether the property supports the type.
* If the property is unknown, false is returned.
*/
function safeCssPropertySupportsType(name, type) {
try {
return DOMUtils.cssPropertySupportsType(name, type);
} catch (e) {
return false;
}
}

View File

@ -3,7 +3,8 @@
"use strict";
var {OutputParser} = require("devtools/client/shared/output-parser");
const {OutputParser} = require("devtools/client/shared/output-parser");
const {initCssProperties, getCssProperties} = require("devtools/shared/fronts/css-properties");
add_task(function* () {
yield addTab("about:blank");
@ -15,7 +16,12 @@ function* performTest() {
let [host, , doc] = yield createHost("bottom", "data:text/html," +
"<h1>browser_outputParser.js</h1><div></div>");
let parser = new OutputParser(doc);
// Mock the toolbox that initCssProperties expect so we get the fallback css properties.
let toolbox = {target: {client: {}, hasActor: () => false}};
yield initCssProperties(toolbox);
let cssProperties = getCssProperties(toolbox);
let parser = new OutputParser(doc, cssProperties.supportsType);
testParseCssProperty(doc, parser);
testParseCssVar(doc, parser);
testParseURL(doc, parser);

View File

@ -4,7 +4,7 @@
"use strict";
const { Cc, Ci, Cu } = require("chrome");
const { Cc, Ci } = require("chrome");
loader.lazyGetter(this, "DOMUtils", () => {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
@ -13,32 +13,40 @@ loader.lazyGetter(this, "DOMUtils", () => {
const protocol = require("devtools/shared/protocol");
const { ActorClassWithSpec, Actor } = protocol;
const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
const clientCssDatabase = require("devtools/shared/css-properties-db")
const { CSS_PROPERTIES, CSS_TYPES } = require("devtools/shared/css-properties-db");
var CssPropertiesActor = exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
typeName: "cssProperties",
initialize: function(conn, parent) {
initialize(conn, parent) {
Actor.prototype.initialize.call(this, conn);
this.parent = parent;
},
destroy: function() {
destroy() {
Actor.prototype.destroy.call(this);
},
getCSSDatabase: function() {
getCSSDatabase() {
const db = {};
const properties = DOMUtils.getCSSPropertyNames(DOMUtils.INCLUDE_ALIASES);
properties.forEach(name => {
// In order to maintain any backwards compatible changes when debugging
// older clients, take the definition from the static database in the
// devtools client, and fill it in with the most recent property
// definition from the server.
const clientDefinition = clientCssDatabase[name] || {};
// Get the list of CSS types this property supports.
let supports = [];
for (let type in CSS_TYPES) {
if (safeCssPropertySupportsType(name, DOMUtils["TYPE_" + type])) {
supports.push(CSS_TYPES[type]);
}
}
// In order to maintain any backwards compatible changes when debugging older
// clients, take the definition from the static CSS properties database, and fill it
// in with the most recent property definition from the server.
const clientDefinition = CSS_PROPERTIES[name] || {};
const serverDefinition = {
isInherited: DOMUtils.isInheritedProperty(name)
isInherited: DOMUtils.isInheritedProperty(name),
supports
};
db[name] = Object.assign(clientDefinition, serverDefinition);
});
@ -65,4 +73,21 @@ function isCssPropertyKnown(name) {
}
}
exports.isCssPropertyKnown = isCssPropertyKnown
exports.isCssPropertyKnown = isCssPropertyKnown;
/**
* A wrapper for DOMUtils.cssPropertySupportsType that ignores invalid
* properties.
*
* @param {String} name The property name.
* @param {number} type The type tested for support.
* @return {Boolean} Whether the property supports the type.
* If the property is unknown, false is returned.
*/
function safeCssPropertySupportsType(name, type) {
try {
return DOMUtils.cssPropertySupportsType(name, type);
} catch (e) {
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
const { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
const { Task } = require("devtools/shared/task");
const { CSS_PROPERTIES } = require("devtools/shared/css-properties-db");
/**
* Build up a regular expression that matches a CSS variable token. This is an
@ -49,6 +50,8 @@ exports.CssPropertiesFront = CssPropertiesFront;
/**
* Ask questions to a CSS database. This class does not care how the database
* gets loaded in, only the questions that you can ask to it.
* Prototype functions are bound to 'this' so they can be passed around as helper
* functions.
*
* @param {Array} propertiesList
* A list of known properties.
@ -58,10 +61,10 @@ exports.CssPropertiesFront = CssPropertiesFront;
*/
function CssProperties(properties) {
this.properties = properties;
// Bind isKnown and isInherited so it can be passed around to helper
// functions.
this.isKnown = this.isKnown.bind(this);
this.isInherited = this.isInherited.bind(this);
this.supportsType = this.supportsType.bind(this);
}
CssProperties.prototype = {
@ -69,16 +72,33 @@ CssProperties.prototype = {
* Checks to see if the property is known by the browser. This function has
* `this` already bound so that it can be passed around by reference.
*
* @param {String} property
* The property name to be checked.
* @param {String} property The property name to be checked.
* @return {Boolean}
*/
isKnown(property) {
return !!this.properties[property] || isCssVariable(property);
},
/**
* Checks to see if the property is an inherited one.
*
* @param {String} property The property name to be checked.
* @return {Boolean}
*/
isInherited(property) {
return this.properties[property] && this.properties[property].isInherited;
},
/**
* Checks if the property supports the given CSS type.
* CSS types should come from devtools/shared/css-properties-db.js' CSS_TYPES.
*
* @param {String} property The property to be checked.
* @param {Number} type One of the type values from CSS_TYPES.
* @return {Boolean}
*/
supportsType(property, type) {
return this.properties[property] && this.properties[property].supports.includes(type);
}
};
@ -103,15 +123,28 @@ exports.initCssProperties = Task.async(function* (toolbox) {
let db, front;
// Get the list dynamically if the cssProperties exists.
// Get the list dynamically if the cssProperties actor exists.
if (toolbox.target.hasActor("cssProperties")) {
front = CssPropertiesFront(client, toolbox.target.form);
db = yield front.getCSSDatabase();
// Even if the target has the cssProperties actor, it may not be the latest version.
// So, the "supports" data may be missing.
// Start with the server's list (because that's the correct one), and add the supports
// information if required.
if (!db.color.supports) {
for (let name in db) {
if (typeof CSS_PROPERTIES[name] === "object") {
db[name].supports = CSS_PROPERTIES[name].supports;
}
}
}
} else {
// The target does not support this actor, so require a static list of
// supported properties.
db = require("devtools/shared/css-properties-db");
// The target does not support this actor, so require a static list of supported
// properties.
db = CSS_PROPERTIES;
}
const cssProperties = new CssProperties(db);
cachedCssProperties.set(client, {cssProperties, front});
return {cssProperties, front};

View File

@ -16,11 +16,10 @@
<ImageView
android:id="@+id/device_type"
android:layout_width="@dimen/favicon_bg"
android:layout_height="@dimen/favicon_bg"
android:layout_margin="16dp"
android:padding="2dp"
android:scaleType="center"
android:layout_width="26dp"
android:layout_height="18dp"
android:layout_margin="20dp"
android:scaleType="fitCenter"
android:layout_gravity="center_vertical"
tools:src="@drawable/cloud"/>

View File

@ -79,6 +79,13 @@
}
},
"incognito": {
"type": "string",
"enum": ["spanning"],
"optional": true,
"onError": "warn"
},
"background": {
"choices": [
{

View File

@ -0,0 +1,27 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* test_manifest_incognito() {
let normalized = yield normalizeManifest({
"incognito": "spanning",
});
equal(normalized.error, undefined, "Should not have an error");
equal(normalized.errors.length, 0, "Should not have warnings");
equal(normalized.value.incognito,
"spanning",
"Should have the expected incognito string");
normalized = yield normalizeManifest({
"incognito": "split",
});
equal(normalized.error, undefined, "Should not have an error");
Assert.deepEqual(normalized.errors,
['Error processing incognito: Invalid enumeration value "split"'],
"Should have the expected warning");
equal(normalized.value.incognito, null,
"Invalid incognito string should be omitted");
});

View File

@ -11,6 +11,7 @@ skip-if = toolkit == 'gonk' || appname == "thunderbird"
[test_ext_contexts.js]
[test_ext_json_parser.js]
[test_ext_manifest_content_security_policy.js]
[test_ext_manifest_incognito.js]
[test_ext_schemas.js]
[test_getAPILevelForWindow.js]
[test_native_messaging.js]

View File

@ -28,15 +28,33 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=360220
<script class="testbody" type="application/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
/** Test for Bug 360220 **/
var menulist = document.getElementById("menulist");
var secondItem = document.getElementById("secondItem");
menulist.selectedItem = secondItem;
ok(menulist.label == "bar", "second item was not selected");
is(menulist.label, "bar", "second item was not selected");
let mutObserver = new MutationObserver(() => {
is(menulist.label, "new label", "menulist label was not updated to the label of its selected item");
done();
});
mutObserver.observe(menulist, { attributeFilter: ['label'] });
secondItem.label = "new label";
ok(menulist.label == "new label", "menulist label was not updated to the label of its selected item");
let failureTimeout = setTimeout(function() {
ok(false, "menulist label should have updated");
done();
}, 2000);
function done() {
mutObserver.disconnect();
clearTimeout(failureTimeout);
SimpleTest.finish();
}
]]>
</script>

View File

@ -84,13 +84,6 @@ SimpleTest.waitForExplicitFinish();
function testtag_menulists()
{
testtag_menulist_UI_start($("menulist"), false);
testtag_menulist_UI_start($("menulist-editable"), true);
// bug 566154, the menulist width should account for vertical scrollbar
ok(document.getElementById("menulist-size").getBoundingClientRect().width >= 210,
"menulist popup width includes scrollbar width");
$("menulist").open = true;
}
function testtag_menulist_UI_start(element, editable)
@ -104,11 +97,25 @@ function testtag_menulist_UI_start(element, editable)
// test the interfaces that menulist implements
test_nsIDOMXULMenuListElement(element, testprefix, editable);
}
function testtag_menulist_UI_finish(element, editable)
{
element.value = "";
test_nsIDOMXULSelectControlElement(element, "menuitem",
editable ? "editable" : null);
if (!editable) {
testtag_menulist_UI_start($("menulist-editable"), true);
}
else {
// bug 566154, the menulist width should account for vertical scrollbar
ok(document.getElementById("menulist-size").getBoundingClientRect().width >= 210,
"menulist popup width includes scrollbar width");
$("menulist").open = true;
}
}
function test_nsIDOMXULMenuListElement(element, testprefix, editable)
@ -152,6 +159,7 @@ function test_nsIDOMXULMenuListElement(element, testprefix, editable)
element.selectedIndex = 1;
is(element.image, "", testprefix + " image set to selected");
is(element.description, "", testprefix + " description set to selected");
test_nsIDOMXULMenuListElement_finish(element, testprefix, editable);
}
else {
element.selectedIndex = 1;
@ -165,27 +173,66 @@ function test_nsIDOMXULMenuListElement(element, testprefix, editable)
is(element.description, "This is the third description", testprefix + " description set to selected again");
// check that changing the properties of the selected item changes the menulist's properties
thirditem.label = "Item Number Three";
is(element.label, "Item Number Three", testprefix + " label modified");
thirditem.value = "item-three";
is(element.value, "item-three", testprefix + " value modified");
thirditem.image = "smile.png";
is(element.image, "smile.png", testprefix + " image modified");
thirditem.setAttribute("description", "Changed description");
is(element.description, "Changed description", testprefix + " description modified");
seconditem.label = "Changed Label 2";
is(element.label, "Item Number Three", testprefix + " label of another item modified");
let properties = [{attr: "label", value: "Item Number Three"},
{attr: "value", value: "item-three"},
{attr: "image", value: "smile.png"},
{attr: "description", value: "Changed description"}];
test_nsIDOMXULMenuListElement_properties(element, testprefix, editable, thirditem, properties);
}
}
element.selectedIndex = 0;
is(element.image, "", testprefix + " image set to selected with no image");
is(element.description, "", testprefix + " description set to selected with no description");
function test_nsIDOMXULMenuListElement_properties(element, testprefix, editable, thirditem, properties)
{
let {attr, value} = properties.shift();
let last = (properties.length == 0);
let mutObserver = new MutationObserver(() => {
is(element.getAttribute(attr), value, `${testprefix} ${attr} modified`);
done();
});
mutObserver.observe(element, { attributeFilter: [attr] });
let failureTimeout = setTimeout(() => {
ok(false, `${testprefix} ${attr} should have updated`);
done();
}, 2000);
function done()
{
clearTimeout(failureTimeout);
mutObserver.disconnect();
if (!last) {
test_nsIDOMXULMenuListElement_properties(element, testprefix, editable, thirditem, properties);
}
else {
test_nsIDOMXULMenuListElement_unselected(element, testprefix, editable, thirditem);
}
}
thirditem.setAttribute(attr, value)
}
function test_nsIDOMXULMenuListElement_unselected(element, testprefix, editable, thirditem)
{
let seconditem = thirditem.previousElementSibling;
seconditem.label = "Changed Label 2";
is(element.label, "Item Number Three", testprefix + " label of another item modified");
element.selectedIndex = 0;
is(element.image, "", testprefix + " image set to selected with no image");
is(element.description, "", testprefix + " description set to selected with no description");
test_nsIDOMXULMenuListElement_finish(element, testprefix, editable);
}
function test_nsIDOMXULMenuListElement_finish(element, testprefix, editable)
{
// check the removeAllItems method
element.appendItem("An Item", "anitem");
element.appendItem("Another Item", "anotheritem");
element.removeAllItems();
is(element.itemCount, 0, testprefix + " removeAllItems");
testtag_menulist_UI_finish(element, editable);
}
function test_menulist_open(element, scroller)

View File

@ -1025,7 +1025,6 @@ extends="chrome://global/content/bindings/popup.xml#popup">
<implementation implements="nsIAutoCompletePopup">
<field name="_currentIndex">0</field>
<field name="_rowHeight">0</field>
<field name="_rlbAnimated">false</field>
<!-- =================== nsIAutoCompletePopup =================== -->
@ -1165,10 +1164,8 @@ extends="chrome://global/content/bindings/popup.xml#popup">
// Default the height to 0 if we have no rows to show
let height = 0;
if (numRows) {
if (!this._rowHeight) {
let firstRowRect = rows[0].getBoundingClientRect();
this._rowHeight = firstRowRect.height;
let firstRowRect = rows[0].getBoundingClientRect();
if (this._rlbPadding == undefined) {
let style = window.getComputedStyle(this.richlistbox);
let transition = style.transitionProperty;
@ -1177,14 +1174,20 @@ extends="chrome://global/content/bindings/popup.xml#popup">
let paddingTop = parseInt(style.paddingTop) || 0;
let paddingBottom = parseInt(style.paddingBottom) || 0;
this._rlbPadding = paddingTop + paddingBottom;
// Set a fixed max-height to avoid flicker when growing the panel.
this.richlistbox.style.maxHeight =
((this._rowHeight * this.maxRows) + this._rlbPadding) + "px";
}
if (numRows > this.maxRows) {
// Set a fixed max-height to avoid flicker when growing the panel.
let lastVisibleRowRect = rows[this.maxRows - 1].getBoundingClientRect();
let visibleHeight = lastVisibleRowRect.bottom - firstRowRect.top;
this.richlistbox.style.maxHeight =
visibleHeight + this._rlbPadding + "px";
}
let lastRowRect = rows[numRows - 1].getBoundingClientRect();
// Calculate the height to have the first row to last row shown
height = (this._rowHeight * numRows) + this._rlbPadding;
height = lastRowRect.bottom - firstRowRect.top +
this._rlbPadding;
}
let animate = this._rlbAnimated &&

View File

@ -66,10 +66,11 @@
</handler>
</handlers>
<implementation implements="nsIDOMXULMenuListElement, nsIDOMEventListener">
<implementation implements="nsIDOMXULMenuListElement">
<constructor>
this.mInputField = null;
this.mSelectedInternal = null;
this.mAttributeObserver = null;
this.menuBoxObject = this.boxObject;
this.setInitialSelection();
</constructor>
@ -211,39 +212,29 @@
if (oldval) {
oldval.removeAttribute('selected');
if (document instanceof Components.interfaces.nsIDOMXULDocument) {
document.removeBroadcastListenerFor(oldval, this, "value");
document.removeBroadcastListenerFor(oldval, this, "label");
document.removeBroadcastListenerFor(oldval, this, "image");
document.removeBroadcastListenerFor(oldval, this, "description");
}
else
oldval.removeEventListener("DOMAttrModified", this, false);
this.mAttributeObserver.disconnect();
}
this.mSelectedInternal = val;
let attributeFilter = ["value", "label", "image", "description"];
if (val) {
val.setAttribute('selected', 'true');
this.setAttribute('value', val.getAttribute('value'));
this.setAttribute('image', val.getAttribute('image'));
this.setAttribute('label', val.getAttribute('label'));
this.setAttribute('description', val.getAttribute('description'));
// DOMAttrModified listeners slow down setAttribute calls within
// the document, see bug 395496
if (document instanceof Components.interfaces.nsIDOMXULDocument) {
document.addBroadcastListenerFor(val, this, "value");
document.addBroadcastListenerFor(val, this, "label");
document.addBroadcastListenerFor(val, this, "image");
document.addBroadcastListenerFor(val, this, "description");
for (let attr of attributeFilter) {
if (val.hasAttribute(attr)) {
this.setAttribute(attr, val.getAttribute(attr));
}
else {
this.removeAttribute(attr);
}
}
else
val.addEventListener("DOMAttrModified", this, false);
this.mAttributeObserver = new MutationObserver(this.handleMutation.bind(this));
this.mAttributeObserver.observe(val, { attributeFilter });
}
else {
this.removeAttribute('value');
this.removeAttribute('image');
this.removeAttribute('label');
this.removeAttribute('description');
for (let attr of attributeFilter) {
this.removeAttribute(attr);
}
}
var event = document.createEvent("Events");
@ -259,19 +250,26 @@
</setter>
</property>
<method name="handleEvent">
<parameter name="aEvent"/>
<method name="handleMutation">
<parameter name="aRecords"/>
<body>
<![CDATA[
if (aEvent.type == "DOMAttrModified" &&
aEvent.target == this.mSelectedInternal) {
var attrName = aEvent.attrName;
switch (attrName) {
case "value":
case "label":
case "image":
case "description":
this.setAttribute(attrName, aEvent.newValue);
for (let record of aRecords) {
let t = record.target;
if (t == this.mSelectedInternal) {
let attrName = record.attributeName;
switch (attrName) {
case "value":
case "label":
case "image":
case "description":
if (t.hasAttribute(attrName)) {
this.setAttribute(attrName, t.getAttribute(attrName));
}
else {
this.removeAttribute(attrName);
}
}
}
}
]]>
@ -384,15 +382,8 @@
<destructor>
<![CDATA[
if (this.mSelectedInternal) {
if (document instanceof Components.interfaces.nsIDOMXULDocument) {
document.removeBroadcastListenerFor(this.mSelectedInternal, this, "value");
document.removeBroadcastListenerFor(this.mSelectedInternal, this, "label");
document.removeBroadcastListenerFor(this.mSelectedInternal, this, "image");
document.removeBroadcastListenerFor(this.mSelectedInternal, this, "description");
}
else
this.mSelectedInternal.removeEventListener("DOMAttrModified", this, false);
if (this.mAttributeObserver) {
this.mAttributeObserver.disconnect();
}
]]>
</destructor>