Merge autoland to central, a=merge

MozReview-Commit-ID: Cu9iXbDAS7t
This commit is contained in:
Wes Kocher 2017-02-09 16:36:11 -08:00
Родитель 28a131c59f 64e3711920
Коммит e27ab18a24
135 изменённых файлов: 2151 добавлений и 1208 удалений

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

@ -11,8 +11,15 @@ jobs:
target-tasks-method: nightly_linux
run-on-projects:
- mozilla-central
- mozilla-aurora
- date
when: [] # never (temporary)
when:
by-project:
# Match buildbot starts for now
date: [{hour: 16, minute: 0}]
mozilla-central: [{hour: 11, minute: 0}]
mozilla-aurora: [{hour: 8, minute: 45}] # Buildbot uses minute 40
# No default
- name: nightly-android
job:
@ -22,8 +29,15 @@ jobs:
target-tasks-method: nightly_fennec
run-on-projects:
- mozilla-central
- mozilla-aurora
- date
when: [] # never (temporary)
when:
by-project:
# Match buildbot starts for now
date: [{hour: 16, minute: 0}]
mozilla-central: [{hour: 11, minute: 0}]
mozilla-aurora: [{hour: 8, minute: 45}] # Buildbot uses minute 40
# No default
- name: nightly-mochitest-valgrind
job:

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

@ -12,6 +12,7 @@ module.exports = { // eslint-disable-line no-undef
"WindowEventManager": true,
"browserActionFor": true,
"getCookieStoreIdForTab": true,
"getDevToolsTargetForContext": true,
"makeWidgetId": true,
"pageActionFor": true,
"tabTracker": true,

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

@ -0,0 +1,35 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
const {
SingletonEventManager,
} = ExtensionUtils;
extensions.registerSchemaAPI("devtools.network", "devtools_parent", (context) => {
return {
devtools: {
network: {
onNavigated: new SingletonEventManager(context, "devtools.onNavigated", fire => {
let listener = (event, data) => {
fire.async(data.url);
};
let targetPromise = getDevToolsTargetForContext(context);
targetPromise.then(target => {
target.on("will-navigate", listener);
});
return () => {
targetPromise.then(target => {
target.off("will-navigate", listener);
});
};
}).api(),
},
},
};
});

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

@ -7,6 +7,7 @@ category webextension-scripts contextMenus chrome://browser/content/ext-contextM
category webextension-scripts desktop-runtime chrome://browser/content/ext-desktop-runtime.js
category webextension-scripts devtools chrome://browser/content/ext-devtools.js
category webextension-scripts devtools-inspectedWindow chrome://browser/content/ext-devtools-inspectedWindow.js
category webextension-scripts devtools-network chrome://browser/content/ext-devtools-network.js
category webextension-scripts history chrome://browser/content/ext-history.js
category webextension-scripts omnibox chrome://browser/content/ext-omnibox.js
category webextension-scripts pageAction chrome://browser/content/ext-pageAction.js
@ -34,6 +35,7 @@ category webextension-schemas context_menus chrome://browser/content/schemas/con
category webextension-schemas context_menus_internal chrome://browser/content/schemas/context_menus_internal.json
category webextension-schemas devtools chrome://browser/content/schemas/devtools.json
category webextension-schemas devtools_inspected_window chrome://browser/content/schemas/devtools_inspected_window.json
category webextension-schemas devtools_network chrome://browser/content/schemas/devtools_network.json
category webextension-schemas history chrome://browser/content/schemas/history.json
category webextension-schemas omnibox chrome://browser/content/schemas/omnibox.json
category webextension-schemas page_action chrome://browser/content/schemas/page_action.json

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

@ -20,6 +20,7 @@ browser.jar:
content/browser/ext-desktop-runtime.js
content/browser/ext-devtools.js
content/browser/ext-devtools-inspectedWindow.js
content/browser/ext-devtools-network.js
content/browser/ext-history.js
content/browser/ext-omnibox.js
content/browser/ext-pageAction.js

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

@ -0,0 +1,93 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
[
{
"namespace": "devtools.network",
"allowedContexts": ["devtools", "devtools_only"],
"defaultContexts": ["devtools", "devtools_only"],
"description": "Use the <code>chrome.devtools.network</code> API to retrieve the information about network requests displayed by the Developer Tools in the Network panel.",
"types": [
{
"id": "Request",
"type": "object",
"description": "Represents a network request for a document resource (script, image and so on). See HAR Specification for reference.",
"functions": [
{
"name": "getContent",
"type": "function",
"description": "Returns content of the response body.",
"async": "callback",
"parameters": [
{
"name": "callback",
"type": "function",
"description": "A function that receives the response body when the request completes.",
"parameters": [
{
"name": "content",
"type": "string",
"description": "Content of the response body (potentially encoded)."
},
{
"name": "encoding",
"type": "string",
"description": "Empty if content is not encoded, encoding name otherwise. Currently, only base64 is supported."
}
]
}
]
}
]
}
],
"functions": [
{
"name": "getHAR",
"unsupported": true,
"type": "function",
"description": "Returns HAR log that contains all known network requests.",
"async": "callback",
"parameters": [
{
"name": "callback",
"type": "function",
"description": "A function that receives the HAR log when the request completes.",
"parameters": [
{
"name": "harLog",
"type": "object",
"additionalProperties": {"type": "any"},
"description": "A HAR log. See HAR specification for details."
}
]
}
]
}
],
"events": [
{
"name": "onRequestFinished",
"unsupported": true,
"type": "function",
"description": "Fired when a network request is finished and all request data are available.",
"parameters": [
{ "name": "request", "$ref": "Request", "description": "Description of a network request in the form of a HAR entry. See HAR specification for details." }
]
},
{
"name": "onNavigated",
"type": "function",
"description": "Fired when the inspected window navigates to a new page.",
"parameters": [
{
"name": "url",
"type": "string",
"description": "URL of the new page."
}
]
}
]
}
]

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

@ -11,6 +11,7 @@ browser.jar:
content/browser/schemas/context_menus_internal.json
content/browser/schemas/devtools.json
content/browser/schemas/devtools_inspected_window.json
content/browser/schemas/devtools_network.json
content/browser/schemas/history.json
content/browser/schemas/omnibox.json
content/browser/schemas/page_action.json

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

@ -52,6 +52,7 @@ support-files =
[browser_ext_currentWindow.js]
[browser_ext_devtools_inspectedWindow.js]
[browser_ext_devtools_inspectedWindow_reload.js]
[browser_ext_devtools_network.js]
[browser_ext_devtools_page.js]
[browser_ext_getViews.js]
[browser_ext_incognito_views.js]

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

@ -0,0 +1,94 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
"resource://devtools/shared/Loader.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
"resource://devtools/client/framework/gDevTools.jsm");
add_task(async function test_devtools_network_on_navigated() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
function background() {
browser.test.onMessage.addListener(msg => {
let code;
if (msg === "navigate") {
code = "window.wrappedJSObject.location.href = 'http://example.com/';";
browser.tabs.executeScript({code});
} else if (msg === "reload") {
code = "window.wrappedJSObject.location.reload(true);";
browser.tabs.executeScript({code});
}
});
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === "complete" && tab.url === "http://example.com/") {
browser.test.sendMessage("tabUpdated");
}
});
browser.test.sendMessage("ready");
}
function devtools_page() {
let eventCount = 0;
let listener = url => {
eventCount++;
browser.test.assertEq("http://example.com/", url, "onNavigated received the expected url.");
if (eventCount === 2) {
browser.devtools.network.onNavigated.removeListener(listener);
}
browser.test.sendMessage("onNavigatedFired", eventCount);
};
browser.devtools.network.onNavigated.addListener(listener);
}
let extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
permissions: ["tabs", "http://mochi.test/", "http://example.com/"],
devtools_page: "devtools_page.html",
},
files: {
"devtools_page.html": `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="devtools_page.js"></script>
</head>
<body>
</body>
</html>`,
"devtools_page.js": devtools_page,
},
});
await extension.startup();
await extension.awaitMessage("ready");
let target = devtools.TargetFactory.forTab(tab);
await gDevTools.showToolbox(target, "webconsole");
info("Developer toolbox opened.");
extension.sendMessage("navigate");
await extension.awaitMessage("tabUpdated");
let eventCount = await extension.awaitMessage("onNavigatedFired");
is(eventCount, 1, "The expected number of events were fired.");
extension.sendMessage("reload");
await extension.awaitMessage("tabUpdated");
eventCount = await extension.awaitMessage("onNavigatedFired");
is(eventCount, 2, "The expected number of events were fired.");
// do a reload after the listener has been removed, do not expect a message to be sent
extension.sendMessage("reload");
await extension.awaitMessage("tabUpdated");
await gDevTools.closeToolbox(target);
await target.destroy();
await extension.unload();
await BrowserTestUtils.removeTab(tab);
});

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

@ -514,7 +514,7 @@ PlacesTreeView.prototype = {
const locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIXULChromeRegistry)
.getSelectedLocale("global", true);
const dtOptions = { year: "2-digit", month: "numeric", day: "numeric",
const dtOptions = { year: "numeric", month: "numeric", day: "numeric",
hour: "numeric", minute: "numeric" };
this.__dateFormatter = new Intl.DateTimeFormat(locale, dtOptions);
}

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

@ -127,7 +127,7 @@
case "date":
let timeObj = new Date(node.time / 1000);
// Default is short date format.
let dtOptions = { year: '2-digit', month: 'numeric', day: 'numeric',
let dtOptions = { year: 'numeric', month: 'numeric', day: 'numeric',
hour: 'numeric', minute: 'numeric' };
// For today's visits we don't show date portion.
if (node.uri == "http://at.midnight.com/" ||

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

@ -2,15 +2,19 @@
* 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/. */
/* globals NetMonitorController */
"use strict";
const {
ADD_REQUEST,
UPDATE_REQUEST,
CLEAR_REQUESTS,
CLONE_SELECTED_REQUEST,
REMOVE_SELECTED_CUSTOM_REQUEST,
CLEAR_REQUESTS,
SEND_CUSTOM_REQUEST,
UPDATE_REQUEST,
} = require("../constants");
const { getSelectedRequest } = require("../selectors/index");
function addRequest(id, data, batch) {
return {
@ -40,6 +44,43 @@ function cloneSelectedRequest() {
};
}
/**
* Send a new HTTP request using the data in the custom request form.
*/
function sendCustomRequest() {
if (!NetMonitorController.supportsCustomRequest) {
return cloneSelectedRequest();
}
return (dispatch, getState) => {
const selected = getSelectedRequest(getState());
if (!selected) {
return;
}
// Send a new HTTP request using the data in the custom request form
let data = {
url: selected.url,
method: selected.method,
httpVersion: selected.httpVersion,
};
if (selected.requestHeaders) {
data.headers = selected.requestHeaders.headers;
}
if (selected.requestPostData) {
data.body = selected.requestPostData.postData.text;
}
NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
return dispatch({
type: SEND_CUSTOM_REQUEST,
id: response.eventActor.actor,
});
});
};
}
/**
* Remove a request from the list. Supports removing only cloned requests with a
* "isCustom" attribute. Other requests never need to be removed.
@ -58,8 +99,9 @@ function clearRequests() {
module.exports = {
addRequest,
updateRequest,
clearRequests,
cloneSelectedRequest,
removeSelectedCustomRequest,
clearRequests,
sendCustomRequest,
updateRequest,
};

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

@ -5,19 +5,7 @@
"use strict";
const { getDisplayedRequests } = require("../selectors/index");
const { SELECT_REQUEST, PRESELECT_REQUEST } = require("../constants");
/**
* When a new request with a given id is added in future, select it immediately.
* Used by the "Edit and Resend" feature, where we know in advance the ID of the
* request, at a time when it wasn't sent yet.
*/
function preselectRequest(id) {
return {
type: PRESELECT_REQUEST,
id
};
}
const { SELECT_REQUEST } = require("../constants");
/**
* Select request with a given id.
@ -61,7 +49,6 @@ function selectDelta(delta) {
}
module.exports = {
preselectRequest,
selectRequest,
selectDelta,
};

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

@ -21,10 +21,10 @@ const actionTypes = {
ENABLE_REQUEST_FILTER_TYPE_ONLY: "ENABLE_REQUEST_FILTER_TYPE_ONLY",
OPEN_SIDEBAR: "OPEN_SIDEBAR",
OPEN_STATISTICS: "OPEN_STATISTICS",
PRESELECT_REQUEST: "PRESELECT_REQUEST",
REMOVE_SELECTED_CUSTOM_REQUEST: "REMOVE_SELECTED_CUSTOM_REQUEST",
SELECT_REQUEST: "SELECT_REQUEST",
SELECT_DETAILS_PANEL_TAB: "SELECT_DETAILS_PANEL_TAB",
SEND_CUSTOM_REQUEST: "SEND_CUSTOM_REQUEST",
SET_REQUEST_FILTER_TEXT: "SET_REQUEST_FILTER_TEXT",
SORT_BY: "SORT_BY",
TOGGLE_REQUEST_FILTER_TYPE: "TOGGLE_REQUEST_FILTER_TYPE",

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

@ -1,222 +0,0 @@
/* 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/. */
/* globals window, dumpn, gNetwork, $, EVENTS, NetMonitorView */
"use strict";
const { Task } = require("devtools/shared/task");
const { writeHeaderText,
getKeyWithEvent,
getUrlQuery,
parseQueryString } = require("./request-utils");
const Actions = require("./actions/index");
/**
* Functions handling the custom request view.
*/
function CustomRequestView() {
dumpn("CustomRequestView was instantiated");
}
CustomRequestView.prototype = {
/**
* Initialization function, called when the network monitor is started.
*/
initialize: function () {
dumpn("Initializing the CustomRequestView");
this.updateCustomRequestEvent = getKeyWithEvent(this.onUpdate.bind(this));
$("#custom-pane").addEventListener("input",
this.updateCustomRequestEvent);
},
/**
* Destruction function, called when the network monitor is closed.
*/
destroy: function () {
dumpn("Destroying the CustomRequestView");
$("#custom-pane").removeEventListener("input",
this.updateCustomRequestEvent);
},
/**
* Populates this view with the specified data.
*
* @param object data
* The data source (this should be the attachment of a request item).
* @return object
* Returns a promise that resolves upon population the view.
*/
populate: Task.async(function* (data) {
$("#custom-url-value").value = data.url;
$("#custom-method-value").value = data.method;
this.updateCustomQuery(data.url);
if (data.requestHeaders) {
let headers = data.requestHeaders.headers;
$("#custom-headers-value").value = writeHeaderText(headers);
}
if (data.requestPostData) {
let postData = data.requestPostData.postData.text;
$("#custom-postdata-value").value = yield gNetwork.getString(postData);
}
window.emit(EVENTS.CUSTOMREQUESTVIEW_POPULATED);
}),
/**
* Handle user input in the custom request form.
*
* @param object field
* the field that the user updated.
*/
onUpdate: function (field) {
let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
let store = NetMonitorView.RequestsMenu.store;
let value;
switch (field) {
case "method":
value = $("#custom-method-value").value.trim();
store.dispatch(Actions.updateRequest(selectedItem.id, { method: value }));
break;
case "url":
value = $("#custom-url-value").value;
this.updateCustomQuery(value);
store.dispatch(Actions.updateRequest(selectedItem.id, { url: value }));
break;
case "query":
let query = $("#custom-query-value").value;
this.updateCustomUrl(query);
value = $("#custom-url-value").value;
store.dispatch(Actions.updateRequest(selectedItem.id, { url: value }));
break;
case "body":
value = $("#custom-postdata-value").value;
store.dispatch(Actions.updateRequest(selectedItem.id, {
requestPostData: {
postData: { text: value }
}
}));
break;
case "headers":
let headersText = $("#custom-headers-value").value;
value = parseHeadersText(headersText);
store.dispatch(Actions.updateRequest(selectedItem.id, {
requestHeaders: { headers: value }
}));
break;
}
},
/**
* Update the query string field based on the url.
*
* @param object url
* The URL to extract query string from.
*/
updateCustomQuery: function (url) {
const paramsArray = parseQueryString(getUrlQuery(url));
if (!paramsArray) {
$("#custom-query").hidden = true;
return;
}
$("#custom-query").hidden = false;
$("#custom-query-value").value = writeQueryText(paramsArray);
},
/**
* Update the url based on the query string field.
*
* @param object queryText
* The contents of the query string field.
*/
updateCustomUrl: function (queryText) {
let params = parseQueryText(queryText);
let queryString = writeQueryString(params);
let url = $("#custom-url-value").value;
let oldQuery = getUrlQuery(url);
let path = url.replace(oldQuery, queryString);
$("#custom-url-value").value = path;
}
};
/**
* Parse text representation of multiple HTTP headers.
*
* @param string text
* Text of headers
* @return array
* Array of headers info {name, value}
*/
function parseHeadersText(text) {
return parseRequestText(text, "\\S+?", ":");
}
/**
* Parse readable text list of a query string.
*
* @param string text
* Text of query string representation
* @return array
* Array of query params {name, value}
*/
function parseQueryText(text) {
return parseRequestText(text, ".+?", "=");
}
/**
* Parse a text representation of a name[divider]value list with
* the given name regex and divider character.
*
* @param string text
* Text of list
* @return array
* Array of headers info {name, value}
*/
function parseRequestText(text, namereg, divider) {
let regex = new RegExp("(" + namereg + ")\\" + divider + "\\s*(.+)");
let pairs = [];
for (let line of text.split("\n")) {
let matches;
if (matches = regex.exec(line)) { // eslint-disable-line
let [, name, value] = matches;
pairs.push({name: name, value: value});
}
}
return pairs;
}
/**
* Write out a list of query params into a chunk of text
*
* @param array params
* Array of query params {name, value}
* @return string
* List of query params in text format
*/
function writeQueryText(params) {
return params.map(({name, value}) => name + "=" + value).join("\n");
}
/**
* Write out a list of query params into a query string
*
* @param array params
* Array of query params {name, value}
* @return string
* Query string that can be appended to a url.
*/
function writeQueryString(params) {
return params.map(({name, value}) => name + "=" + value).join("&");
}
exports.CustomRequestView = CustomRequestView;

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

@ -62,9 +62,6 @@ const EVENTS = {
// Fired when Sidebar has finished being populated.
SIDEBAR_POPULATED: "NetMonitor:SidebarPopulated",
// Fired when CustomRequestView has finished being populated.
CUSTOMREQUESTVIEW_POPULATED: "NetMonitor:CustomRequestViewPopulated",
// Fired when charts have been displayed in the PerformanceStatisticsView.
PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",

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

@ -15,7 +15,6 @@ DIRS += [
DevToolsModules(
'constants.js',
'custom-request-view.js',
'events.js',
'filter-predicates.js',
'l10n.js',

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

@ -9,7 +9,6 @@
const { Task } = require("devtools/shared/task");
const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
const { RequestsMenuView } = require("./requests-menu-view");
const { CustomRequestView } = require("./custom-request-view");
const { SidebarView } = require("./sidebar-view");
const { ACTIVITY_TYPE } = require("./constants");
const { Prefs } = require("./prefs");
@ -19,6 +18,7 @@ const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
// Components
const CustomRequestPanel = createFactory(require("./shared/components/custom-request-panel"));
const DetailsPanel = createFactory(require("./shared/components/details-panel"));
const StatisticsPanel = createFactory(require("./components/statistics-panel"));
const Toolbar = createFactory(require("./components/toolbar"));
@ -33,6 +33,13 @@ var NetMonitorView = {
initialize: function () {
this._initializePanes();
this.customRequestPanel = $("#react-custom-request-panel-hook");
ReactDOM.render(Provider(
{ store: gStore },
CustomRequestPanel(),
), this.customRequestPanel);
this.detailsPanel = $("#react-details-panel-hook");
ReactDOM.render(Provider(
@ -55,7 +62,6 @@ var NetMonitorView = {
), this.toolbar);
this.RequestsMenu.initialize(gStore);
this.CustomRequest.initialize();
// Store watcher here is for observing the statisticsOpen state change.
// It should be removed once we migrate to react and apply react/redex binding.
@ -72,7 +78,7 @@ var NetMonitorView = {
destroy: function () {
this._isDestroyed = true;
this.RequestsMenu.destroy();
this.CustomRequest.destroy();
ReactDOM.unmountComponentAtNode(this.customRequestPanel);
ReactDOM.unmountComponentAtNode(this.detailsPanel);
ReactDOM.unmountComponentAtNode(this.statisticsPanel);
ReactDOM.unmountComponentAtNode(this.toolbar);
@ -195,6 +201,5 @@ function storeWatcher(initialValue, reduceValue, onChange) {
*/
NetMonitorView.Sidebar = new SidebarView();
NetMonitorView.RequestsMenu = new RequestsMenuView();
NetMonitorView.CustomRequest = new CustomRequestView();
exports.NetMonitorView = NetMonitorView;

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

@ -35,65 +35,8 @@
<deck id="details-pane"
hidden="true">
<vbox id="custom-pane"
class="tabpanel-content">
<hbox align="baseline">
<label data-localization="content=netmonitor.custom.newRequest"
class="plain tabpanel-summary-label
custom-header"/>
<hbox flex="1" pack="end"
class="devtools-toolbarbutton-group">
<button id="custom-request-send-button"
class="devtools-toolbarbutton"
data-localization="label=netmonitor.custom.send"/>
<button id="custom-request-close-button"
class="devtools-toolbarbutton"
data-localization="label=netmonitor.custom.cancel"/>
</hbox>
</hbox>
<hbox id="custom-method-and-url"
class="tabpanel-summary-container"
align="center">
<textbox id="custom-method-value"
data-key="method"/>
<textbox id="custom-url-value"
flex="1"
data-key="url"/>
</hbox>
<vbox id="custom-query"
class="tabpanel-summary-container custom-section">
<label class="plain tabpanel-summary-label"
data-localization="content=netmonitor.custom.query"/>
<textbox id="custom-query-value"
class="tabpanel-summary-input"
multiline="true"
rows="4"
wrap="off"
data-key="query"/>
</vbox>
<vbox id="custom-headers"
class="tabpanel-summary-container custom-section">
<label class="plain tabpanel-summary-label"
data-localization="content=netmonitor.custom.headers"/>
<textbox id="custom-headers-value"
class="tabpanel-summary-input"
multiline="true"
rows="8"
wrap="off"
data-key="headers"/>
</vbox>
<vbox id="custom-postdata"
class="tabpanel-summary-container custom-section">
<label class="plain tabpanel-summary-label"
data-localization="content=netmonitor.custom.postData"/>
<textbox id="custom-postdata-value"
class="tabpanel-summary-input"
multiline="true"
rows="6"
wrap="off"
data-key="body"/>
</vbox>
</vbox>
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="react-custom-request-panel-hook"/>
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="react-details-panel-hook"/>
</deck>

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

@ -8,13 +8,13 @@ const I = require("devtools/client/shared/vendor/immutable");
const { getUrlDetails } = require("../request-utils");
const {
ADD_REQUEST,
UPDATE_REQUEST,
CLEAR_REQUESTS,
SELECT_REQUEST,
PRESELECT_REQUEST,
CLONE_SELECTED_REQUEST,
REMOVE_SELECTED_CUSTOM_REQUEST,
OPEN_SIDEBAR,
REMOVE_SELECTED_CUSTOM_REQUEST,
SELECT_REQUEST,
SEND_CUSTOM_REQUEST,
UPDATE_REQUEST,
} = require("../constants");
const Request = I.Record({
@ -43,6 +43,9 @@ const Request = I.Record({
totalTime: undefined,
eventTimings: undefined,
headersSize: undefined,
// Text value is used for storing custom request query
// which only appears when user edit the custom requst form
customQueryValue: undefined,
requestHeaders: undefined,
requestHeadersFromUploadStream: undefined,
requestCookies: undefined,
@ -81,6 +84,7 @@ const UPDATE_PROPS = [
"totalTime",
"eventTimings",
"headersSize",
"customQueryValue",
"requestHeaders",
"requestHeadersFromUploadStream",
"requestCookies",
@ -92,6 +96,29 @@ const UPDATE_PROPS = [
"formDataSections",
];
/**
* Remove the currently selected custom request.
*/
function closeCustomRequest(state) {
let { requests, selectedId } = state;
if (!selectedId) {
return state;
}
let removedRequest = requests.get(selectedId);
// Only custom requests can be removed
if (!removedRequest || !removedRequest.isCustom) {
return state;
}
return state.withMutations(st => {
st.requests = st.requests.delete(selectedId);
st.selectedId = null;
});
}
function requestsReducer(state = new Requests(), action) {
switch (action.type) {
case ADD_REQUEST: {
@ -119,7 +146,6 @@ function requestsReducer(state = new Requests(), action) {
}
});
}
case UPDATE_REQUEST: {
let { requests, lastEndedMillis } = state;
@ -166,9 +192,6 @@ function requestsReducer(state = new Requests(), action) {
case SELECT_REQUEST: {
return state.set("selectedId", action.id);
}
case PRESELECT_REQUEST: {
return state.set("preselectedId", action.id);
}
case CLONE_SELECTED_REQUEST: {
let { requests, selectedId } = state;
@ -197,22 +220,13 @@ function requestsReducer(state = new Requests(), action) {
});
}
case REMOVE_SELECTED_CUSTOM_REQUEST: {
let { requests, selectedId } = state;
if (!selectedId) {
return state;
}
// Only custom requests can be removed
let removedRequest = requests.get(selectedId);
if (!removedRequest || !removedRequest.isCustom) {
return state;
}
return state.withMutations(st => {
st.requests = requests.delete(selectedId);
st.selectedId = null;
});
return closeCustomRequest(state);
}
case SEND_CUSTOM_REQUEST: {
// When a new request with a given id is added in future, select it immediately.
// where we know in advance the ID of the request, at a time when it
// wasn't sent yet.
return closeCustomRequest(state.set("preselectedId", action.id));
}
case OPEN_SIDEBAR: {
if (!action.open) {

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

@ -6,35 +6,8 @@
"use strict";
const { KeyCodes } = require("devtools/client/shared/keycodes");
const { Task } = require("devtools/shared/task");
/**
* Helper method to get a wrapped function which can be bound to as
* an event listener directly and is executed only when data-key is
* present in event.target.
*
* @param {function} callback - function to execute execute when data-key
* is present in event.target.
* @param {bool} onlySpaceOrReturn - flag to indicate if callback should only
* be called when the space or return button
* is pressed
* @return {function} wrapped function with the target data-key as the first argument
* and the event as the second argument.
*/
function getKeyWithEvent(callback, onlySpaceOrReturn) {
return function (event) {
let key = event.target.getAttribute("data-key");
let filterKeyboardEvent = !onlySpaceOrReturn ||
event.keyCode === KeyCodes.DOM_VK_SPACE ||
event.keyCode === KeyCodes.DOM_VK_RETURN;
if (key && filterKeyboardEvent) {
callback(key);
}
};
}
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request.
@ -254,7 +227,6 @@ function parseQueryString(query) {
}
module.exports = {
getKeyWithEvent,
getFormDataSections,
fetchHeaders,
formDataURI,

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

@ -139,9 +139,6 @@ RequestsMenuView.prototype = {
},
));
this.sendCustomRequestEvent = this.sendCustomRequest.bind(this);
this.closeCustomRequestEvent = this.closeCustomRequest.bind(this);
this._summary = $("#requests-menu-network-summary-button");
this._summary.setAttribute("label", L10N.getStr("networkMenu.empty"));
@ -157,17 +154,6 @@ RequestsMenuView.prototype = {
{ store: this.store },
RequestList()
), this.mountPoint);
window.once("connected", this._onConnect.bind(this));
},
_onConnect() {
if (NetMonitorController.supportsCustomRequest) {
$("#custom-request-send-button")
.addEventListener("click", this.sendCustomRequestEvent);
$("#custom-request-close-button")
.addEventListener("click", this.closeCustomRequestEvent);
}
},
/**
@ -178,13 +164,6 @@ RequestsMenuView.prototype = {
Prefs.filters = getActiveFilters(this.store.getState());
// this.flushRequestsTask.disarm();
$("#custom-request-send-button")
.removeEventListener("click", this.sendCustomRequestEvent);
$("#custom-request-close-button")
.removeEventListener("click", this.closeCustomRequestEvent);
this._splitter.removeEventListener("mouseup", this.onResize);
window.removeEventListener("resize", this.onResize);
@ -426,48 +405,7 @@ RequestsMenuView.prototype = {
this.store.dispatch(Actions.resizeWaterfall(width));
}
});
},
/**
* Create a new custom request form populated with the data from
* the currently selected request.
*/
cloneSelectedRequest() {
this.store.dispatch(Actions.cloneSelectedRequest());
},
/**
* Send a new HTTP request using the data in the custom request form.
*/
sendCustomRequest: function () {
let selected = getSelectedRequest(this.store.getState());
let data = {
url: selected.url,
method: selected.method,
httpVersion: selected.httpVersion,
};
if (selected.requestHeaders) {
data.headers = selected.requestHeaders.headers;
}
if (selected.requestPostData) {
data.body = selected.requestPostData.postData.text;
}
NetMonitorController.webConsoleClient.sendHTTPRequest(data, response => {
let id = response.eventActor.actor;
this.store.dispatch(Actions.preselectRequest(id));
});
this.closeCustomRequest();
},
/**
* Remove the currently selected custom request.
*/
closeCustomRequest() {
this.store.dispatch(Actions.removeSelectedCustomRequest());
},
}
};
exports.RequestsMenuView = RequestsMenuView;

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

@ -94,7 +94,7 @@ const getDisplayedRequestsSummary = createSelector(
const getSelectedRequest = createSelector(
state => state.requests,
({ selectedId, requests }) => selectedId ? requests.get(selectedId) : null
({ selectedId, requests }) => selectedId ? requests.get(selectedId) : undefined
);
function getRequestById(state, id) {

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

@ -0,0 +1,257 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { L10N } = require("../../l10n");
const Actions = require("../../actions/index");
const { getSelectedRequest } = require("../../selectors/index");
const {
getUrlQuery,
parseQueryString,
writeHeaderText,
} = require("../../request-utils");
const {
button,
div,
input,
textarea,
} = DOM;
const CUSTOM_CANCEL = L10N.getStr("netmonitor.custom.cancel");
const CUSTOM_HEADERS = L10N.getStr("netmonitor.custom.headers");
const CUSTOM_NEW_REQUEST = L10N.getStr("netmonitor.custom.newRequest");
const CUSTOM_POSTDATA = L10N.getStr("netmonitor.custom.postData");
const CUSTOM_QUERY = L10N.getStr("netmonitor.custom.query");
const CUSTOM_SEND = L10N.getStr("netmonitor.custom.send");
function CustomRequestPanel({
removeSelectedCustomRequest,
request = {},
sendCustomRequest,
updateRequest,
}) {
let {
method,
customQueryValue,
requestHeaders,
requestPostData,
url,
} = request;
let headers = "";
if (requestHeaders) {
headers = requestHeaders.customHeadersValue ?
requestHeaders.customHeadersValue : writeHeaderText(requestHeaders.headers);
}
let queryArray = url ? parseQueryString(getUrlQuery(url)) : [];
let params = customQueryValue;
if (!params) {
params = queryArray ?
queryArray.map(({ name, value }) => name + "=" + value).join("\n") : "";
}
let postData = requestPostData && requestPostData.postData.text ?
requestPostData.postData.text : "";
return (
div({ className: "custom-request-panel" },
div({ className: "tabpanel-summary-container custom-request" },
div({ className: "custom-request-label custom-header" },
CUSTOM_NEW_REQUEST
),
button({
className: "devtools-button",
id: "custom-request-send-button",
onClick: sendCustomRequest,
},
CUSTOM_SEND
),
button({
className: "devtools-button",
id: "custom-request-close-button",
onClick: removeSelectedCustomRequest,
},
CUSTOM_CANCEL
),
),
div({
className: "tabpanel-summary-container custom-method-and-url",
id: "custom-method-and-url",
},
input({
className: "custom-method-value",
id: "custom-method-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
value: method || "GET",
}),
input({
className: "custom-url-value",
id: "custom-url-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
value: url || "http://",
}),
),
// Hide query field when there is no params
params ? div({
className: "tabpanel-summary-container custom-section",
id: "custom-query",
},
div({ className: "custom-request-label" }, CUSTOM_QUERY),
textarea({
className: "tabpanel-summary-input",
id: "custom-query-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
rows: 4,
value: params,
wrap: "off",
})
) : null,
div({
id: "custom-headers",
className: "tabpanel-summary-container custom-section",
},
div({ className: "custom-request-label" }, CUSTOM_HEADERS),
textarea({
className: "tabpanel-summary-input",
id: "custom-headers-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
rows: 8,
value: headers,
wrap: "off",
})
),
div({
id: "custom-postdata",
className: "tabpanel-summary-container custom-section",
},
div({ className: "custom-request-label" }, CUSTOM_POSTDATA),
textarea({
className: "tabpanel-summary-input",
id: "custom-postdata-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
rows: 6,
value: postData,
wrap: "off",
})
),
)
);
}
CustomRequestPanel.displayName = "CustomRequestPanel";
CustomRequestPanel.propTypes = {
removeSelectedCustomRequest: PropTypes.func.isRequired,
request: PropTypes.object,
sendCustomRequest: PropTypes.func.isRequired,
updateRequest: PropTypes.func.isRequired,
};
/**
* Parse a text representation of a name[divider]value list with
* the given name regex and divider character.
*
* @param {string} text - Text of list
* @return {array} array of headers info {name, value}
*/
function parseRequestText(text, namereg, divider) {
let regex = new RegExp(`(${namereg})\\${divider}\\s*(.+)`);
let pairs = [];
for (let line of text.split("\n")) {
let matches = regex.exec(line);
if (matches) {
let [, name, value] = matches;
pairs.push({ name, value });
}
}
return pairs;
}
/**
* Update Custom Request Fields
*
* @param {Object} evt click event
* @param {Object} request current request
* @param {updateRequest} updateRequest action
*/
function updateCustomRequestFields(evt, request, updateRequest) {
const val = evt.target.value;
let data;
switch (evt.target.id) {
case "custom-headers-value":
let customHeadersValue = val || "";
// Parse text representation of multiple HTTP headers
let headersArray = parseRequestText(customHeadersValue, "\\S+?", ":");
// Remove temp customHeadersValue while query string is parsable
if (customHeadersValue === "" ||
headersArray.length === customHeadersValue.split("\n").length) {
customHeadersValue = null;
}
data = {
requestHeaders: {
customHeadersValue,
headers: headersArray,
},
};
break;
case "custom-method-value":
data = { method: val.trim() };
break;
case "custom-postdata-value":
data = {
requestPostData: {
postData: { text: val },
}
};
break;
case "custom-query-value":
let customQueryValue = val || "";
// Parse readable text list of a query string
let queryArray = customQueryValue ?
parseRequestText(customQueryValue, ".+?", "=") : [];
// Write out a list of query params into a query string
let queryString = queryArray.map(
({ name, value }) => name + "=" + value).join("&");
let url = queryString ? [request.url.split("?")[0], queryString].join("?") :
request.url.split("?")[0];
// Remove temp customQueryValue while query string is parsable
if (customQueryValue === "" ||
queryArray.length === customQueryValue.split("\n").length) {
customQueryValue = null;
}
data = {
customQueryValue,
url,
};
break;
case "custom-url-value":
data = {
customQueryValue: null,
url: val
};
break;
default:
break;
}
if (data) {
// All updateRequest batch mode should be disabled to make UI editing in sync
updateRequest(request.id, data, false);
}
}
module.exports = connect(
(state) => ({ request: getSelectedRequest(state) }),
(dispatch) => ({
removeSelectedCustomRequest: () => dispatch(Actions.removeSelectedCustomRequest()),
sendCustomRequest: () => dispatch(Actions.sendCustomRequest()),
updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
})
)(CustomRequestPanel);

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

@ -4,6 +4,7 @@
DevToolsModules(
'cookies-panel.js',
'custom-request-panel.js',
'details-panel.js',
'editor.js',
'headers-mdn.js',

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

@ -38,10 +38,6 @@ SidebarView.prototype = {
populate: Task.async(function* (data) {
let isCustom = data.isCustom;
if (isCustom) {
yield NetMonitorView.CustomRequest.populate(data);
}
$("#details-pane").selectedIndex = isCustom ? 0 : 1;
window.emit(EVENTS.SIDEBAR_POPULATED);

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

@ -134,6 +134,7 @@ skip-if = (os == 'linux' && debug && bits == 32) # Bug 1303439
[browser_net_req-resp-bodies.js]
[browser_net_resend_cors.js]
[browser_net_resend_headers.js]
[browser_net_resend.js]
[browser_net_security-details.js]
[browser_net_security-error.js]
[browser_net_security-icon-click.js]

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

@ -30,13 +30,13 @@ add_task(function* () {
wait = waitForDOM(document, ".raw-headers-container textarea", 2);
EventUtils.sendMouseEvent({ type: "click" },
document.querySelectorAll(".tool-button")[1]);
document.querySelectorAll(".headers-summary .tool-button")[1]);
yield wait;
testShowRawHeaders(origItem);
EventUtils.sendMouseEvent({ type: "click" },
document.querySelectorAll(".tool-button")[1]);
document.querySelectorAll(".headers-summary .tool-button")[1]);
testHideRawHeaders(document);

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

@ -17,7 +17,8 @@ add_task(function* () {
info("Starting test... ");
let { panelWin } = monitor;
let { document, EVENTS, NetMonitorView } = panelWin;
let { document, gStore, NetMonitorView, windowRequire } = panelWin;
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
@ -33,9 +34,7 @@ add_task(function* () {
RequestsMenu.selectedItem = origItem;
// add a new custom request cloned from selected request
let onPopulated = panelWin.once(EVENTS.CUSTOMREQUESTVIEW_POPULATED);
RequestsMenu.cloneSelectedRequest();
yield onPopulated;
gStore.dispatch(Actions.cloneSelectedRequest());
testCustomForm(origItem);
@ -50,7 +49,7 @@ add_task(function* () {
// send the new request
wait = waitForNetworkEvents(monitor, 0, 1);
RequestsMenu.sendCustomRequest();
gStore.dispatch(Actions.sendCustomRequest());
yield wait;
let sentItem = RequestsMenu.selectedItem;

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

@ -12,7 +12,8 @@ add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CORS_URL);
info("Starting test... ");
let { EVENTS, NetMonitorView } = monitor.panelWin;
let { gStore, NetMonitorView, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
@ -27,51 +28,45 @@ add_task(function* () {
yield wait;
const METHODS = ["OPTIONS", "POST"];
const ITEMS = METHODS.map((val, i) => RequestsMenu.getItemAtIndex(i));
// Check the requests that were sent
for (let [i, method] of METHODS.entries()) {
let item = RequestsMenu.getItemAtIndex(i);
is(item.method, method, `The ${method} request has the right method`);
is(item.url, requestUrl, `The ${method} request has the right URL`);
}
ITEMS.forEach((item, i) => {
is(item.method, METHODS[i], `The ${item.method} request has the right method`);
is(item.url, requestUrl, `The ${item.method} request has the right URL`);
});
// Resend both requests without modification. Wait for resent OPTIONS, then POST.
// POST is supposed to have no preflight OPTIONS request this time (CORS is disabled)
let onRequests = waitForNetworkEvents(monitor, 1, 1);
for (let [i, method] of METHODS.entries()) {
let item = RequestsMenu.getItemAtIndex(i);
info(`Selecting the ${method} request (at index ${i})`);
let onRequests = waitForNetworkEvents(monitor, 1, 0);
ITEMS.forEach((item) => {
info(`Selecting the ${item.method} request`);
RequestsMenu.selectedItem = item;
info("Cloning the selected request into a custom clone");
let onPopulate = monitor.panelWin.once(EVENTS.CUSTOMREQUESTVIEW_POPULATED);
RequestsMenu.cloneSelectedRequest();
yield onPopulate;
gStore.dispatch(Actions.cloneSelectedRequest());
info("Sending the cloned request (without change)");
RequestsMenu.sendCustomRequest();
}
gStore.dispatch(Actions.sendCustomRequest());
});
info("Waiting for both resent requests");
yield onRequests;
// Check the resent requests
for (let [i, method] of METHODS.entries()) {
let index = i + 2;
let item = RequestsMenu.getItemAtIndex(index);
is(item.method, method, `The ${method} request has the right method`);
is(item.url, requestUrl, `The ${method} request has the right URL`);
is(item.status, 200, `The ${method} response has the right status`);
ITEMS.forEach((item, i) => {
is(item.method, METHODS[i], `The ${item.method} request has the right method`);
is(item.url, requestUrl, `The ${item.method} request has the right URL`);
is(item.status, 200, `The ${item.method} response has the right status`);
if (method === "POST") {
if (item.method === "POST") {
is(item.requestPostData.postData.text, "post-data",
"The POST request has the right POST data");
// eslint-disable-next-line mozilla/no-cpows-in-tests
is(item.responseContent.content.text, "Access-Control-Allow-Origin: *",
"The POST response has the right content");
}
}
});
info("Finishing the test");
return teardown(monitor);

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

@ -28,6 +28,7 @@
align-items: center;
}
.custom-request-panel,
#details-pane {
/* Make details-pane's width adjustable by splitter */
min-width: 50px;
@ -45,18 +46,10 @@
display: none;
}
#custom-pane {
overflow: auto;
}
#response-content-image-box {
overflow: auto;
}
#network-statistics-charts {
overflow: auto;
}
.cropped-textbox .textbox-input {
/* workaround for textbox not supporting the @crop attribute */
text-overflow: ellipsis;
@ -96,7 +89,7 @@
--table-splitter-color: rgba(0,0,0,0.15);
--table-zebra-background: rgba(0,0,0,0.05);
--timing-blocked-color: rgba(235, 83, 104, 0.8); /* red */
--timing-blocked-color: rgba(235, 83, 104, 0.8); /* red */
--timing-dns-color: rgba(223, 128, 255, 0.8); /* pink */
--timing-connect-color: rgba(217, 102, 41, 0.8); /* orange */
--timing-send-color: rgba(70, 175, 227, 0.8); /* light blue */
@ -661,14 +654,6 @@
/* Network request details tabpanels */
.tabpanel-content {
background-color: var(--theme-sidebar-background);
}
.theme-dark .tabpanel-content {
color: var(--theme-selection-color);
}
.theme-firebug .variables-view-scope:focus > .title {
color: var(--theme-body-color);
}
@ -785,24 +770,61 @@
}
}
/* Custom request form */
/* Custom request view */
#custom-pane {
padding: 0.6em 0.5em;
.custom-request-panel {
overflow: auto;
/* Full view hight - toolbar height */
height: calc(100vh - 24px);
padding: 6px 4px;
background-color: var(--theme-sidebar-background);
}
.theme-dark .custom-request-panel {
color: var(--theme-selection-color);
}
.custom-request-label {
font-weight: 600;
}
.custom-request-panel textarea {
resize: none;
font: message-box;
}
.custom-request-panel .devtools-button {
margin: 3px 1px;
min-width: 78px;
}
.custom-header,
.custom-method-and-url,
.custom-request,
.custom-section {
display: flex;
}
.custom-header {
flex-grow: 1;
font-size: 1.1em;
padding-top: 4px;
}
.custom-section {
flex-direction: column;
margin-top: 0.5em;
}
#custom-method-value {
.custom-method-value {
width: 4.5em;
}
.custom-url-value {
flex-grow: 1;
margin-inline-start: 6px;
}
/* Performance analysis buttons */
#requests-menu-network-summary-button {
@ -1002,6 +1024,11 @@
padding-left: 8px;
}
.custom-request-panel {
height: 100%;
}
.custom-request-panel,
#details-pane {
margin: 0 !important;
/* To prevent all the margin hacks to hide the sidebar. */
@ -1212,34 +1239,39 @@
}
.headers-summary .tool-button {
border: 1px solid transparent;
color: var(--theme-body-color);
transition: background 0.05s ease-in-out;
margin-inline-end: 6px;
padding: 0 5px;
}
.theme-light .headers-summary .tool-button {
.tool-button {
background: transparent;
border: none;
border-color: var(--toolbar-button-border-color);
color: var(--theme-body-color);
min-height: 18px;
transition: background 0.05s ease-in-out;
}
.theme-light .tool-button {
background-color: var(--toolbar-tab-hover);
}
.theme-light .headers-summary .tool-button:hover {
.theme-light .tool-button:hover {
background-color: rgba(170, 170, 170, 0.3);
}
.theme-light .headers-summary .tool-button:hover:active {
.theme-light .tool-button:hover:active {
background-color: var(--toolbar-tab-hover-active);
}
.theme-dark .headers-summary .tool-button {
.theme-dark .tool-button {
background-color: rgba(0, 0, 0, 0.2);
}
.theme-dark .headers-summary .tool-button:hover {
.theme-dark .tool-button:hover {
background-color: rgba(0, 0, 0, 0.3);
}
.theme-dark .headers-summary .tool-button:hover:active {
.theme-dark .tool-button:hover:active {
background-color: rgba(0, 0, 0, 0.4);
}
@ -1254,7 +1286,7 @@
.headers-summary .raw-headers {
width: 50%;
padding: 0px 4px;
padding: 0 4px;
}
.headers-summary .raw-headers textarea {
@ -1297,6 +1329,7 @@
overflow: hidden;
}
#react-custom-request-panel-hook,
#statistics-panel,
#react-details-panel-hook {
display: flex;

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

@ -1870,6 +1870,10 @@ ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PR
extraArgs.push_back("-stringPrefs");
extraArgs.push_back(stringPrefs.str());
if (gSafeMode) {
extraArgs.push_back("-safeMode");
}
if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) {
MarkAsDead();
return false;

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

@ -197,6 +197,9 @@ ContentProcess::Init(int aArgc, char* aArgv[])
SET_PREF_PHASE(END_INIT_PREFS);
foundStringPrefs = true;
}
else if (!strcmp(aArgv[idx], "-safeMode")) {
gSafeMode = true;
}
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
else if (!strcmp(aArgv[idx], "-profile")) {

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

@ -16,7 +16,7 @@ namespace mozilla {
namespace _ipdltest {
bool
IPDLUnitTestProcessChild::Init()
IPDLUnitTestProcessChild::Init(int aArgc, char* aArgv[])
{
IPDLUnitTestChildInit(IOThreadChild::channel(),
ParentPid(),

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

@ -23,7 +23,7 @@ public:
~IPDLUnitTestProcessChild()
{ }
virtual bool Init();
virtual bool Init(int aArgc, char* aArgv[]) override;
};
} // namespace _ipdltest

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

@ -1077,7 +1077,7 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
// Hence the weird SELECT * FROM (SELECT ...relevant suggested sites... LIMIT ?)
" SELECT * FROM (SELECT " +
Bookmarks._ID + ", " +
Bookmarks._ID + " AS " + Combined.BOOKMARK_ID + ", " +
" NULL " + " AS " + Combined.BOOKMARK_ID + ", " +
" -1 AS " + Combined.HISTORY_ID + ", " +
Bookmarks.URL + ", " +
Bookmarks.TITLE + ", " +

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

@ -26,9 +26,10 @@ public class TopSite implements Item {
final String title = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Combined.TITLE));
final int type = cursor.getInt(cursor.getColumnIndexOrThrow(BrowserContract.TopSites.TYPE));
// We can't figure out bookmark state of a pin, so we leave it as unknown to be queried later.
// We can't figure out bookmark state of a pin or suggested site, so we leave it as unknown to be queried later.
Boolean isBookmarked = null;
if (type != BrowserContract.TopSites.TYPE_PINNED) {
if (type != BrowserContract.TopSites.TYPE_PINNED &&
type != BrowserContract.TopSites.TYPE_SUGGESTED) {
isBookmarked = !cursor.isNull(cursor.getColumnIndexOrThrow(BrowserContract.Combined.BOOKMARK_ID));
}

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

@ -45,6 +45,9 @@ import android.util.Log;
public class GeckoNetworkManager extends BroadcastReceiver implements BundleEventListener {
private static final String LOGTAG = "GeckoNetworkManager";
// If network configuration and/or status changed, we send details of what changed.
// If we received a "check out new network state!" intent from the OS but nothing in it looks
// different, we ignore it. See Bug 1330836 for some relevant details.
private static final String LINK_DATA_CHANGED = "changed";
private static GeckoNetworkManager instance;
@ -321,8 +324,9 @@ public class GeckoNetworkManager extends BroadcastReceiver implements BundleEven
* Send current network state and connection type to whomever is listening.
*/
private void sendNetworkStateToListeners(final Context context) {
if (currentConnectionType != previousConnectionType ||
currentConnectionSubtype != previousConnectionSubtype) {
final boolean connectionTypeOrSubtypeChanged = currentConnectionType != previousConnectionType ||
currentConnectionSubtype != previousConnectionSubtype;
if (connectionTypeOrSubtypeChanged) {
previousConnectionType = currentConnectionType;
previousConnectionSubtype = currentConnectionSubtype;
@ -341,20 +345,26 @@ public class GeckoNetworkManager extends BroadcastReceiver implements BundleEven
}
}
final String status;
// If neither network status nor network configuration changed, do nothing.
if (currentNetworkStatus == previousNetworkStatus && !connectionTypeOrSubtypeChanged) {
return;
}
if (currentNetworkStatus != previousNetworkStatus) {
// If network status remains the same, send "changed". Otherwise, send new network status.
// See Bug 1330836 for relevant details.
final String status;
if (currentNetworkStatus == previousNetworkStatus) {
status = LINK_DATA_CHANGED;
} else {
previousNetworkStatus = currentNetworkStatus;
status = currentNetworkStatus.value;
} else {
status = LINK_DATA_CHANGED;
}
if (GeckoThread.isRunning()) {
onStatusChanged(status);
} else {
GeckoThread.queueNativeCall(GeckoNetworkManager.class, "onStatusChanged",
String.class, status);
String.class, status);
}
}

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

@ -11,6 +11,7 @@
#include "mozilla/Logging.h"
#include "mozilla/Telemetry.h"
#include "nsCOMArray.h"
#include "nsDependentSubstring.h"
#include "nsIAsyncInputStream.h"
#include "nsIFile.h"
#include "nsIMutableArray.h"
@ -893,11 +894,11 @@ BackgroundFileSaver::ExtractSignatureInfo(const nsAString& filePath)
continue;
}
nsCOMPtr<nsIX509Cert> nssCert = nullptr;
rv = certDB->ConstructX509(
nsDependentCSubstring certDER(
reinterpret_cast<char *>(
certChainElement->pCertContext->pbCertEncoded),
certChainElement->pCertContext->cbCertEncoded,
getter_AddRefs(nssCert));
certChainElement->pCertContext->cbCertEncoded);
rv = certDB->ConstructX509(certDER, getter_AddRefs(nssCert));
if (!nssCert) {
extractionSuccess = false;
LOG(("Couldn't create NSS cert [this = %p]", this));

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

@ -8,6 +8,7 @@
#include "mozilla/net/DNS.h"
#include "nsAutoPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsDependentSubstring.h"
#include "nsIServerSocket.h"
#include "nsITimer.h"
#include "nsIX509Cert.h"
@ -467,9 +468,10 @@ TLSServerConnectionInfo::HandshakeCallback(PRFileDesc* aFD)
}
nsCOMPtr<nsIX509Cert> clientCertPSM;
rv = certDB->ConstructX509(reinterpret_cast<char*>(clientCert->derCert.data),
clientCert->derCert.len,
getter_AddRefs(clientCertPSM));
nsDependentCSubstring certDER(
reinterpret_cast<char*>(clientCert->derCert.data),
clientCert->derCert.len);
rv = certDB->ConstructX509(certDER, getter_AddRefs(clientCertPSM));
if (NS_FAILED(rv)) {
return rv;
}

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

@ -82,7 +82,7 @@ interface nsIX509CertDB : nsISupports {
* @param aDBkey Database internal key, as obtained using
* attribute dbkey in nsIX509Cert.
*/
nsIX509Cert findCertByDBKey(in string aDBkey);
nsIX509Cert findCertByDBKey(in ACString aDBkey);
/**
* Find a certificate by email address.
@ -92,7 +92,7 @@ interface nsIX509CertDB : nsISupports {
*
* @return The matching certificate if found.
*/
nsIX509Cert findCertByEmailAddress(in string aEmailAddress);
nsIX509Cert findCertByEmailAddress(in ACString aEmailAddress);
/**
* Use this to import a stream sent down as a mime type into
@ -220,10 +220,9 @@ interface nsIX509CertDB : nsISupports {
*
* @param certDER The raw representation of a certificate,
* encoded as raw DER.
* @param length The length of the DER string.
* @return The new certificate object.
*/
nsIX509Cert constructX509(in string certDER, in unsigned long length);
nsIX509Cert constructX509(in ACString certDER);
/**
* Verifies the signature on the given JAR file to verify that it has a
@ -337,7 +336,7 @@ interface nsIX509CertDB : nsISupports {
verifyCertAtTime(in nsIX509Cert aCert,
in int64_t /*SECCertificateUsage*/ aUsage,
in uint32_t aFlags,
in string aHostname,
in ACString aHostname,
in uint64_t aTime,
out nsIX509CertList aVerifiedChain,
out bool aHasEVPolicy);
@ -345,7 +344,7 @@ interface nsIX509CertDB : nsISupports {
verifyCertNow(in nsIX509Cert aCert,
in int64_t /*SECCertificateUsage*/ aUsage,
in uint32_t aFlags,
in string aHostname,
in ACString aHostname,
out nsIX509CertList aVerifiedChain,
out bool aHasEVPolicy);
@ -356,7 +355,7 @@ interface nsIX509CertDB : nsISupports {
void asyncVerifyCertAtTime(in nsIX509Cert aCert,
in int64_t /*SECCertificateUsage*/ aUsage,
in uint32_t aFlags,
in string aHostname,
in ACString aHostname,
in uint64_t aTime,
in nsICertVerificationCallback aCallback);

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

@ -94,13 +94,16 @@ nsNSSCertificateDB::~nsNSSCertificateDB()
}
NS_IMETHODIMP
nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey,nsIX509Cert** _cert)
nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
/*out*/ nsIX509Cert** _cert)
{
NS_ENSURE_ARG_POINTER(aDBKey);
NS_ENSURE_ARG(aDBKey[0]);
NS_ENSURE_ARG_POINTER(_cert);
*_cert = nullptr;
if (aDBKey.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
}
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
@ -124,7 +127,7 @@ nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey,nsIX509Cert** _cert)
}
nsresult
nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey,
nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
UniqueCERTCertificate& cert)
{
static_assert(sizeof(uint64_t) == 8, "type size sanity check");
@ -994,7 +997,7 @@ nsNSSCertificateDB::ExportPKCS12File(nsIFile* aFile, uint32_t count,
}
NS_IMETHODIMP
nsNSSCertificateDB::FindCertByEmailAddress(const char* aEmailAddress,
nsNSSCertificateDB::FindCertByEmailAddress(const nsACString& aEmailAddress,
nsIX509Cert** _retval)
{
nsNSSShutDownPreventionLock locker;
@ -1005,8 +1008,9 @@ nsNSSCertificateDB::FindCertByEmailAddress(const char* aEmailAddress,
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
const nsCString& flatEmailAddress = PromiseFlatCString(aEmailAddress);
UniqueCERTCertList certlist(
PK11_FindCertsFromEmailAddress(aEmailAddress, nullptr));
PK11_FindCertsFromEmailAddress(flatEmailAddress.get(), nullptr));
if (!certlist)
return NS_ERROR_FAILURE;
@ -1073,12 +1077,11 @@ nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
return rv;
}
return ConstructX509(certDER.get(), certDER.Length(), _retval);
return ConstructX509(certDER, _retval);
}
NS_IMETHODIMP
nsNSSCertificateDB::ConstructX509(const char* certDER,
uint32_t lengthDER,
nsNSSCertificateDB::ConstructX509(const nsACString& certDER,
nsIX509Cert** _retval)
{
nsNSSShutDownPreventionLock locker;
@ -1089,13 +1092,13 @@ nsNSSCertificateDB::ConstructX509(const char* certDER,
return NS_ERROR_INVALID_POINTER;
}
SECItem secitem_cert;
secitem_cert.type = siDERCertBuffer;
secitem_cert.data = (unsigned char*)certDER;
secitem_cert.len = lengthDER;
SECItem certData;
certData.type = siDERCertBuffer;
certData.data = BitwiseCast<unsigned char*, const char*>(certDER.BeginReading());
certData.len = certDER.Length();
UniqueCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
&secitem_cert, nullptr,
&certData, nullptr,
false, true));
if (!cert)
return (PORT_GetError() == SEC_ERROR_NO_MEMORY)
@ -1364,7 +1367,7 @@ nsresult
VerifyCertAtTime(nsIX509Cert* aCert,
int64_t /*SECCertificateUsage*/ aUsage,
uint32_t aFlags,
const char* aHostname,
const nsACString& aHostname,
mozilla::pkix::Time aTime,
nsIX509CertList** aVerifiedChain,
bool* aHasEVPolicy,
@ -1392,13 +1395,14 @@ VerifyCertAtTime(nsIX509Cert* aCert,
SECOidTag evOidPolicy;
mozilla::pkix::Result result;
if (aHostname && aUsage == certificateUsageSSLServer) {
const nsCString& flatHostname = PromiseFlatCString(aHostname);
if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
result = certVerifier->VerifySSLServerCert(nssCert,
nullptr, // stapledOCSPResponse
nullptr, // sctsFromTLSExtension
aTime,
nullptr, // Assume no context
aHostname,
flatHostname.get(),
resultChain,
false, // don't save intermediates
aFlags,
@ -1407,7 +1411,8 @@ VerifyCertAtTime(nsIX509Cert* aCert,
} else {
result = certVerifier->VerifyCert(nssCert.get(), aUsage, aTime,
nullptr, // Assume no context
aHostname,
aHostname.IsVoid() ? nullptr
: flatHostname.get(),
resultChain,
aFlags,
nullptr, // stapledOCSPResponse
@ -1434,7 +1439,7 @@ NS_IMETHODIMP
nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert,
int64_t /*SECCertificateUsage*/ aUsage,
uint32_t aFlags,
const char* aHostname,
const nsACString& aHostname,
nsIX509CertList** aVerifiedChain,
bool* aHasEVPolicy,
int32_t* /*PRErrorCode*/ _retval)
@ -1453,7 +1458,7 @@ NS_IMETHODIMP
nsNSSCertificateDB::VerifyCertAtTime(nsIX509Cert* aCert,
int64_t /*SECCertificateUsage*/ aUsage,
uint32_t aFlags,
const char* aHostname,
const nsACString& aHostname,
uint64_t aTime,
nsIX509CertList** aVerifiedChain,
bool* aHasEVPolicy,
@ -1473,7 +1478,7 @@ class VerifyCertAtTimeTask final : public CryptoTask
{
public:
VerifyCertAtTimeTask(nsIX509Cert* aCert, int64_t aUsage, uint32_t aFlags,
const char* aHostname, uint64_t aTime,
const nsACString& aHostname, uint64_t aTime,
nsICertVerificationCallback* aCallback)
: mCert(aCert)
, mUsage(aUsage)
@ -1494,13 +1499,7 @@ private:
if (!certDB) {
return NS_ERROR_FAILURE;
}
// Unfortunately mHostname will have made the empty string out of a null
// pointer passed in the constructor. If we pass the empty string on to
// VerifyCertAtTime with the usage certificateUsageSSLServer, it will call
// VerifySSLServerCert, which expects a non-empty hostname. To avoid this,
// check the length and use nullptr if appropriate.
const char* hostname = mHostname.Length() > 0 ? mHostname.get() : nullptr;
return certDB->VerifyCertAtTime(mCert, mUsage, mFlags, hostname, mTime,
return certDB->VerifyCertAtTime(mCert, mUsage, mFlags, mHostname, mTime,
getter_AddRefs(mVerifiedCertList),
&mHasEVPolicy, &mPRErrorCode);
}
@ -1534,7 +1533,7 @@ NS_IMETHODIMP
nsNSSCertificateDB::AsyncVerifyCertAtTime(nsIX509Cert* aCert,
int64_t /*SECCertificateUsage*/ aUsage,
uint32_t aFlags,
const char* aHostname,
const nsACString& aHostname,
uint64_t aTime,
nsICertVerificationCallback* aCallback)
{

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

@ -40,7 +40,7 @@ public:
// This is a separate static method so nsNSSComponent can use it during NSS
// initialization. Other code should probably not use it.
static nsresult
FindCertByDBKey(const char* aDBKey, mozilla::UniqueCERTCertificate& cert);
FindCertByDBKey(const nsACString& aDBKey, mozilla::UniqueCERTCertificate& cert);
protected:
virtual ~nsNSSCertificateDB();

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

@ -2158,8 +2158,7 @@ ClientAuthDataRunnable::RunOnTargetThread()
nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
if (certdb) {
nsCOMPtr<nsIX509Cert> foundCert;
rv = certdb->FindCertByDBKey(rememberedDBKey.get(),
getter_AddRefs(foundCert));
rv = certdb->FindCertByDBKey(rememberedDBKey, getter_AddRefs(foundCert));
if (NS_SUCCEEDED(rv) && foundCert) {
nsNSSCertificate* objCert =
BitwiseCast<nsNSSCertificate*, nsIX509Cert*>(foundCert.get());

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

@ -152,7 +152,7 @@ function constructCertFromFile(filename) {
let certdb = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
try {
return certdb.constructX509(certBytes, certBytes.length);
return certdb.constructX509(certBytes);
} catch (e) {}
// It might be PEM instead of DER.
return certdb.constructX509FromBase64(pemToBase64(certBytes));

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

@ -127,7 +127,7 @@ function loadCertificates(certFile, currentWhitelist) {
cert = gCertDB.constructX509FromBase64(certData);
} catch (e) {}
if (!cert) {
cert = gCertDB.constructX509(certData, certData.length);
cert = gCertDB.constructX509(certData);
}
// Don't add multiple copies of any particular certificate.
if (cert.sha256Fingerprint in certMap) {

18
servo/Cargo.lock сгенерированный
Просмотреть файл

@ -934,7 +934,7 @@ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.17.0",
"selectors 0.18.0",
"servo_url 0.0.1",
"style 0.0.1",
"style_traits 0.0.1",
@ -1355,7 +1355,7 @@ dependencies = [
"rayon 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"script_layout_interface 0.0.1",
"script_traits 0.0.1",
"selectors 0.17.0",
"selectors 0.18.0",
"serde 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1401,7 +1401,7 @@ dependencies = [
"script 0.0.1",
"script_layout_interface 0.0.1",
"script_traits 0.0.1",
"selectors 0.17.0",
"selectors 0.18.0",
"serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_config 0.0.1",
@ -2292,7 +2292,7 @@ dependencies = [
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"script_layout_interface 0.0.1",
"script_traits 0.0.1",
"selectors 0.17.0",
"selectors 0.18.0",
"serde 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_atoms 0.0.1",
@ -2336,7 +2336,7 @@ dependencies = [
"profile_traits 0.0.1",
"range 0.0.1",
"script_traits 0.0.1",
"selectors 0.17.0",
"selectors 0.18.0",
"servo_url 0.0.1",
"style 0.0.1",
]
@ -2386,7 +2386,7 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.17.0"
version = "0.18.0"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2766,7 +2766,7 @@ dependencies = [
"rayon 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.17.0",
"selectors 0.18.0",
"serde 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_atoms 0.0.1",
@ -2792,7 +2792,7 @@ dependencies = [
"parking_lot 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.17.0",
"selectors 0.18.0",
"servo_atoms 0.0.1",
"servo_config 0.0.1",
"servo_url 0.0.1",
@ -2829,7 +2829,7 @@ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.17.0",
"selectors 0.18.0",
"servo_url 0.0.1",
"style 0.0.1",
"style_traits 0.0.1",

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

@ -1072,15 +1072,18 @@ impl FragmentDisplayListBuilding for Fragment {
style: &ServoComputedValues,
bounds: &Rect<Au>,
clip: &ClippingRegion) {
use style::values::Either;
let width = style.get_outline().outline_width;
if width == Au(0) {
return
}
let outline_style = style.get_outline().outline_style;
if outline_style == border_style::T::none {
return
}
let outline_style = match style.get_outline().outline_style {
Either::First(_auto) => border_style::T::solid,
Either::Second(border_style::T::none) => return,
Either::Second(border_style) => border_style
};
// Outlines are not accounted for in the dimensions of the border box, so adjust the
// absolute bounds.

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

@ -1051,7 +1051,8 @@ impl Fragment {
// Note: We can not precompute the ratio and store it as a float, because
// doing so may result one pixel difference in calculation for certain
// images, thus make some tests fail.
inline_size * intrinsic_block_size.0 / intrinsic_inline_size.0
Au((inline_size.0 as i64 * intrinsic_block_size.0 as i64 /
intrinsic_inline_size.0 as i64) as i32)
} else {
intrinsic_block_size
};
@ -1060,7 +1061,8 @@ impl Fragment {
(MaybeAuto::Auto, MaybeAuto::Specified(block_size)) => {
let block_size = block_constraint.clamp(block_size);
let inline_size = if self.has_intrinsic_ratio() {
block_size * intrinsic_inline_size.0 / intrinsic_block_size.0
Au((block_size.0 as i64 * intrinsic_inline_size.0 as i64 /
intrinsic_block_size.0 as i64) as i32)
} else {
intrinsic_inline_size
};
@ -1075,10 +1077,11 @@ impl Fragment {
// First, create two rectangles that keep aspect ratio while may be clamped
// by the contraints;
let first_isize = inline_constraint.clamp(intrinsic_inline_size);
let first_bsize = first_isize * intrinsic_block_size.0 / intrinsic_inline_size.0;
let first_bsize = Au((first_isize.0 as i64 * intrinsic_block_size.0 as i64 /
intrinsic_inline_size.0 as i64) as i32);
let second_bsize = block_constraint.clamp(intrinsic_block_size);
let second_isize = second_bsize * intrinsic_inline_size.0 / intrinsic_block_size.0;
let second_isize = Au((second_bsize.0 as i64 * intrinsic_inline_size.0 as i64 /
intrinsic_block_size.0 as i64) as i32);
let (inline_size, block_size) = match (first_isize.cmp(&intrinsic_inline_size) ,
second_isize.cmp(&intrinsic_inline_size)) {
(Ordering::Equal, Ordering::Equal) =>

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

@ -716,14 +716,14 @@ pub fn process_resolved_style_request<'a, N>(context: &LayoutContext,
// has a mechanism to give us that within a defined scope (after which point
// it's cleared to maintained style system invariants).
let mut tlc = ThreadLocalStyleContext::new(&context.style_context);
let context = StyleContext {
let mut context = StyleContext {
shared: &context.style_context,
thread_local: &mut tlc,
};
let mut result = None;
let ensure = |el: N::ConcreteElement| el.as_node().initialize_data();
let clear = |el: N::ConcreteElement| el.as_node().clear_data();
resolve_style(&context, element, &ensure, &clear, |_: &_| {
resolve_style(&mut context, element, &ensure, &clear, |_: &_| {
let s = process_resolved_style_request_internal(node, pseudo, property, layout_root);
result = Some(s);
});

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

@ -76,6 +76,7 @@ use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::rpc::LayoutRPC;
use script_traits::{DocumentActivity, TimerEventId, TimerSource, TouchpadPressurePhase};
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use selectors::matching::ElementSelectorFlags;
use serde::{Deserialize, Serialize};
use servo_atoms::Atom;
use servo_url::ServoUrl;
@ -347,6 +348,7 @@ unsafe_no_jsmanaged_fields!(TimeProfilerChan);
unsafe_no_jsmanaged_fields!(MemProfilerChan);
unsafe_no_jsmanaged_fields!(PseudoElement);
unsafe_no_jsmanaged_fields!(Length);
unsafe_no_jsmanaged_fields!(ElementSelectorFlags);
unsafe_no_jsmanaged_fields!(ElementState);
unsafe_no_jsmanaged_fields!(DOMString);
unsafe_no_jsmanaged_fields!(Mime);

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

@ -83,7 +83,7 @@ use parking_lot::RwLock;
use ref_filter_map::ref_filter_map;
use script_layout_interface::message::ReflowQueryType;
use script_thread::Runnable;
use selectors::matching::{ElementFlags, MatchingReason, matches};
use selectors::matching::{ElementSelectorFlags, matches};
use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom;
@ -95,7 +95,6 @@ use std::default::Default;
use std::fmt;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::context::{QuirksMode, ReflowGoal};
use style::element_state::*;
@ -109,6 +108,7 @@ use style::rule_tree::CascadeLevel;
use style::selector_parser::{NonTSPseudoClass, RestyleDamage, SelectorImpl, SelectorParser};
use style::sink::Push;
use style::stylist::ApplicableDeclarationBlock;
use style::thread_state;
use style::values::CSSFloat;
use style::values::specified::{self, CSSColor, CSSRGBA};
use stylesheet_loader::StylesheetOwner;
@ -131,7 +131,12 @@ pub struct Element {
attr_list: MutNullableJS<NamedNodeMap>,
class_list: MutNullableJS<DOMTokenList>,
state: Cell<ElementState>,
atomic_flags: AtomicElementFlags,
/// These flags are set by the style system to indicate the that certain
/// operations may require restyling this element or its descendants. The
/// flags are not atomic, so the style system takes care of only set them
/// when it has exclusive access to the element.
#[ignore_heap_size_of = "bitflags defined in rust-selectors"]
selector_flags: Cell<ElementSelectorFlags>,
}
impl fmt::Debug for Element {
@ -219,7 +224,7 @@ impl Element {
attr_list: Default::default(),
class_list: Default::default(),
state: Cell::new(state),
atomic_flags: AtomicElementFlags::new(),
selector_flags: Cell::new(ElementSelectorFlags::empty()),
}
}
@ -351,7 +356,8 @@ pub trait LayoutElementHelpers {
fn get_checked_state_for_layout(&self) -> bool;
fn get_indeterminate_state_for_layout(&self) -> bool;
fn get_state_for_layout(&self) -> ElementState;
fn insert_atomic_flags(&self, flags: ElementFlags);
fn insert_selector_flags(&self, flags: ElementSelectorFlags);
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
}
impl LayoutElementHelpers for LayoutJS<Element> {
@ -401,7 +407,7 @@ impl LayoutElementHelpers for LayoutJS<Element> {
if let Some(color) = bgcolor {
hints.push(from_declaration(
PropertyDeclaration::BackgroundColor(DeclaredValue::Value(
CSSColor { parsed: Color::RGBA(color), authored: None }))));
Box::new(CSSColor { parsed: Color::RGBA(color), authored: None })))));
}
let background = if let Some(this) = self.downcast::<HTMLBodyElement>() {
@ -434,10 +440,10 @@ impl LayoutElementHelpers for LayoutJS<Element> {
if let Some(color) = color {
hints.push(from_declaration(
PropertyDeclaration::Color(DeclaredValue::Value(CSSRGBA {
PropertyDeclaration::Color(DeclaredValue::Value(Box::new(CSSRGBA {
parsed: color,
authored: None,
}))));
})))));
}
let font_family = if let Some(this) = self.downcast::<HTMLFontElement>() {
@ -474,10 +480,10 @@ impl LayoutElementHelpers for LayoutJS<Element> {
let width_value = specified::Length::from_px(cellspacing as f32);
hints.push(from_declaration(
PropertyDeclaration::BorderSpacing(DeclaredValue::Value(
border_spacing::SpecifiedValue {
Box::new(border_spacing::SpecifiedValue {
horizontal: width_value.clone(),
vertical: width_value,
}))));
})))));
}
@ -720,9 +726,19 @@ impl LayoutElementHelpers for LayoutJS<Element> {
#[inline]
#[allow(unsafe_code)]
fn insert_atomic_flags(&self, flags: ElementFlags) {
fn insert_selector_flags(&self, flags: ElementSelectorFlags) {
debug_assert!(thread_state::get() == thread_state::LAYOUT);
unsafe {
(*self.unsafe_get()).atomic_flags.insert(flags);
let f = &(*self.unsafe_get()).selector_flags;
f.set(f.get() | flags);
}
}
#[inline]
#[allow(unsafe_code)]
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
unsafe {
(*self.unsafe_get()).selector_flags.get().contains(flags)
}
}
}
@ -1973,7 +1989,7 @@ impl ElementMethods for Element {
match SelectorParser::parse_author_origin_no_namespace(&selectors) {
Err(()) => Err(Error::Syntax),
Ok(selectors) => {
Ok(matches(&selectors.0, &Root::from_ref(self), None, MatchingReason::Other))
Ok(matches(&selectors.0, &Root::from_ref(self), None))
}
}
}
@ -1991,7 +2007,8 @@ impl ElementMethods for Element {
let root = self.upcast::<Node>();
for element in root.inclusive_ancestors() {
if let Some(element) = Root::downcast::<Element>(element) {
if matches(&selectors.0, &element, None, MatchingReason::Other) {
if matches(&selectors.0, &element, None)
{
return Ok(Some(element));
}
}
@ -2231,7 +2248,7 @@ impl VirtualMethods for Element {
s.children_changed(mutation);
}
let flags = self.atomic_flags.get();
let flags = self.selector_flags.get();
if flags.intersects(HAS_SLOW_SELECTOR) {
// All children of this node need to be restyled when any child changes.
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
@ -2744,24 +2761,6 @@ impl<'a> AttributeMutation<'a> {
}
}
/// Thread-safe wrapper for ElementFlags set during selector matching
#[derive(JSTraceable, HeapSizeOf)]
struct AtomicElementFlags(AtomicUsize);
impl AtomicElementFlags {
fn new() -> Self {
AtomicElementFlags(AtomicUsize::new(0))
}
fn get(&self) -> ElementFlags {
ElementFlags::from_bits_truncate(self.0.load(Ordering::Relaxed) as u8)
}
fn insert(&self, flags: ElementFlags) {
self.0.fetch_or(flags.bits() as usize, Ordering::Relaxed);
}
}
/// A holder for an element's "tag name", which will be lazily
/// resolved and cached. Should be reset when the document
/// owner changes.

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

@ -67,7 +67,7 @@ use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddr
use script_layout_interface::message::Msg;
use script_traits::DocumentActivity;
use script_traits::UntrustedNodeAddress;
use selectors::matching::{MatchingReason, matches};
use selectors::matching::matches;
use selectors::parser::SelectorList;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
@ -322,7 +322,7 @@ impl<'a> Iterator for QuerySelectorIterator {
// (instead of passing `None`)? Probably.
self.iterator.by_ref().filter_map(|node| {
if let Some(element) = Root::downcast(node) {
if matches(selectors, &element, None, MatchingReason::Other) {
if matches(selectors, &element, None) {
return Some(Root::upcast(element));
}
}
@ -685,7 +685,7 @@ impl Node {
// Step 3.
Ok(selectors) => {
Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| {
matches(&selectors.0, element, None, MatchingReason::Other)
matches(&selectors.0, element, None)
}))
}
}

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

@ -11,8 +11,8 @@ use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::globalscope::GlobalScope;
use dom::storageevent::StorageEvent;
use dom::window::Window;
use ipc_channel::ipc::{self, IpcSender};
use net_traits::IpcSend;
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
@ -35,7 +35,7 @@ impl Storage {
}
}
pub fn new(global: &GlobalScope, storage_type: StorageType) -> Root<Storage> {
pub fn new(global: &Window, storage_type: StorageType) -> Root<Storage> {
reflect_dom_object(box Storage::new_inherited(storage_type), global, StorageBinding::Wrap)
}
@ -196,7 +196,7 @@ impl Runnable for StorageEventRunnable {
let window = global.as_window();
let storage_event = StorageEvent::new(
&global,
&window,
atom!("storage"),
EventBubbles::DoesNotBubble, EventCancelable::NotCancelable,
this.key.map(DOMString::from), this.old_value.map(DOMString::from), this.new_value.map(DOMString::from),

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

@ -11,7 +11,6 @@ use dom::bindings::js::{MutNullableJS, Root, RootedReference};
use dom::bindings::reflector::reflect_dom_object;
use dom::bindings::str::DOMString;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::globalscope::GlobalScope;
use dom::storage::Storage;
use dom::window::Window;
use servo_atoms::Atom;
@ -50,7 +49,7 @@ impl StorageEvent {
StorageEventBinding::Wrap)
}
pub fn new(global: &GlobalScope,
pub fn new(global: &Window,
type_: Atom,
bubbles: EventBubbles,
cancelable: EventCancelable,
@ -70,7 +69,7 @@ impl StorageEvent {
ev
}
pub fn Constructor(global: &GlobalScope,
pub fn Constructor(global: &Window,
type_: DOMString,
init: &StorageEventBinding::StorageEventInit) -> Fallible<Root<StorageEvent>> {
let key = init.key.clone();

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

@ -7,7 +7,7 @@
*
*/
[Exposed=(Window,Worker)]
[Exposed=Window]
interface Storage {
readonly attribute unsigned long length;

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

@ -9,7 +9,7 @@
* Event sent to a window when a storage area changes.
*/
[Constructor(DOMString type, optional StorageEventInit eventInitDict), Exposed=(Window,Worker)]
[Constructor(DOMString type, optional StorageEventInit eventInitDict), Exposed=Window]
interface StorageEvent : Event {
readonly attribute DOMString? key;
readonly attribute DOMString? oldValue;

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

@ -469,12 +469,12 @@ impl WindowMethods for Window {
// https://html.spec.whatwg.org/multipage/#dom-sessionstorage
fn SessionStorage(&self) -> Root<Storage> {
self.session_storage.or_init(|| Storage::new(self.upcast(), StorageType::Session))
self.session_storage.or_init(|| Storage::new(self, StorageType::Session))
}
// https://html.spec.whatwg.org/multipage/#dom-localstorage
fn LocalStorage(&self) -> Root<Storage> {
self.local_storage.or_init(|| Storage::new(self.upcast(), StorageType::Local))
self.local_storage.or_init(|| Storage::new(self, StorageType::Local))
}
// https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-GlobalCrypto

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

@ -49,7 +49,7 @@ use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, Truste
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData};
use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode};
use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use selectors::matching::ElementFlags;
use selectors::matching::ElementSelectorFlags;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom;
use servo_url::ServoUrl;
@ -437,6 +437,14 @@ impl<'le> TElement for ServoLayoutElement<'le> {
fn skip_root_and_item_based_display_fixup(&self) -> bool {
false
}
unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags) {
self.element.insert_selector_flags(flags);
}
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
self.element.has_selector_flags(flags)
}
}
impl<'le> PartialEq for ServoLayoutElement<'le> {
@ -665,10 +673,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
self.element.html_element_in_html_document_for_layout()
}
}
fn insert_flags(&self, flags: ElementFlags) {
self.element.insert_atomic_flags(flags);
}
}
#[derive(Copy, Clone, Debug)]
@ -1009,6 +1013,10 @@ impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> {
self.as_node().type_id()
}
unsafe fn unsafe_get(self) -> ServoLayoutElement<'le> {
self.element
}
fn get_attr<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> {
self.element.get_attr(namespace, name)
}

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

@ -313,6 +313,15 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
/// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
fn type_id(&self) -> Option<LayoutNodeType>;
/// Returns access to the underlying TElement. This is breaks the abstraction
/// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used
/// carefully.
///
/// We need this so that the functions defined on this trait can call
/// lazily_compute_pseudo_element_style, which operates on TElement.
unsafe fn unsafe_get(self) ->
<<Self::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteNode as TNode>::ConcreteElement;
#[inline]
fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str>;
@ -413,7 +422,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
let new_style =
context.stylist
.lazily_compute_pseudo_element_style(
self,
unsafe { &self.unsafe_get() },
&style_pseudo,
&data.styles().primary.values,
&context.default_computed_values);

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

@ -1,7 +1,7 @@
[package]
name = "selectors"
version = "0.17.0"
version = "0.18.0" # Not yet published
authors = ["Simon Sapin <simon.sapin@exyr.org>", "Alan Jeffrey <ajeffrey@mozilla.com>"]
documentation = "https://docs.rs/selectors/"

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

@ -7,27 +7,6 @@ use parser::{SimpleSelector, Selector, SelectorImpl};
use std::borrow::Borrow;
use tree::Element;
/// The reason why we're doing selector matching.
///
/// If this is for styling, this will include the flags in the parent element.
///
/// This is done because Servo doesn't need those flags at all when it's not
/// styling (e.g., when you're doing document.querySelector). For example, a
/// slow selector in an API like querySelector doesn't imply that the parent
/// could match it.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MatchingReason {
ForStyling,
Other,
}
impl MatchingReason {
#[inline]
fn for_styling(&self) -> bool {
*self == MatchingReason::ForStyling
}
}
// The bloom filter for descendant CSS selectors will have a <1% false
// positive rate until it has this many selectors in it, then it will
// rapidly increase.
@ -84,23 +63,20 @@ bitflags! {
}
bitflags! {
/// Set of flags that are set on the parent depending on whether a child
/// could potentially match a selector.
///
/// These setters, in the case of Servo, must be atomic, due to the parallel
/// traversal.
pub flags ElementFlags: u8 {
/// When a child is added or removed from this element, all the children
/// Set of flags that are set on either the element or its parent (depending
/// on the flag) if the element could potentially match a selector.
pub flags ElementSelectorFlags: u8 {
/// When a child is added or removed from the parent, all the children
/// must be restyled, because they may match :nth-last-child,
/// :last-of-type, :nth-last-of-type, or :only-of-type.
const HAS_SLOW_SELECTOR = 1 << 0,
/// When a child is added or removed from this element, any later
/// When a child is added or removed from the parent, any later
/// children must be restyled, because they may match :nth-child,
/// :first-of-type, or :nth-of-type.
const HAS_SLOW_SELECTOR_LATER_SIBLINGS = 1 << 1,
/// When a child is added or removed from this element, the first and
/// When a child is added or removed from the parent, the first and
/// last children must be restyled, because they may match :first-child,
/// :last-child, or :only-child.
const HAS_EDGE_CHILD_SELECTOR = 1 << 2,
@ -111,16 +87,28 @@ bitflags! {
}
}
impl ElementSelectorFlags {
/// Returns the subset of flags that apply to the element.
pub fn for_self(self) -> ElementSelectorFlags {
self & (HAS_EMPTY_SELECTOR)
}
/// Returns the subset of flags that apply to the parent.
pub fn for_parent(self) -> ElementSelectorFlags {
self & (HAS_SLOW_SELECTOR | HAS_SLOW_SELECTOR_LATER_SIBLINGS | HAS_EDGE_CHILD_SELECTOR)
}
}
pub fn matches<E>(selector_list: &[Selector<E::Impl>],
element: &E,
parent_bf: Option<&BloomFilter>,
reason: MatchingReason)
parent_bf: Option<&BloomFilter>)
-> bool
where E: Element
{
selector_list.iter().any(|selector| {
selector.pseudo_element.is_none() &&
matches_complex_selector(&*selector.complex_selector, element, parent_bf, &mut StyleRelations::empty(), reason)
matches_complex_selector(&*selector.complex_selector, element, parent_bf,
&mut StyleRelations::empty(), &mut ElementSelectorFlags::empty())
})
}
@ -134,11 +122,11 @@ pub fn matches_complex_selector<E>(selector: &ComplexSelector<E::Impl>,
element: &E,
parent_bf: Option<&BloomFilter>,
relations: &mut StyleRelations,
reason: MatchingReason)
flags: &mut ElementSelectorFlags)
-> bool
where E: Element
{
match matches_complex_selector_internal(selector, element, parent_bf, relations, reason) {
match matches_complex_selector_internal(selector, element, parent_bf, relations, flags) {
SelectorMatchingResult::Matched => {
match selector.next {
Some((_, Combinator::NextSibling)) |
@ -209,12 +197,12 @@ fn can_fast_reject<E>(mut selector: &ComplexSelector<E::Impl>,
element: &E,
parent_bf: Option<&BloomFilter>,
relations: &mut StyleRelations,
reason: MatchingReason)
flags: &mut ElementSelectorFlags)
-> Option<SelectorMatchingResult>
where E: Element
{
if !selector.compound_selector.iter().all(|simple_selector| {
matches_simple_selector(simple_selector, element, parent_bf, relations, reason) }) {
matches_simple_selector(simple_selector, element, parent_bf, relations, flags) }) {
return Some(SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling);
}
@ -271,11 +259,11 @@ fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>,
element: &E,
parent_bf: Option<&BloomFilter>,
relations: &mut StyleRelations,
reason: MatchingReason)
flags: &mut ElementSelectorFlags)
-> SelectorMatchingResult
where E: Element
{
if let Some(result) = can_fast_reject(selector, element, parent_bf, relations, reason) {
if let Some(result) = can_fast_reject(selector, element, parent_bf, relations, flags) {
return result;
}
@ -302,7 +290,7 @@ fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>,
&element,
parent_bf,
relations,
reason);
flags);
match (result, combinator) {
// Return the status immediately.
(SelectorMatchingResult::Matched, _) => return result,
@ -346,7 +334,7 @@ fn matches_simple_selector<E>(
element: &E,
parent_bf: Option<&BloomFilter>,
relations: &mut StyleRelations,
reason: MatchingReason)
flags: &mut ElementSelectorFlags)
-> bool
where E: Element
{
@ -429,14 +417,14 @@ fn matches_simple_selector<E>(
AFFECTED_BY_STATE)
}
SimpleSelector::FirstChild => {
relation_if!(matches_first_child(element, reason), AFFECTED_BY_CHILD_INDEX)
relation_if!(matches_first_child(element, flags), AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::LastChild => {
relation_if!(matches_last_child(element, reason), AFFECTED_BY_CHILD_INDEX)
relation_if!(matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::OnlyChild => {
relation_if!(matches_first_child(element, reason) &&
matches_last_child(element, reason), AFFECTED_BY_CHILD_INDEX)
relation_if!(matches_first_child(element, flags) &&
matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::Root => {
// We never share styles with an element with no parent, so no point
@ -444,43 +432,41 @@ fn matches_simple_selector<E>(
element.is_root()
}
SimpleSelector::Empty => {
if reason.for_styling() {
element.insert_flags(HAS_EMPTY_SELECTOR);
}
flags.insert(HAS_EMPTY_SELECTOR);
relation_if!(element.is_empty(), AFFECTED_BY_EMPTY)
}
SimpleSelector::NthChild(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, false, false, reason),
relation_if!(matches_generic_nth_child(element, a, b, false, false, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::NthLastChild(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, false, true, reason),
relation_if!(matches_generic_nth_child(element, a, b, false, true, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::NthOfType(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, true, false, reason),
relation_if!(matches_generic_nth_child(element, a, b, true, false, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::NthLastOfType(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, true, true, reason),
relation_if!(matches_generic_nth_child(element, a, b, true, true, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::FirstOfType => {
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, reason),
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::LastOfType => {
relation_if!(matches_generic_nth_child(element, 0, 1, true, true, reason),
relation_if!(matches_generic_nth_child(element, 0, 1, true, true, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::OnlyOfType => {
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, reason) &&
matches_generic_nth_child(element, 0, 1, true, true, reason),
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags) &&
matches_generic_nth_child(element, 0, 1, true, true, flags),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::Negation(ref negated) => {
!negated.iter().all(|s| {
matches_complex_selector(s, element, parent_bf, relations, reason)
matches_complex_selector(s, element, parent_bf, relations, flags)
})
}
}
@ -492,22 +478,15 @@ fn matches_generic_nth_child<E>(element: &E,
b: i32,
is_of_type: bool,
is_from_end: bool,
reason: MatchingReason) -> bool
flags: &mut ElementSelectorFlags)
-> bool
where E: Element
{
// Selectors Level 4 changed from Level 3:
// This can match without a parent element:
// https://drafts.csswg.org/selectors-4/#child-index
if reason.for_styling() {
if let Some(parent) = element.parent_element() {
parent.insert_flags(if is_from_end {
HAS_SLOW_SELECTOR
} else {
HAS_SLOW_SELECTOR_LATER_SIBLINGS
});
}
}
flags.insert(if is_from_end {
HAS_SLOW_SELECTOR
} else {
HAS_SLOW_SELECTOR_LATER_SIBLINGS
});
let mut index = 1;
let mut next_sibling = if is_from_end {
@ -546,28 +525,15 @@ fn matches_generic_nth_child<E>(element: &E,
}
#[inline]
fn matches_first_child<E>(element: &E, reason: MatchingReason) -> bool where E: Element {
// Selectors Level 4 changed from Level 3:
// This can match without a parent element:
// https://drafts.csswg.org/selectors-4/#child-index
if reason.for_styling() {
if let Some(parent) = element.parent_element() {
parent.insert_flags(HAS_EDGE_CHILD_SELECTOR);
}
}
fn matches_first_child<E>(element: &E, flags: &mut ElementSelectorFlags)
-> bool where E: Element {
flags.insert(HAS_EDGE_CHILD_SELECTOR);
element.prev_sibling_element().is_none()
}
#[inline]
fn matches_last_child<E>(element: &E, reason: MatchingReason) -> bool where E: Element {
// Selectors Level 4 changed from Level 3:
// This can match without a parent element:
// https://drafts.csswg.org/selectors-4/#child-index
if reason.for_styling() {
if let Some(parent) = element.parent_element() {
parent.insert_flags(HAS_EDGE_CHILD_SELECTOR);
}
}
fn matches_last_child<E>(element: &E, flags: &mut ElementSelectorFlags)
-> bool where E: Element {
flags.insert(HAS_EDGE_CHILD_SELECTOR);
element.next_sibling_element().is_none()
}

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

@ -5,7 +5,6 @@
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
//! style.
use matching::ElementFlags;
use parser::{AttrSelector, SelectorImpl};
use std::ascii::AsciiExt;
@ -162,16 +161,4 @@ pub trait Element: MatchAttr + Sized {
// in the future when we have associated types and/or a more convenient
// JS GC story... --pcwalton
fn each_class<F>(&self, callback: F) where F: FnMut(&<Self::Impl as SelectorImpl>::ClassName);
/// Add flags to the element. See the `ElementFlags` docs for details.
///
/// This may be called while the element *or one of its children* is being
/// matched. Therefore the implementation must be thread-safe if children
/// may be matched in parallel.
fn insert_flags(&self, _flags: ElementFlags) {}
/// Clears the relevant ElementFlags. This is *not* called from
/// rust-selectors, but provided as part of the Element interface since it
/// makes sense.
fn clear_flags(&self) {}
}

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

@ -66,7 +66,7 @@ impl<E: TElement> StyleBloom<E> {
/// Push an element to the bloom filter, knowing that it's a child of the
/// last element parent.
fn push(&mut self, element: E) {
pub fn push(&mut self, element: E) {
if cfg!(debug_assertions) {
if self.elements.is_empty() {
assert!(element.parent_element().is_none());
@ -86,12 +86,20 @@ impl<E: TElement> StyleBloom<E> {
popped
}
fn clear(&mut self) {
/// Returns true if the bloom filter is empty.
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
/// Clears the bloom filter.
pub fn clear(&mut self) {
self.filter.clear();
self.elements.clear();
}
fn rebuild(&mut self, mut element: E) -> usize {
/// Rebuilds the bloom filter up to the parent of the given element.
pub fn rebuild(&mut self, mut element: E) -> usize {
self.clear();
while let Some(parent) = element.parent_element() {

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

@ -505,6 +505,7 @@ mod bindings {
"RawServoDeclarationBlock",
"RawGeckoPresContext",
"RawGeckoPresContextOwned",
"RefPtr",
"ThreadSafeURIHolder",
"ThreadSafePrincipalHolder",
"CSSPseudoClassType",

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

@ -9,12 +9,14 @@ use animation::Animation;
use app_units::Au;
use bloom::StyleBloom;
use data::ElementData;
use dom::{OpaqueNode, TNode, TElement};
use dom::{OpaqueNode, TNode, TElement, SendElement};
use error_reporting::ParseErrorReporter;
use euclid::Size2D;
use matching::StyleSharingCandidateCache;
use parking_lot::RwLock;
use properties::ComputedValues;
use selectors::matching::ElementSelectorFlags;
use servo_config::opts;
use std::collections::HashMap;
use std::env;
use std::fmt;
@ -22,6 +24,7 @@ use std::ops::Add;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use stylist::Stylist;
use thread_state;
use timer::Timer;
/// This structure is used to create a local style context from a shared one.
@ -160,7 +163,35 @@ lazy_static! {
impl TraversalStatistics {
/// Returns whether statistics dumping is enabled.
pub fn should_dump() -> bool {
*DUMP_STYLE_STATISTICS
*DUMP_STYLE_STATISTICS || opts::get().style_sharing_stats
}
}
/// A task to be run in sequential mode on the parent (non-worker) thread. This
/// is used by the style system to queue up work which is not safe to do during
/// the parallel traversal.
pub enum SequentialTask<E: TElement> {
/// Sets selector flags. This is used when we need to set flags on an
/// element that we don't have exclusive access to (i.e. the parent).
SetSelectorFlags(SendElement<E>, ElementSelectorFlags),
}
impl<E: TElement> SequentialTask<E> {
/// Executes this task.
pub fn execute(self) {
use self::SequentialTask::*;
debug_assert!(thread_state::get() == thread_state::LAYOUT);
match self {
SetSelectorFlags(el, flags) => {
unsafe { el.set_selector_flags(flags) };
}
}
}
/// Creates a task to set the selector flags on an element.
pub fn set_selector_flags(el: E, flags: ElementSelectorFlags) -> Self {
use self::SequentialTask::*;
SetSelectorFlags(unsafe { SendElement::new(el) }, flags)
}
}
@ -177,6 +208,10 @@ pub struct ThreadLocalStyleContext<E: TElement> {
/// A channel on which new animations that have been triggered by style
/// recalculation can be sent.
pub new_animations_sender: Sender<Animation>,
/// A set of tasks to be run (on the parent thread) in sequential mode after
/// the rest of the styling is complete. This is useful for infrequently-needed
/// non-threadsafe operations.
pub tasks: Vec<SequentialTask<E>>,
/// Statistics about the traversal.
pub statistics: TraversalStatistics,
/// Information related to the current element, non-None during processing.
@ -190,6 +225,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
style_sharing_candidate_cache: StyleSharingCandidateCache::new(),
bloom_filter: StyleBloom::new(),
new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(),
tasks: Vec::new(),
statistics: TraversalStatistics::default(),
current_element_info: None,
}
@ -221,10 +257,15 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
}
}
#[cfg(debug_assertions)]
impl<E: TElement> Drop for ThreadLocalStyleContext<E> {
fn drop(&mut self) {
debug_assert!(self.current_element_info.is_none());
// Execute any enqueued sequential tasks.
debug_assert!(thread_state::get() == thread_state::LAYOUT);
for task in self.tasks.drain(..) {
task.execute();
}
}
}

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

@ -8,7 +8,7 @@
use Atom;
use cssparser::{Delimiter, Parser, SourcePosition, Token, TokenSerializationType};
use parser::{Parse, ParserContext};
use parser::ParserContext;
use properties::DeclaredValue;
use std::ascii::AsciiExt;
use std::borrow::Cow;
@ -129,16 +129,17 @@ impl ComputedValue {
}
}
impl Parse for SpecifiedValue {
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
impl SpecifiedValue {
/// Parse a custom property SpecifiedValue.
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Box<Self>, ()> {
let mut references = Some(HashSet::new());
let (first, css, last) = try!(parse_self_contained_declaration_value(input, &mut references));
Ok(SpecifiedValue {
Ok(Box::new(SpecifiedValue {
css: css.into_owned(),
first_token_type: first,
last_token_type: last,
references: references.unwrap(),
})
}))
}
}
@ -328,7 +329,7 @@ pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedSpec
inherited: &'a Option<Arc<HashMap<Name, ComputedValue>>>,
seen: &mut HashSet<&'a Name>,
name: &'a Name,
specified_value: &'a DeclaredValue<SpecifiedValue>) {
specified_value: &'a DeclaredValue<Box<SpecifiedValue>>) {
let was_already_present = !seen.insert(name);
if was_already_present {
return;
@ -360,7 +361,7 @@ pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedSpec
references: Some(&specified_value.references),
});
},
DeclaredValue::WithVariables { .. } => unreachable!(),
DeclaredValue::WithVariables(_) => unreachable!(),
DeclaredValue::Initial => {
map.remove(&name);
}

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

@ -14,6 +14,7 @@ use element_state::ElementState;
use parking_lot::RwLock;
use properties::{ComputedValues, PropertyDeclarationBlock};
use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement};
use selectors::matching::ElementSelectorFlags;
use sink::Push;
use std::fmt;
use std::fmt::Debug;
@ -321,6 +322,19 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
/// blockification on this element. (This function exists so that Gecko
/// native anonymous content can opt out of this style fixup.)
fn skip_root_and_item_based_display_fixup(&self) -> bool;
/// Sets selector flags, which indicate what kinds of selectors may have
/// matched on this element and therefore what kind of work may need to
/// be performed when DOM state changes.
///
/// This is unsafe, like all the flag-setting methods, because it's only safe
/// to call with exclusive access to the element. When setting flags on the
/// parent during parallel traversal, we use SequentialTask to queue up the
/// set to run after the threads join.
unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags);
/// Returns true if the element has all the specified selector flags.
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
}
/// TNode and TElement aren't Send because we want to be careful and explicit

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

@ -46,6 +46,7 @@ use properties::PropertyDeclarationBlock;
use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::{ElementExt, Snapshot};
use selectors::Element;
use selectors::matching::ElementSelectorFlags;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_url::ServoUrl;
use sink::Push;
@ -379,6 +380,29 @@ lazy_static! {
};
}
/// Converts flags from the layout used by rust-selectors to the layout used
/// by Gecko. We could align these and then do this without conditionals, but
/// it's probably not worth the trouble.
fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
use gecko_bindings::structs::*;
use selectors::matching::*;
let mut gecko_flags = 0u32;
if flags.contains(HAS_SLOW_SELECTOR) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR as u32;
}
if flags.contains(HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS as u32;
}
if flags.contains(HAS_EDGE_CHILD_SELECTOR) {
gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR as u32;
}
if flags.contains(HAS_EMPTY_SELECTOR) {
gecko_flags |= NODE_HAS_EMPTY_SELECTOR as u32;
}
gecko_flags
}
impl<'le> TElement for GeckoElement<'le> {
type ConcreteNode = GeckoNode<'le>;
@ -476,6 +500,16 @@ impl<'le> TElement for GeckoElement<'le> {
// are NAC handles both cases.
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
}
unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags) {
debug_assert!(!flags.is_empty());
self.set_flags(selector_flags_to_node_flags(flags));
}
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
let node_flags = selector_flags_to_node_flags(flags);
(self.flags() & node_flags) == node_flags
}
}
impl<'le> PartialEq for GeckoElement<'le> {

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

@ -156,6 +156,7 @@ use gecko_bindings::structs::Loader;
use gecko_bindings::structs::ServoStyleSheet;
use gecko_bindings::structs::EffectCompositor_CascadeLevel;
use gecko_bindings::structs::RawServoAnimationValueBorrowedListBorrowed;
use gecko_bindings::structs::RefPtr;
pub type nsTArrayBorrowed_uintptr_t<'a> = &'a mut ::gecko_bindings::structs::nsTArray<usize>;
pub type ServoComputedValuesStrong = ::gecko_bindings::sugar::ownership::Strong<ServoComputedValues>;
pub type ServoComputedValuesBorrowed<'a> = &'a ServoComputedValues;
@ -1301,6 +1302,15 @@ extern "C" {
RawServoAnimationValueBorrowedListBorrowed)
-> RawServoDeclarationBlockStrong;
}
extern "C" {
pub fn Servo_AnimationValues_GetOpacity(value:
RawServoAnimationValueBorrowed)
-> f32;
}
extern "C" {
pub fn Servo_AnimationValues_GetTransform(value: RawServoAnimationValueBorrowed,
list: &mut RefPtr<nsCSSValueSharedList>);
}
extern "C" {
pub fn Servo_ParseStyleAttribute(data: *const nsACString_internal)
-> RawServoDeclarationBlockStrong;

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

@ -5152,10 +5152,15 @@ pub mod root {
pub struct ContainerLayerParameters([u8; 0]);
#[repr(C)]
#[derive(Debug)]
pub struct AnimationValue {
pub mGecko: root::mozilla::StyleAnimationValue,
pub mServo: root::RefPtr<root::RawServoAnimationValue>,
}
#[repr(C)]
#[derive(Debug)]
pub struct PropertyStyleAnimationValuePair {
pub mProperty: root::nsCSSPropertyID,
pub mValue: root::mozilla::StyleAnimationValue,
pub mServoValue: root::RefPtr<root::RawServoAnimationValue>,
pub mValue: root::mozilla::AnimationValue,
}
#[test]
fn bindgen_test_layout_PropertyStyleAnimationValuePair() {

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

@ -5068,10 +5068,15 @@ pub mod root {
pub struct ContainerLayerParameters([u8; 0]);
#[repr(C)]
#[derive(Debug)]
pub struct AnimationValue {
pub mGecko: root::mozilla::StyleAnimationValue,
pub mServo: root::RefPtr<root::RawServoAnimationValue>,
}
#[repr(C)]
#[derive(Debug)]
pub struct PropertyStyleAnimationValuePair {
pub mProperty: root::nsCSSPropertyID,
pub mValue: root::mozilla::StyleAnimationValue,
pub mServoValue: root::RefPtr<root::RawServoAnimationValue>,
pub mValue: root::mozilla::AnimationValue,
}
#[test]
fn bindgen_test_layout_PropertyStyleAnimationValuePair() {

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

@ -12,7 +12,7 @@ use animation::{self, Animation, PropertyAnimation};
use atomic_refcell::AtomicRefMut;
use cache::LRUCache;
use cascade_info::CascadeInfo;
use context::{SharedStyleContext, StyleContext};
use context::{SequentialTask, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, ElementStyles, PseudoRuleNodes, PseudoStyles};
use dom::{SendElement, TElement, TNode};
use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
@ -22,7 +22,8 @@ use rule_tree::{CascadeLevel, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::MatchAttr;
use selectors::bloom::BloomFilter;
use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, AFFECTED_BY_STYLE_ATTRIBUTE, MatchingReason, StyleRelations};
use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, AFFECTED_BY_STYLE_ATTRIBUTE};
use selectors::matching::{ElementSelectorFlags, StyleRelations};
use servo_config::opts;
use sink::ForgetfulSink;
use std::collections::HashMap;
@ -577,8 +578,7 @@ impl<E: TElement> PrivateMatchMethods for E {}
pub trait MatchMethods : TElement {
/// Runs selector matching of this element, and returns the result.
fn match_element(&self,
context: &StyleContext<Self>,
parent_bf: Option<&BloomFilter>)
context: &mut StyleContext<Self>)
-> MatchResults
{
let mut applicable_declarations =
@ -587,16 +587,17 @@ pub trait MatchMethods : TElement {
let stylist = &context.shared.stylist;
let style_attribute = self.style_attribute();
let animation_rules = self.get_animation_rules(None);
let mut flags = ElementSelectorFlags::empty();
// Compute the primary rule node.
let mut primary_relations =
stylist.push_applicable_declarations(self,
parent_bf,
Some(context.thread_local.bloom_filter.filter()),
style_attribute,
animation_rules,
None,
&mut applicable_declarations,
MatchingReason::ForStyling);
&mut flags);
let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
// Compute the pseudo rule nodes.
@ -604,11 +605,12 @@ pub trait MatchMethods : TElement {
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
debug_assert!(applicable_declarations.is_empty());
let pseudo_animation_rules = self.get_animation_rules(Some(&pseudo));
stylist.push_applicable_declarations(self, parent_bf, None,
pseudo_animation_rules,
stylist.push_applicable_declarations(self,
Some(context.thread_local.bloom_filter.filter()),
None, pseudo_animation_rules,
Some(&pseudo),
&mut applicable_declarations,
MatchingReason::ForStyling);
&mut flags);
if !applicable_declarations.is_empty() {
let rule_node = compute_rule_node(context, &mut applicable_declarations);
@ -621,6 +623,22 @@ pub trait MatchMethods : TElement {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
// Apply the selector flags.
let self_flags = flags.for_self();
if !self_flags.is_empty() {
unsafe { self.set_selector_flags(self_flags); }
}
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = self.parent_element() {
// Avoid the overhead of the SequentialTask if the flags are already set.
if !p.has_selector_flags(parent_flags) {
let task = SequentialTask::set_selector_flags(p, parent_flags);
context.thread_local.tasks.push(task);
}
}
}
MatchResults {
primary: primary_rule_node,
relations: primary_relations,

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

@ -26,7 +26,6 @@ use context::TraversalStatistics;
use dom::{OpaqueNode, SendNode, TElement, TNode};
use rayon;
use scoped_tls::ScopedTLS;
use servo_config::opts;
use std::borrow::Borrow;
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
@ -75,7 +74,7 @@ pub fn traverse_dom<E, D>(traversal: &D,
});
// Dump statistics to stdout if requested.
if TraversalStatistics::should_dump() || opts::get().style_sharing_stats {
if TraversalStatistics::should_dump() {
let slots = unsafe { tls.unsafe_get() };
let aggregate = slots.iter().fold(TraversalStatistics::default(), |acc, t| {
match *t.borrow() {

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

@ -96,7 +96,7 @@ class Longhand(object):
predefined_type=None, custom_cascade=False, experimental=False, internal=False,
need_clone=False, need_index=False, gecko_ffi_name=None, depend_on_viewport_size=False,
allowed_in_keyframe_block=True, complex_color=False, cast_type='u8',
has_uncacheable_values=False, logical=False, alias=None, extra_prefixes=None):
has_uncacheable_values=False, logical=False, alias=None, extra_prefixes=None, boxed=False):
self.name = name
if not spec:
raise TypeError("Spec should be specified for %s" % name)
@ -119,6 +119,7 @@ class Longhand(object):
self.logical = arg_to_bool(logical)
self.alias = alias.split() if alias else []
self.extra_prefixes = extra_prefixes.split() if extra_prefixes else []
self.boxed = arg_to_bool(boxed)
# https://drafts.csswg.org/css-animations/#keyframes
# > The <declaration-list> inside of <keyframe-block> accepts any CSS property

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

@ -59,6 +59,8 @@ use std::ptr;
use std::sync::Arc;
use std::cmp;
use values::computed::ToComputedValue;
use values::{Either, Auto};
use computed_values::border_style;
pub mod style_structs {
% for style_struct in data.style_structs:
@ -166,8 +168,16 @@ impl ComputedValues {
PropertyDeclarationBlock {
declarations: vec![
(PropertyDeclaration::${prop.camel_case}(DeclaredValue::Value(
% if prop.boxed:
Box::new(
% endif
longhands::${prop.ident}::SpecifiedValue::from_computed_value(
&self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}()))),
&self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
% if prop.boxed:
)
% endif
)),
Importance::Normal)
],
important_count: 0
@ -913,7 +923,38 @@ fn static_assert() {
skip_longhands="${skip_outline_longhands}"
skip_additionals="*">
<% impl_keyword("outline_style", "mOutlineStyle", border_style_keyword, need_clone=True) %>
#[allow(non_snake_case)]
pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
// FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
let result = match v {
% for value in border_style_keyword.values_for('gecko'):
Either::Second(border_style::T::${to_rust_ident(value)}) =>
structs::${border_style_keyword.gecko_constant(value)} ${border_style_keyword.maybe_cast("u8")},
% endfor
Either::First(Auto) =>
structs::${border_style_keyword.gecko_constant('auto')} ${border_style_keyword.maybe_cast("u8")},
};
${set_gecko_property("mOutlineStyle", "result")}
}
#[allow(non_snake_case)]
pub fn copy_outline_style_from(&mut self, other: &Self) {
self.gecko.mOutlineStyle = other.gecko.mOutlineStyle;
}
#[allow(non_snake_case)]
pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
// FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
match ${get_gecko_property("mOutlineStyle")} ${border_style_keyword.maybe_cast("u32")} {
% for value in border_style_keyword.values_for('gecko'):
structs::${border_style_keyword.gecko_constant(value)} => Either::Second(border_style::T::${value}),
% endfor
structs::${border_style_keyword.gecko_constant('auto')} => Either::First(Auto),
% if border_style_keyword.gecko_inexhaustive:
x => panic!("Found unexpected value in style struct for outline_style property: {:?}", x),
% endif
}
}
<% impl_app_units("outline_width", "mActualOutlineWidth", need_clone=True,
round_to_pixels=True) %>
@ -1322,7 +1363,7 @@ fn static_assert() {
"number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)",
}
%>
ComputedOperation::${name.title()}(${pattern}) => {
longhands::transform::computed_value::ComputedOperation::${name.title()}(${pattern}) => {
bindings::Gecko_CSSValue_SetFunction(gecko_value, ${len(items) + 1});
bindings::Gecko_CSSValue_SetKeyword(
bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
@ -1336,27 +1377,20 @@ fn static_assert() {
% endfor
}
</%def>
pub fn set_transform(&mut self, other: longhands::transform::computed_value::T) {
pub fn convert_transform(input: Vec<longhands::transform::computed_value::ComputedOperation>,
output: &mut structs::root::RefPtr<structs::root::nsCSSValueSharedList>) {
use gecko_bindings::structs::nsCSSKeyword::*;
use gecko_bindings::sugar::refptr::RefPtr;
use properties::longhands::transform::computed_value::ComputedMatrix;
use properties::longhands::transform::computed_value::ComputedOperation;
let vec = if let Some(v) = other.0 {
v
} else {
unsafe {
self.gecko.mSpecifiedTransform.clear();
}
return;
};
unsafe { output.clear() };
let list = unsafe {
RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(vec.len() as u32))
RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(input.len() as u32))
};
let mut cur = list.mHead;
let mut iter = vec.into_iter();
let mut iter = input.into_iter();
while !cur.is_null() {
let gecko_value = unsafe { &mut (*cur).mValue };
let servo = iter.next().expect("Gecko_NewCSSValueSharedList should create a shared \
@ -1374,7 +1408,19 @@ fn static_assert() {
}
}
debug_assert!(iter.next().is_none());
unsafe { self.gecko.mSpecifiedTransform.set_move(list) };
unsafe { output.set_move(list) };
}
pub fn set_transform(&mut self, other: longhands::transform::computed_value::T) {
let vec = if let Some(v) = other.0 {
v
} else {
unsafe {
self.gecko.mSpecifiedTransform.clear();
}
return;
};
Self::convert_transform(vec, &mut self.gecko.mSpecifiedTransform);
}
pub fn copy_transform_from(&mut self, other: &Self) {
@ -2804,4 +2850,3 @@ pub unsafe extern "C" fn Servo_GetStyleVariables(_cv: ServoComputedValuesBorrowe
-> *const nsStyleVariables {
&*EMPTY_VARIABLES_STRUCT
}

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

@ -9,8 +9,13 @@
${caller.body()}
% if not data.longhands_by_name[name].derived_from:
pub fn parse_specified(context: &ParserContext, input: &mut Parser)
-> Result<DeclaredValue<SpecifiedValue>, ()> {
parse(context, input).map(DeclaredValue::Value)
% if data.longhands_by_name[name].boxed:
-> Result<DeclaredValue<Box<SpecifiedValue>>, ()> {
parse(context, input).map(|result| DeclaredValue::Value(Box::new(result)))
% else:
-> Result<DeclaredValue<SpecifiedValue>, ()> {
parse(context, input).map(DeclaredValue::Value)
% endif
}
% endif
</%call>
@ -191,7 +196,7 @@
% if not property.derived_from:
use cssparser::Parser;
use parser::{Parse, ParserContext, ParserContextExtraData};
use properties::{CSSWideKeyword, DeclaredValue, ShorthandId};
use properties::{CSSWideKeyword, DeclaredValue, UnparsedValue, ShorthandId};
% endif
use values::{Auto, Either, None_, Normal};
use cascade_info::CascadeInfo;
@ -254,7 +259,7 @@
.set_${property.ident}(computed ${maybe_wm});
% endif
}
DeclaredValue::WithVariables { .. } => unreachable!(),
DeclaredValue::WithVariables(_) => unreachable!(),
% if not data.current_style_struct.inherited:
DeclaredValue::Unset |
% endif
@ -298,7 +303,11 @@
}
% if not property.derived_from:
pub fn parse_declared(context: &ParserContext, input: &mut Parser)
-> Result<DeclaredValue<SpecifiedValue>, ()> {
% if property.boxed:
-> Result<DeclaredValue<Box<SpecifiedValue>>, ()> {
% else:
-> Result<DeclaredValue<SpecifiedValue>, ()> {
% endif
match input.try(|i| CSSWideKeyword::parse(context, i)) {
Ok(CSSWideKeyword::InheritKeyword) => Ok(DeclaredValue::Inherit),
Ok(CSSWideKeyword::InitialKeyword) => Ok(DeclaredValue::Initial),
@ -315,12 +324,12 @@
input.reset(start);
let (first_token_type, css) = try!(
::custom_properties::parse_non_custom_with_var(input));
return Ok(DeclaredValue::WithVariables {
return Ok(DeclaredValue::WithVariables(Box::new(UnparsedValue {
css: css.into_owned(),
first_token_type: first_token_type,
base_url: context.base_url.clone(),
from_shorthand: None,
})
})))
}
specified
}
@ -333,9 +342,9 @@
<%def name="single_keyword(name, values, vector=False, **kwargs)">
<%call expr="single_keyword_computed(name, values, vector, **kwargs)">
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
</%call>
</%def>
@ -412,7 +421,8 @@
#[allow(unused_imports)]
use cssparser::Parser;
use parser::ParserContext;
use properties::{longhands, PropertyDeclaration, DeclaredValue, ShorthandId};
use properties::{DeclaredValue, PropertyDeclaration, UnparsedValue};
use properties::{ShorthandId, longhands};
use properties::declaration_block::Importance;
use std::fmt;
use style_traits::ToCss;
@ -429,7 +439,13 @@
/// correspond to a shorthand.
pub struct LonghandsToSerialize<'a> {
% for sub_property in shorthand.sub_properties:
pub ${sub_property.ident}: &'a DeclaredValue<longhands::${sub_property.ident}::SpecifiedValue>,
% if sub_property.boxed:
pub ${sub_property.ident}:
&'a DeclaredValue<Box<longhands::${sub_property.ident}::SpecifiedValue>>,
% else:
pub ${sub_property.ident}:
&'a DeclaredValue<longhands::${sub_property.ident}::SpecifiedValue>,
% endif
% endfor
}
@ -489,7 +505,7 @@
DeclaredValue::Initial => all_flags &= ALL_INITIAL,
DeclaredValue::Inherit => all_flags &= ALL_INHERIT,
DeclaredValue::Unset => all_flags &= ALL_UNSET,
DeclaredValue::WithVariables {..} => with_variables = true,
DeclaredValue::WithVariables(_) => with_variables = true,
DeclaredValue::Value(..) => {
all_flags = SerializeFlags::empty();
}
@ -529,7 +545,11 @@
% for sub_property in shorthand.sub_properties:
declarations.push((PropertyDeclaration::${sub_property.camel_case}(
match value.${sub_property.ident} {
Some(value) => DeclaredValue::Value(value),
% if sub_property.boxed:
Some(value) => DeclaredValue::Value(Box::new(value)),
% else:
Some(value) => DeclaredValue::Value(value),
% endif
None => DeclaredValue::Initial,
}
), Importance::Normal));
@ -541,12 +561,12 @@
::custom_properties::parse_non_custom_with_var(input));
% for sub_property in shorthand.sub_properties:
declarations.push((PropertyDeclaration::${sub_property.camel_case}(
DeclaredValue::WithVariables {
DeclaredValue::WithVariables(Box::new(UnparsedValue {
css: css.clone().into_owned(),
first_token_type: first_token_type,
base_url: context.base_url.clone(),
from_shorthand: Some(ShorthandId::${shorthand.camel_case}),
}
}))
), Importance::Normal));
% endfor
Ok(())

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

@ -278,7 +278,11 @@ impl AnimationValue {
AnimationValue::${prop.camel_case}(ref from) => {
PropertyDeclaration::${prop.camel_case}(
DeclaredValue::Value(
longhands::${prop.ident}::SpecifiedValue::from_computed_value(from)))
% if prop.boxed:
Box::new(longhands::${prop.ident}::SpecifiedValue::from_computed_value(from))))
% else:
longhands::${prop.ident}::SpecifiedValue::from_computed_value(from)))
% endif
}
% endif
% endfor
@ -293,7 +297,7 @@ impl AnimationValue {
PropertyDeclaration::${prop.camel_case}(ref val) => {
let computed = match *val {
// https://bugzilla.mozilla.org/show_bug.cgi?id=1326131
DeclaredValue::WithVariables{..} => unimplemented!(),
DeclaredValue::WithVariables(_) => unimplemented!(),
DeclaredValue::Value(ref val) => val.to_computed_value(context),
% if not prop.style_struct.inherited:
DeclaredValue::Unset |

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

@ -9,15 +9,15 @@
${helpers.predefined_type("background-color", "CSSColor",
"::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */",
spec="https://drafts.csswg.org/css-backgrounds/#background-color",
animatable=True, complex_color=True)}
animatable=True, complex_color=True, boxed=True)}
<%helpers:vector_longhand name="background-image" animatable="False"
spec="https://drafts.csswg.org/css-backgrounds/#the-background-image"
has_uncacheable_values="${product == 'gecko'}">
use std::fmt;
use style_traits::ToCss;
use values::HasViewportPercentage;
use values::specified::Image;
use values::NoViewportPercentage;
pub mod computed_value {
use values::computed;
@ -35,7 +35,7 @@ ${helpers.predefined_type("background-color", "CSSColor",
}
}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]

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

@ -20,13 +20,13 @@
"::cssparser::Color::CurrentColor",
alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
spec=maybe_logical_spec(side, "color"),
animatable=True, logical = side[1])}
animatable=True, logical = side[1], boxed=True)}
% endfor
% for side in ALL_SIDES:
${helpers.predefined_type("border-%s-style" % side[0], "BorderStyle",
"specified::BorderStyle::none",
needs_context=False, need_clone=True,
need_clone=True,
alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
spec=maybe_logical_spec(side, "style"),
animatable=False, logical = side[1])}
@ -83,14 +83,14 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)",
animatable=False)}
<%helpers:longhand name="border-image-source" products="gecko" animatable="False"
<%helpers:longhand name="border-image-source" products="gecko" animatable="False" boxed="True"
spec="https://drafts.csswg.org/css-backgrounds/#border-image-source">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::specified::Image;
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
use values::computed;
@ -279,9 +279,9 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
pub use super::RepeatKeyword;
@ -557,10 +557,10 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::specified::{Number, Percentage};
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
use values::computed::Number;

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

@ -31,8 +31,8 @@
%>
use values::computed::ComputedValueAsSpecified;
use style_traits::ToCss;
use values::NoViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
use values::HasViewportPercentage;
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
pub use super::SpecifiedValue as T;
@ -113,8 +113,8 @@ ${helpers.single_keyword("-moz-top-layer", "none top",
}
}
use values::NoViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
use values::HasViewportPercentage;
no_viewport_percentage!(SpecifiedValue);
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
@ -152,8 +152,8 @@ ${helpers.single_keyword("-moz-top-layer", "none top",
gecko_inexhaustive="True"
gecko_ffi_name="mFloat"
spec="https://drafts.csswg.org/css-box/#propdef-float">
use values::NoViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
use values::HasViewportPercentage;
no_viewport_percentage!(SpecifiedValue);
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
@ -194,8 +194,8 @@ ${helpers.single_keyword("-moz-top-layer", "none top",
gecko_enum_prefix="StyleClear"
gecko_ffi_name="mBreakType"
spec="https://www.w3.org/TR/CSS2/visuren.html#flow-control">
use values::NoViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
use values::HasViewportPercentage;
no_viewport_percentage!(SpecifiedValue);
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
@ -396,9 +396,9 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
use std::fmt;
use style_traits::ToCss;
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@ -442,8 +442,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
use values::specified::Time;
pub use values::specified::Time as SpecifiedValue;
use values::NoViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
use values::HasViewportPercentage;
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
pub use values::computed::Time as T;
@ -703,8 +703,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
}
}
use values::NoViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
use values::HasViewportPercentage;
no_viewport_percentage!(SpecifiedValue);
#[inline]
pub fn get_initial_value() -> computed_value::T {
@ -745,8 +745,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
SpecifiedValue::parse(input)
}
use values::NoViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
use values::HasViewportPercentage;
no_viewport_percentage!(SpecifiedValue);
impl ComputedValueAsSpecified for SpecifiedValue { }
</%helpers:vector_longhand>
@ -773,7 +773,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
use std::ops::Deref;
use style_traits::ToCss;
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
pub mod computed_value {
pub use super::SpecifiedValue as T;
@ -805,7 +805,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
})
}
}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
SpecifiedValue::parse(context, input)
@ -847,7 +847,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
use std::fmt;
use style_traits::ToCss;
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
pub mod computed_value {
pub use super::SpecifiedValue as T;
@ -885,7 +885,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
}
}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[inline]
pub fn get_initial_value() -> computed_value::T {
@ -1755,7 +1755,7 @@ ${helpers.single_keyword("transform-style",
extra_prefixes="moz webkit",
animatable=False)}
<%helpers:longhand name="transform-origin" animatable="True" extra_prefixes="moz webkit"
<%helpers:longhand name="transform-origin" animatable="True" extra_prefixes="moz webkit" boxed="True"
spec="https://drafts.csswg.org/css-transforms/#transform-origin-property">
use app_units::Au;
use std::fmt;
@ -1896,7 +1896,8 @@ ${helpers.predefined_type("-moz-binding", "UrlOrNone", "Either::Second(None_)",
products="gecko",
animatable="False",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)",
disable_when_testing="True")}
disable_when_testing="True",
boxed=True)}
${helpers.single_keyword("-moz-orient",
"inline block horizontal vertical",

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

@ -6,7 +6,7 @@
<% data.new_style_struct("Color", inherited=True) %>
<%helpers:raw_longhand name="color" need_clone="True" animatable="True"
<%helpers:raw_longhand name="color" need_clone="True" animatable="True" boxed="True"
spec="https://drafts.csswg.org/css-color/#color">
use cssparser::Color as CSSParserColor;
use cssparser::RGBA;
@ -39,15 +39,15 @@
RGBA { red: 0., green: 0., blue: 0., alpha: 1. } /* black */
}
pub fn parse_specified(context: &ParserContext, input: &mut Parser)
-> Result<DeclaredValue<SpecifiedValue>, ()> {
-> Result<DeclaredValue<Box<SpecifiedValue>>, ()> {
let value = try!(CSSColor::parse(context, input));
let rgba = match value.parsed {
CSSParserColor::RGBA(rgba) => rgba,
CSSParserColor::CurrentColor => return Ok(DeclaredValue::Inherit)
};
Ok(DeclaredValue::Value(CSSRGBA {
Ok(DeclaredValue::Value(Box::new(CSSRGBA {
parsed: rgba,
authored: value.authored,
}))
})))
}
</%helpers:raw_longhand>

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

@ -22,9 +22,9 @@ ${helpers.predefined_type("column-width",
spec="https://drafts.csswg.org/css-multicol/#propdef-column-count">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -147,7 +147,7 @@ ${helpers.single_keyword("column-fill", "auto balance", extra_prefixes="moz",
${helpers.predefined_type("column-rule-color", "CSSColor",
"::cssparser::Color::CurrentColor",
products="gecko", animatable=True, extra_prefixes="moz",
complex_color=True, need_clone=True,
complex_color=True, need_clone=True, boxed=True,
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color")}
// It's not implemented in servo or gecko yet.

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

@ -10,7 +10,7 @@
use cssparser::Token;
use std::ascii::AsciiExt;
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use super::list_style_type;
@ -18,7 +18,7 @@
pub use self::computed_value::ContentItem;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
use super::super::list_style_type;
@ -179,7 +179,7 @@
use std::fmt;
use style_traits::ToCss;
use super::content;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
use cssparser::{Token, serialize_identifier};
@ -199,7 +199,7 @@
}
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {

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

@ -77,7 +77,7 @@ ${helpers.predefined_type("opacity",
</%helpers:vector_longhand>
// FIXME: This prop should be animatable
<%helpers:longhand name="clip" products="servo" animatable="False"
<%helpers:longhand name="clip" products="servo" animatable="False" boxed="True"
spec="https://drafts.fxtf.org/css-masking/#clip-property">
use std::fmt;
use style_traits::ToCss;

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

@ -11,12 +11,12 @@
<%helpers:longhand name="font-family" animatable="False" need_index="True"
spec="https://drafts.csswg.org/css-fonts/#propdef-font-family">
use self::computed_value::{FontFamily, FamilyName};
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
pub use self::computed_value::T as SpecifiedValue;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
use cssparser::CssStringWriter;
@ -223,9 +223,9 @@ ${helpers.single_keyword("font-variant-caps",
spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -458,12 +458,12 @@ ${helpers.single_keyword("font-variant-caps",
<%helpers:longhand products="gecko" name="font-size-adjust" animatable="True"
spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
use values::specified::Number;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -520,11 +520,11 @@ ${helpers.single_keyword("font-variant-caps",
spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
pub use super::SpecifiedValue as T;
@ -610,12 +610,12 @@ ${helpers.single_keyword("font-variant-position",
spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
pub use self::computed_value::T as SpecifiedValue;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
use cssparser::Parser;
@ -719,12 +719,12 @@ ${helpers.single_keyword("font-variant-position",
// https://www.w3.org/TR/css-fonts-3/#propdef-font-language-override
<%helpers:longhand name="font-language-override" products="none" animatable="False" extra_prefixes="moz"
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override">
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
pub use self::computed_value::T as SpecifiedValue;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
use std::fmt;

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

@ -35,8 +35,8 @@ ${helpers.single_keyword("direction", "ltr rtl", need_clone=True, animatable=Fal
animatable="False"
spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation"
>
use values::NoViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
use values::HasViewportPercentage;
no_viewport_percentage!(SpecifiedValue);
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
@ -91,8 +91,8 @@ ${helpers.single_keyword("image-rendering",
use style_traits::ToCss;
use values::specified::Angle;
use values::NoViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
use values::HasViewportPercentage;
no_viewport_percentage!(SpecifiedValue);
use std::f32::consts::PI;
use values::CSSFloat;
@ -225,9 +225,9 @@ ${helpers.single_keyword("image-rendering",
use std::fmt;
use style_traits::ToCss;
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]

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

@ -19,7 +19,7 @@ ${helpers.single_keyword("caption-side", "top bottom",
animatable=False,
spec="https://drafts.csswg.org/css-tables/#propdef-caption-side")}
<%helpers:longhand name="border-spacing" animatable="False"
<%helpers:longhand name="border-spacing" animatable="False" boxed="True"
spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing">
use app_units::Au;
use std::fmt;

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

@ -204,9 +204,9 @@ ${helpers.single_keyword("text-align-last",
<%helpers:longhand name="text-align" animatable="False" spec="https://drafts.csswg.org/css-text/#propdef-text-align">
pub use self::computed_value::T as SpecifiedValue;
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
use style_traits::ToCss;
macro_rules! define_text_align {
@ -429,16 +429,16 @@ ${helpers.single_keyword("text-align-last",
<%helpers:longhand name="-servo-text-decorations-in-effect"
derived_from="display text-decoration"
need_clone="True" products="servo"
animatable="False"
animatable="False" boxed="True"
spec="Nonstandard (Internal property used by Servo)">
use cssparser::RGBA;
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[derive(Clone, PartialEq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -518,9 +518,9 @@ ${helpers.single_keyword("text-align-last",
animatable="False"
spec="https://drafts.csswg.org/css-text/#propdef-white-space">
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
impl SpecifiedValue {
pub fn allow_wrap(&self) -> bool {
@ -771,9 +771,9 @@ ${helpers.single_keyword("text-align-last",
use std::fmt;
use style_traits::ToCss;
use unicode_segmentation::UnicodeSegmentation;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
#[derive(Debug, Clone, PartialEq)]
@ -977,7 +977,7 @@ ${helpers.single_keyword("text-align-last",
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
use std::fmt;
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use style_traits::ToCss;
define_css_keyword_enum!(HorizontalWritingModeValue:
@ -996,7 +996,7 @@ ${helpers.single_keyword("text-align-last",
}
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub fn get_initial_value() -> computed_value::T {
SpecifiedValue(HorizontalWritingModeValue::Over, VerticalWritingModeValue::Right)
@ -1027,6 +1027,7 @@ ${helpers.predefined_type("text-emphasis-color", "CSSColor",
"::cssparser::Color::CurrentColor",
products="gecko",animatable=True,
complex_color=True, need_clone=True,
boxed=True,
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color")}
// CSS Compatibility
@ -1035,14 +1036,14 @@ ${helpers.predefined_type(
"-webkit-text-fill-color", "CSSColor",
"CSSParserColor::CurrentColor",
products="gecko", animatable=True,
complex_color=True, need_clone=True,
complex_color=True, need_clone=True, boxed=True,
spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color")}
${helpers.predefined_type(
"-webkit-text-stroke-color", "CSSColor",
"CSSParserColor::CurrentColor",
products="gecko", animatable=True,
complex_color=True, need_clone=True,
complex_color=True, need_clone=True, boxed=True,
spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color")}
<%helpers:longhand products="gecko" name="-webkit-text-stroke-width" animatable="False"

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

@ -29,7 +29,7 @@ ${helpers.single_keyword("list-style-type", """
spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
${helpers.predefined_type("list-style-image", "UrlOrNone", "Either::Second(None_)",
animatable="False",
animatable=False,
spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image")}
<%helpers:longhand name="quotes" animatable="False"
@ -39,7 +39,7 @@ ${helpers.predefined_type("list-style-image", "UrlOrNone", "Either::Second(None_
use std::fmt;
use style_traits::ToCss;
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
pub use self::computed_value::T as SpecifiedValue;
@ -50,7 +50,7 @@ ${helpers.predefined_type("list-style-image", "UrlOrNone", "Either::Second(None_
}
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {

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

@ -11,22 +11,50 @@
// TODO(pcwalton): `invert`
${helpers.predefined_type("outline-color", "CSSColor", "::cssparser::Color::CurrentColor",
animatable=True, complex_color=True, need_clone=True,
animatable=True, complex_color=True, need_clone=True, boxed=True,
spec="https://drafts.csswg.org/css-ui/#propdef-outline-color")}
<%helpers:longhand name="outline-style" need_clone="True" animatable="False"
spec="https://drafts.csswg.org/css-ui/#propdef-outline-style">
pub use values::specified::BorderStyle as SpecifiedValue;
pub fn get_initial_value() -> SpecifiedValue { SpecifiedValue::none }
pub mod computed_value {
pub use values::specified::BorderStyle as T;
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
match SpecifiedValue::parse(input) {
Ok(SpecifiedValue::hidden) => Err(()),
result => result
use std::fmt;
use style_traits::ToCss;
use values::specified::BorderStyle;
use values::computed::ComputedValueAsSpecified;
pub type SpecifiedValue = Either<Auto, BorderStyle>;
impl SpecifiedValue {
#[inline]
pub fn none_or_hidden(&self) -> bool {
match *self {
Either::First(ref _auto) => false,
Either::Second(ref border_style) => border_style.none_or_hidden()
}
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
Either::Second(BorderStyle::none)
}
pub mod computed_value {
pub type T = super::SpecifiedValue;
}
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
SpecifiedValue::parse(context, input)
.and_then(|result| {
if let Either::Second(BorderStyle::hidden) = result {
// The outline-style property accepts the same values as border-style,
// except that 'hidden' is not a legal outline style.
Err(())
} else {
Ok(result)
}
})
}
</%helpers:longhand>
<%helpers:longhand name="outline-width" animatable="True"

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

@ -8,12 +8,12 @@
<%helpers:longhand name="cursor" animatable="False" spec="https://drafts.csswg.org/css-ui/#cursor">
pub use self::computed_value::T as SpecifiedValue;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
use values::specified::url::SpecifiedUrl;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
use std::fmt;

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

@ -24,11 +24,11 @@
% endfor
<%helpers:longhand name="z-index" spec="https://www.w3.org/TR/CSS2/visuren.html#z-index" animatable="True">
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
pub type SpecifiedValue = computed_value::T;
pub mod computed_value {
use std::fmt;
@ -218,5 +218,6 @@ ${helpers.single_keyword("object-fit", "fill contain cover none scale-down",
"Default::default()",
animatable=False,
spec="https://drafts.csswg.org/css-grid/#propdef-%s" % longhand,
products="gecko")}
products="gecko",
boxed=True)}
% endfor

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

@ -25,6 +25,7 @@ ${helpers.predefined_type(
"CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
products="gecko",
animatable=False,
boxed=True,
spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty")}
${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
@ -39,6 +40,7 @@ ${helpers.predefined_type(
"CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
products="gecko",
animatable=False,
boxed=True,
spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty")}
${helpers.predefined_type("flood-opacity", "Opacity",
@ -50,6 +52,7 @@ ${helpers.predefined_type(
"CSSParserColor::RGBA(RGBA { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 })",
products="gecko",
animatable=False,
boxed=True,
spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty")}
// CSS Masking Module Level 1
@ -58,11 +61,11 @@ ${helpers.single_keyword("mask-type", "luminance alpha",
products="gecko", animatable=False,
spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type")}
<%helpers:longhand name="clip-path" animatable="False" products="gecko"
<%helpers:longhand name="clip-path" animatable="False" products="gecko" boxed="True"
spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::specified::basic_shape::{ShapeSource, GeometryBox};
pub mod computed_value {
@ -83,7 +86,7 @@ ${helpers.single_keyword("mask-type", "luminance alpha",
ShapeSource::parse(context, input)
}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
</%helpers:longhand>
${helpers.single_keyword("mask-mode",
@ -196,7 +199,7 @@ ${helpers.single_keyword("mask-composite",
use std::sync::Arc;
use values::specified::Image;
use values::specified::url::SpecifiedUrl;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
pub mod computed_value {
use std::fmt;
@ -222,7 +225,7 @@ ${helpers.single_keyword("mask-composite",
}
}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]

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

@ -12,16 +12,16 @@
Method("has_overline", "bool"),
Method("has_line_through", "bool")]) %>
<%helpers:longhand name="text-overflow" animatable="False"
<%helpers:longhand name="text-overflow" animatable="False" boxed="True"
spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
use cssparser;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -108,11 +108,11 @@ ${helpers.single_keyword("unicode-bidi",
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line">
use std::fmt;
use style_traits::ToCss;
use values::NoViewportPercentage;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
impl NoViewportPercentage for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -214,4 +214,5 @@ ${helpers.predefined_type(
complex_color=True,
products="gecko",
animatable=True,
boxed=True,
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}

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

@ -246,23 +246,29 @@ mod property_bit_field {
/// the resulting declared value.
#[allow(non_snake_case)]
fn substitute_variables_${property.ident}<F>(
value: &DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
% if property.boxed:
value: &DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>,
% else:
value: &DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
% endif
custom_properties: &Option<Arc<::custom_properties::ComputedValuesMap>>,
f: F,
error_reporter: &mut StdBox<ParseErrorReporter + Send>)
where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
% if property.boxed:
where F: FnOnce(&DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>)
% else:
where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
% endif
{
if let DeclaredValue::WithVariables {
ref css, first_token_type, ref base_url, from_shorthand
} = *value {
if let DeclaredValue::WithVariables(ref with_variables) = *value {
// FIXME(heycam): A ParserContextExtraData should be built from data
// stored in the WithVariables, in case variable expansion results in
// a url() value.
let extra_data = ParserContextExtraData::default();
substitute_variables_${property.ident}_slow(css,
first_token_type,
base_url,
from_shorthand,
substitute_variables_${property.ident}_slow(&with_variables.css,
with_variables.first_token_type,
&with_variables.base_url,
with_variables.from_shorthand,
custom_properties,
f,
error_reporter,
@ -283,7 +289,11 @@ mod property_bit_field {
f: F,
error_reporter: &mut StdBox<ParseErrorReporter + Send>,
extra_data: ParserContextExtraData)
where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
% if property.boxed:
where F: FnOnce(&DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>)
% else:
where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
% endif
{
f(&
::custom_properties::substitute(css, first_token_type, custom_properties)
@ -305,7 +315,11 @@ mod property_bit_field {
Some(ShorthandId::${shorthand.camel_case}) => {
shorthands::${shorthand.ident}::parse_value(&context, input)
.map(|result| match result.${property.ident} {
Some(value) => DeclaredValue::Value(value),
% if property.boxed:
Some(value) => DeclaredValue::Value(Box::new(value)),
% else:
Some(value) => DeclaredValue::Value(value),
% endif
None => DeclaredValue::Initial,
})
}
@ -598,17 +612,8 @@ impl ShorthandId {
pub enum DeclaredValue<T> {
/// A known specified value from the stylesheet.
Value(T),
/// A value that contained any css variables.
WithVariables {
/// The css serialization for this value.
css: String,
/// The first token type for this serialization.
first_token_type: TokenSerializationType,
/// The base url.
base_url: ServoUrl,
/// The shorthand this came from.
from_shorthand: Option<ShorthandId>,
},
/// An unparsed value that contains `var()` functions.
WithVariables(Box<UnparsedValue>),
/// The `initial` keyword.
Initial,
/// The `inherit` keyword.
@ -617,11 +622,25 @@ pub enum DeclaredValue<T> {
Unset,
}
/// An unparsed property value that contains `var()` functions.
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct UnparsedValue {
/// The css serialization for this value.
css: String,
/// The first token type for this serialization.
first_token_type: TokenSerializationType,
/// The base url.
base_url: ServoUrl,
/// The shorthand this came from.
from_shorthand: Option<ShorthandId>,
}
impl<T: HasViewportPercentage> HasViewportPercentage for DeclaredValue<T> {
fn has_viewport_percentage(&self) -> bool {
match *self {
DeclaredValue::Value(ref v) => v.has_viewport_percentage(),
DeclaredValue::WithVariables { .. } => {
DeclaredValue::WithVariables(_) => {
panic!("DeclaredValue::has_viewport_percentage without \
resolving variables!")
},
@ -638,11 +657,13 @@ impl<T: ToCss> ToCss for DeclaredValue<T> {
{
match *self {
DeclaredValue::Value(ref inner) => inner.to_css(dest),
DeclaredValue::WithVariables { ref css, from_shorthand: None, .. } => {
dest.write_str(css)
}
// https://drafts.csswg.org/css-variables/#variables-in-shorthands
DeclaredValue::WithVariables { .. } => Ok(()),
DeclaredValue::WithVariables(ref with_variables) => {
// https://drafts.csswg.org/css-variables/#variables-in-shorthands
if with_variables.from_shorthand.is_none() {
dest.write_str(&*with_variables.css)?
}
Ok(())
},
DeclaredValue::Initial => dest.write_str("initial"),
DeclaredValue::Inherit => dest.write_str("inherit"),
DeclaredValue::Unset => dest.write_str("unset"),
@ -826,11 +847,15 @@ impl PropertyId {
pub enum PropertyDeclaration {
% for property in data.longhands:
/// ${property.name}
${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
% if property.boxed:
${property.camel_case}(DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>),
% else:
${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
% endif
% endfor
/// A custom property declaration, with the property name and the declared
/// value.
Custom(::custom_properties::Name, DeclaredValue<::custom_properties::SpecifiedValue>),
Custom(::custom_properties::Name, DeclaredValue<Box<::custom_properties::SpecifiedValue>>),
}
impl HasViewportPercentage for PropertyDeclaration {
@ -939,9 +964,12 @@ impl PropertyDeclaration {
match *self {
% for property in data.longhands:
PropertyDeclaration::${property.camel_case}(ref value) => match *value {
DeclaredValue::WithVariables { ref css, from_shorthand: Some(s), .. }
if s == shorthand => {
Some(&**css)
DeclaredValue::WithVariables(ref with_variables) => {
if let Some(s) = with_variables.from_shorthand {
if s == shorthand {
Some(&*with_variables.css)
} else { None }
} else { None }
}
_ => None
},
@ -956,12 +984,12 @@ impl PropertyDeclaration {
match *self {
% for property in data.longhands:
PropertyDeclaration::${property.camel_case}(ref value) => match *value {
DeclaredValue::WithVariables { .. } => true,
DeclaredValue::WithVariables(_) => true,
_ => false,
},
% endfor
PropertyDeclaration::Custom(_, ref value) => match *value {
DeclaredValue::WithVariables { .. } => true,
DeclaredValue::WithVariables(_) => true,
_ => false,
}
}
@ -977,7 +1005,7 @@ impl PropertyDeclaration {
match *self {
% for property in data.longhands:
PropertyDeclaration::${property.camel_case}(ref value) => {
matches!(*value, DeclaredValue::WithVariables { .. })
matches!(*value, DeclaredValue::WithVariables(_))
},
% endfor
PropertyDeclaration::Custom(..) => true
@ -1009,7 +1037,8 @@ impl PropertyDeclaration {
Err(()) => return PropertyDeclarationParseResult::InvalidValue,
}
};
result_list.push((PropertyDeclaration::Custom(name, value), Importance::Normal));
result_list.push((PropertyDeclaration::Custom(name, value),
Importance::Normal));
return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration;
}
PropertyId::Longhand(id) => match id {
@ -2300,3 +2329,18 @@ macro_rules! longhand_properties_idents {
}
}
}
/// Retuns all longhands SpecifiedValue sizes. This is used in unit tests.
#[cfg(feature = "testing")]
pub fn specified_value_sizes() -> Vec<(&'static str, usize, bool)> {
use std::mem::size_of;
let mut sizes = vec![];
% for property in data.longhands:
sizes.push(("${property.name}",
size_of::<longhands::${property.ident}::SpecifiedValue>(),
${"true" if property.boxed else "false"}));
% endfor
sizes
}

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

@ -10,7 +10,6 @@ ${helpers.four_sides_shorthand("border-color", "border-%s-color", "specified::CS
${helpers.four_sides_shorthand("border-style", "border-%s-style",
"specified::BorderStyle::parse",
needs_context=False,
spec="https://drafts.csswg.org/css-backgrounds/#border-style")}
<%helpers:shorthand name="border-width" sub_properties="${
@ -61,7 +60,7 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser)
}
}
if style.is_none() {
if let Ok(value) = input.try(specified::BorderStyle::parse) {
if let Ok(value) = input.try(|i| specified::BorderStyle::parse(context, i)) {
style = Some(value);
any = true;
continue

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

@ -22,7 +22,7 @@
(&DeclaredValue::Value(ref x_value), &DeclaredValue::Value(ref y_container)) => {
*x_value == y_container.0
},
(&DeclaredValue::WithVariables { .. }, &DeclaredValue::WithVariables { .. }) => true,
(&DeclaredValue::WithVariables(_), &DeclaredValue::WithVariables(_)) => true,
(&DeclaredValue::Initial, &DeclaredValue::Initial) => true,
(&DeclaredValue::Inherit, &DeclaredValue::Inherit) => true,
(&DeclaredValue::Unset, &DeclaredValue::Unset) => true,
@ -43,8 +43,8 @@
(&DeclaredValue::Value(ref x_value), &DeclaredValue::Value(ref y_container)) => {
*x_value == y_container.0
},
(_, &DeclaredValue::WithVariables { .. }) |
(&DeclaredValue::WithVariables { .. }, _) => {
(_, &DeclaredValue::WithVariables(_)) |
(&DeclaredValue::WithVariables(_), _) => {
// We don't serialize shorthands with variables
return dest.write_str("");
},

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

@ -6,7 +6,7 @@
<%helpers:shorthand name="outline" sub_properties="outline-color outline-style outline-width"
spec="https://drafts.csswg.org/css-ui/#propdef-outline">
use properties::longhands::outline_width;
use properties::longhands::{outline_width, outline_style};
use values::specified;
use parser::Parse;
@ -25,7 +25,7 @@
}
}
if style.is_none() {
if let Ok(value) = input.try(specified::BorderStyle::parse) {
if let Ok(value) = input.try(|input| outline_style::parse(context, input)) {
style = Some(value);
any = true;
continue

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

@ -58,10 +58,10 @@ pub fn serialize_four_sides<W, I>(dest: &mut W,
Ok(())
}
fn serialize_directional_border<W, I>(dest: &mut W,
fn serialize_directional_border<W, I,>(dest: &mut W,
width: &DeclaredValue<I>,
style: &DeclaredValue<BorderStyle>,
color: &DeclaredValue<CSSColor>)
color: &DeclaredValue<Box<CSSColor>>)
-> fmt::Result where W: fmt::Write, I: ToCss {
match *width {
DeclaredValue::Value(ref width) => {

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