зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to central, a=merge
MozReview-Commit-ID: Cu9iXbDAS7t
This commit is contained in:
Коммит
e27ab18a24
18
.cron.yml
18
.cron.yml
|
@ -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) {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче