Merge m-c to inbound, a=merge

MozReview-Commit-ID: Cb0b59wJ0vy
This commit is contained in:
Wes Kocher 2017-04-21 17:35:24 -07:00
commit 4a05296446
168 changed files with 2202 additions and 1109 deletions

View File

@ -18,6 +18,8 @@ module.exports = {
// No (!foo in bar) or (!object instanceof Class)
"no-unsafe-negation": "error",
// No eval() and no strings in the first param of setTimeout or setInterval
"no-implied-eval": "error",
},
"env": {
"es6": true

View File

@ -522,6 +522,10 @@ toolbar:not(#TabsToolbar) > #personal-bookmarks {
display: none;
}
#PopupAutoComplete[firstresultstyle="insecureWarning"] {
min-width: 200px;
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] {
-moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-richlistitem-insecure-field");
height: auto;

View File

@ -1071,17 +1071,6 @@
<deck id="content-deck" flex="1">
<hbox flex="1" id="browser">
<vbox id="browser-border-start" hidden="true" layer="true"/>
<vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
<sidebarheader id="sidebar-header" align="center">
<label id="sidebar-title" persist="value" flex="1" crop="end" control="sidebar"/>
<image id="sidebar-throbber"/>
<toolbarbutton class="close-icon tabbable" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="SidebarUI.hide();"/>
</sidebarheader>
<browser id="sidebar" flex="1" autoscroll="false" disablehistory="true" disablefullscreen="true"
style="min-width: 14em; width: 18em; max-width: 36em;" tooltip="aHTMLTooltip"/>
</vbox>
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
<vbox id="appcontent" flex="1">
<notificationbox id="high-priority-global-notificationbox" notificationside="top"/>
<tabbrowser id="content"
@ -1092,6 +1081,16 @@
selectmenulist="ContentSelectDropdown"
datetimepicker="DateTimePickerPanel"/>
</vbox>
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
<vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
<sidebarheader id="sidebar-header" align="center">
<label id="sidebar-title" persist="value" flex="1" crop="end" control="sidebar"/>
<image id="sidebar-throbber"/>
<toolbarbutton class="close-icon tabbable" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="SidebarUI.hide();"/>
</sidebarheader>
<browser id="sidebar" flex="1" autoscroll="false" disablehistory="true" disablefullscreen="true"
style="min-width: 14em; width: 18em; max-width: 36em;" tooltip="aHTMLTooltip"/>
</vbox>
<vbox id="browser-border-end" hidden="true" layer="true"/>
</hbox>
#include ../../components/customizableui/content/customizeMode.inc.xul

View File

@ -145,25 +145,27 @@ add_task(function* () {
let sidebar = document.getElementById("sidebar");
let loadPromise = BrowserTestUtils.waitForEvent(sidebar, "load", true);
let focusPromise = BrowserTestUtils.waitForEvent(sidebar, "focus", true);
SidebarUI.toggle("viewBookmarksSidebar");
yield loadPromise;
yield focusPromise;
gURLBar.focus();
yield* expectFocusOnF6(false, "html1", "html1",
true, "focus with sidebar open content");
yield* expectFocusOnF6(false, "bookmarksPanel",
sidebar.contentDocument.getElementById("search-box").inputField,
false, "focus with sidebar open sidebar");
yield* expectFocusOnF6(false, "html1", "html1",
true, "focus with sidebar open content");
yield* expectFocusOnF6(false, "main-window", gURLBar.inputField,
false, "focus with sidebar urlbar");
// Now go backwards
yield* expectFocusOnF6(true, "html1", "html1",
true, "back focus with sidebar open content");
yield* expectFocusOnF6(true, "bookmarksPanel",
sidebar.contentDocument.getElementById("search-box").inputField,
false, "back focus with sidebar open sidebar");
yield* expectFocusOnF6(true, "html1", "html1",
true, "back focus with sidebar open content");
yield* expectFocusOnF6(true, "main-window", gURLBar.inputField,
false, "back focus with sidebar urlbar");

View File

@ -159,11 +159,6 @@ var whitelist = new Set([
platforms: ["linux"]},
{file: "chrome://global/skin/arrow/panelarrow-vertical.svg",
platforms: ["linux"]},
// Bug 1348359
{file: "chrome://global/skin/dirListing/folder.png", platforms: ["linux"]},
{file: "chrome://global/skin/dirListing/local.png", platforms: ["linux", "win"]},
{file: "chrome://global/skin/dirListing/remote.png"},
{file: "chrome://global/skin/dirListing/up.png", platforms: ["linux"]},
// Bug 1348362
{file: "chrome://global/skin/icons/Close.gif", platforms: ["win"]},
{file: "chrome://global/skin/icons/Error.png", platforms: ["linux", "macosx"]},

View File

@ -124,6 +124,12 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
this.inputField.removeEventListener("mouseout", this);
this.inputField.removeEventListener("overflow", this);
this.inputField.removeEventListener("underflow", this);
// Null out the one-offs' popup and textbox so that it cleans up its
// internal state for both. Most importantly, it removes the event
// listeners that it added to both.
this.popup.oneOffSearchButtons.popup = null;
this.popup.oneOffSearchButtons.textbox = null;
]]></destructor>
<field name="_value">""</field>

View File

@ -100,6 +100,11 @@ add_task(function* () {
yield expectEvent("on-input-started-fired");
EventUtils.synthesizeKey("t", {});
yield expectEvent("on-input-changed-fired", {text: "t"});
// Wait for the autocomplete search. Note that we cannot wait for the search
// to be complete, since the add-on doesn't communicate when it's done, so
// just check matches count.
yield BrowserTestUtils.waitForCondition(() => gURLBar.controller.matchCount >= 2,
"waiting urlbar search to complete");
return "t";
}
@ -267,7 +272,6 @@ add_task(function* () {
});
// Start monitoring the console.
SimpleTest.waitForExplicitFinish();
let waitForConsole = new Promise(resolve => {
SimpleTest.monitorConsole(resolve, [{
message: new RegExp(`The keyword provided is already registered: "${keyword}"`),

View File

@ -19,7 +19,9 @@ var gLanguagesDialog = {
// see bug 1194346.
forceReflow() {
this._activeLanguages.style.fontKerning = "none";
setTimeout("gLanguagesDialog._activeLanguages.style.removeProperty('font-kerning')", 0);
setTimeout(() => {
this._activeLanguages.style.removeProperty("font-kerning")
}, 0);
},
get _activeLanguages() {

View File

@ -1243,10 +1243,6 @@
return this._popup;
]]></getter>
<setter><![CDATA[
if (this._popup == val) {
return val;
}
let events = [
"popupshowing",
"popuphidden",
@ -1285,9 +1281,6 @@
return this._textbox;
]]></getter>
<setter><![CDATA[
if (this._textbox == val) {
return val;
}
if (this._textbox) {
this._textbox.removeEventListener("input", this);
}

View File

@ -594,7 +594,7 @@ Experiments.Experiments.prototype = {
}),
_telemetryStatusChanged() {
this._toggleExperimentsEnabled(gExperimentsEnabled);
this._toggleExperimentsEnabled(gPrefs.get(PREF_ENABLED, false));
},
/**

View File

@ -16,30 +16,19 @@ XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
"resource://services-common/utils.js");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryUtils",
"resource://gre/modules/TelemetryUtils.jsm");
const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
const PREF_ACTIVE_EXPERIMENT = "experiments.activeExperiment"; // whether we have an active experiment
const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
const PREF_TELEMETRY_UNIFIED = "toolkit.telemetry.unified";
const DELAY_INIT_MS = 30 * 1000;
// Whether the FHR/Telemetry unification features are enabled.
// Changing this pref requires a restart.
const IS_UNIFIED_TELEMETRY = Preferences.get(PREF_TELEMETRY_UNIFIED, false);
XPCOMUtils.defineLazyGetter(
this, "gPrefs", () => {
return new Preferences();
});
XPCOMUtils.defineLazyGetter(
this, "gExperimentsEnabled", () => {
// We can enable experiments if either unified Telemetry or FHR is on, and the user
// has opted into Telemetry.
return gPrefs.get(PREF_EXPERIMENTS_ENABLED, false) &&
IS_UNIFIED_TELEMETRY && gPrefs.get(PREF_TELEMETRY_ENABLED, false);
});
XPCOMUtils.defineLazyGetter(
this, "gActiveExperiment", () => {
return gPrefs.get(PREF_ACTIVE_EXPERIMENT);
@ -54,8 +43,15 @@ ExperimentsService.prototype = {
classID: Components.ID("{f7800463-3b97-47f9-9341-b7617e6d8d49}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback, Ci.nsIObserver]),
get _experimentsEnabled() {
// We can enable experiments if either unified Telemetry or FHR is on, and the user
// has opted into Telemetry.
return gPrefs.get(PREF_EXPERIMENTS_ENABLED, false) &&
TelemetryUtils.isTelemetryEnabled;
},
notify(timer) {
if (!gExperimentsEnabled) {
if (!this._experimentsEnabled) {
return;
}
if (OS.Constants.Path.profileDir === undefined) {
@ -63,7 +59,11 @@ ExperimentsService.prototype = {
}
let instance = Experiments.instance();
if (instance.isReady) {
instance.updateManifest();
instance.updateManifest().catch(error => {
// Don't throw, as this breaks tests. In any case the best we can do here
// is to log the failure.
Cu.reportError(error);
});
}
},
@ -77,7 +77,7 @@ ExperimentsService.prototype = {
observe(subject, topic, data) {
switch (topic) {
case "profile-after-change":
if (gExperimentsEnabled) {
if (this._experimentsEnabled) {
Services.obs.addObserver(this, "quit-application");
Services.obs.addObserver(this, "sessionstore-state-finalized");
Services.obs.addObserver(this, "EM-loaded");

View File

@ -13,9 +13,16 @@ add_test(function test_experiments_activation() {
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, false);
let experiments = Experiments.instance();
Assert.ok(!experiments.enabled, "Experiments must be disabled if Telemetry is disabled.");
// TODO: Test that Experiments are turned back on when bug 1232648 lands.
// Patch updateManifest to not do anything when the pref is switched back to true,
// otherwise it attempts to connect to the server.
experiments.updateManifest = () => Promise.resolve();
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
Assert.ok(experiments.enabled, "Experiments must be re-enabled if Telemetry is re-enabled");
run_next_test();
});

View File

@ -1,12 +1,8 @@
"use strict";
module.exports = {
"extends": "../../.eslintrc.js",
"globals": {
"addMessageListener": false,
"Components": true,
"dump": true,
"removeMessageListener": false,
"sendAsyncMessage": false,
"TextDecoder": false,
@ -34,10 +30,7 @@ module.exports = {
"requireReturn": false,
"requireReturnDescription": false,
}],
// Braces only needed for multi-line arrow function blocks
// "arrow-body-style": ["error", "as-needed"],
// Forbid spaces inside the square brackets of array literals.
"array-bracket-spacing": ["error", "never"],
@ -46,11 +39,7 @@ module.exports = {
// No space padding in parentheses
"space-in-parens": ["error", "never"],
// Enforce one true brace style (opening brace on the same line) and avoid
// start and end braces on the same line.
"brace-style": ["error", "1tbs", {"allowSingleLine": true}],
// Commas at the end of the line not the start
"comma-style": "error",
@ -63,18 +52,12 @@ module.exports = {
// Two space indent
"indent": ["error", 2, {"SwitchCase": 1}],
// Space after colon not before in property declarations
"key-spacing": ["error", {"beforeColon": false, "afterColon": true, "mode": "minimum"}],
// Always require parenthesis for new calls
"new-parens": "error",
// Use [] instead of Array()
"no-array-constructor": "error",
// If an if block ends with a return no need for an else block
// "no-else-return": "error",
// Disallow empty statements. This will report an error for:
// try { something(); } catch (e) {}
// but will not report it for:
@ -97,9 +80,6 @@ module.exports = {
// Always require semicolon at end of statement
"semi": ["error", "always"],
// Require spaces around operators, except for a|"off".
"space-infix-ops": ["error", {"int32Hint": true}],
// Disallow using variables outside the blocks they are defined (especially
// since only let and const are used, see "no-var").
"block-scoped-var": "error",
@ -111,10 +91,6 @@ module.exports = {
// Warn about cyclomatic complexity in functions.
"complexity": ["error", {"max": 20}],
// Don't warn for inconsistent naming when capturing this (not so important
// with auto-binding fat arrow functions).
// "consistent-this": ["error", "self"],
// Enforce dots on the next line with property name.
"dot-location": ["error", "property"],

View File

@ -239,6 +239,7 @@ inline bool isIgnoredPathForSprintfLiteral(const CallExpr *Call, const SourceMan
Begin->compare_lower(StringRef("google-breakpad")) == 0 ||
Begin->compare_lower(StringRef("gflags")) == 0 ||
Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
Begin->compare_lower(StringRef("jsoncpp")) == 0 ||
Begin->compare_lower(StringRef("libstagefright")) == 0 ||
Begin->compare_lower(StringRef("mtransport")) == 0 ||
Begin->compare_lower(StringRef("protobuf")) == 0 ||

View File

@ -50,7 +50,7 @@ def rust_compiler(rustc_info, cargo_info):
die(dedent('''\
Rust compiler not found.
To compile rust language sources, you must have 'rustc' in your path.
See https//www.rust-lang.org/ for more information.
See https://www.rust-lang.org/ for more information.
You can install rust by running './mach bootstrap'
or by directly running the installer from https://rustup.rs/

View File

@ -233,6 +233,8 @@ module.exports = {
"no-fallthrough": "error",
// Allow the use of leading or trailing decimal points in numeric literals.
"no-floating-decimal": "off",
// disallow use of eval()-like methods
"no-implied-eval": "error",
// Allow comments inline after code.
"no-inline-comments": "off",
// Disallow if as the only statement in an else block.
@ -427,8 +429,6 @@ module.exports = {
"no-eq-null": "off",
// disallow overwriting functions written as function declarations
"no-func-assign": "off",
// disallow use of eval()-like methods
"no-implied-eval": "off",
// disallow function or variable declarations in nested blocks
"no-inner-declarations": "off",
// disallow invalid regular expression strings in the RegExp constructor

View File

@ -13,7 +13,7 @@ const TreeView = React.createFactory(require("devtools/client/shared/components/
// Reps
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const Rep = React.createFactory(REPS.Rep);
const { Rep } = REPS;
const Grip = REPS.Grip;
// DOM Panel

View File

@ -15,7 +15,7 @@ const BoxModelEditable = createFactory(require("./BoxModelEditable"));
// Reps
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const Rep = createFactory(REPS.Rep);
const { Rep } = REPS;
const Types = require("../types");

View File

@ -4,12 +4,12 @@
"use strict";
const { addons, createClass, createFactory, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react");
const { addons, createClass, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
// Reps
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const Rep = createFactory(REPS.Rep);
const { Rep } = REPS;
const ElementNode = REPS.ElementNode;
const Types = require("../types");

View File

@ -12,7 +12,7 @@ define(function (require, exports, module) {
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { createFactories } = require("devtools/client/shared/react-utils");
const Rep = createFactory(REPS.Rep);
const { Rep } = REPS;
const { SearchBox } = createFactories(require("./search-box"));
const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));

View File

@ -24,7 +24,7 @@ const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const MDNLink = createFactory(require("./mdn-link"));
const PropertiesView = createFactory(require("./properties-view"));
const Rep = createFactory(REPS.Rep);
const { Rep } = REPS;
const { button, div, input, textarea } = DOM;
const EDIT_AND_RESEND = L10N.getStr("netmonitor.summary.editAndResend");

View File

@ -14,7 +14,7 @@ const {
} = require("devtools/client/shared/vendor/react");
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const Rep = createFactory(REPS.Rep);
const { Rep } = REPS;
const { FILTER_SEARCH_DELAY } = require("../constants");

View File

@ -8,7 +8,7 @@ const React = require("devtools/client/shared/vendor/react");
const TreeView = React.createFactory(require("devtools/client/shared/components/tree/tree-view"));
const { REPS, MODE, parseURLEncodedText } = require("devtools/client/shared/components/reps/reps");
const Rep = React.createFactory(REPS.Rep);
const { Rep } = REPS;
// Network
const NetInfoParams = React.createFactory(require("./net-info-params"));

View File

@ -8,7 +8,7 @@ const React = require("devtools/client/shared/vendor/react");
// Reps
const TreeView = React.createFactory(require("devtools/client/shared/components/tree/tree-view"));
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const Rep = React.createFactory(REPS.Rep);
const { Rep } = REPS;
// Network
const SizeLimit = React.createFactory(require("./size-limit"));

View File

@ -21,10 +21,9 @@ const {
const VariablesViewLink = createFactory(require("devtools/client/webconsole/new-console-output/components/variables-view-link"));
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { createFactories } = require("devtools/client/shared/react-utils");
const Rep = createFactory(REPS.Rep);
const Rep = REPS.Rep;
const Grip = REPS.Grip;
const StringRep = createFactories(REPS.StringRep).rep;
const StringRep = REPS.StringRep.rep;
GripMessageBody.displayName = "GripMessageBody";
@ -39,6 +38,7 @@ GripMessageBody.propTypes = {
}),
userProvidedStyle: PropTypes.string,
useQuotes: PropTypes.bool,
escapeWhitespace: PropTypes.bool,
};
GripMessageBody.defaultProps = {
@ -46,7 +46,13 @@ GripMessageBody.defaultProps = {
};
function GripMessageBody(props) {
const { grip, userProvidedStyle, serviceContainer, useQuotes } = props;
const {
grip,
userProvidedStyle,
serviceContainer,
useQuotes,
escapeWhitespace
} = props;
let styleObject;
if (userProvidedStyle && userProvidedStyle !== "") {
@ -66,6 +72,7 @@ function GripMessageBody(props) {
? StringRep({
object: grip,
useQuotes: useQuotes,
escapeWhitespace: escapeWhitespace,
mode: props.mode,
style: styleObject
})

View File

@ -43,7 +43,12 @@ function EvaluationResult(props) {
if (message.messageText) {
messageBody = message.messageText;
} else {
messageBody = GripMessageBody({grip: parameters, serviceContainer, useQuotes: true});
messageBody = GripMessageBody({
grip: parameters,
serviceContainer,
useQuotes: true,
escapeWhitespace: false,
});
}
const topLevelClasses = ["cm-s-mozilla"];

View File

@ -11,7 +11,7 @@ add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
info("console.log with a string argument");
let receievedMessages = waitForMessages({
let receivedMessages = waitForMessages({
hud,
messages: [{
// Test that the output does not include quotes.
@ -23,13 +23,14 @@ add_task(function* () {
content.wrappedJSObject.stringLog();
});
yield receievedMessages;
yield receivedMessages;
info("evaluating a string constant");
let jsterm = hud.jsterm;
yield jsterm.execute("\"string constant\"");
yield jsterm.execute("\"string\\nconstant\"");
let msg = yield waitFor(() => findMessage(hud, "constant"));
let body = msg.querySelector(".message-body");
// On the other hand, a string constant result should be quoted.
ok(body.textContent.includes("\"string constant\""), "found expected text");
// On the other hand, a string constant result should be quoted, but
// newlines should be let through.
ok(body.textContent.includes("\"string\nconstant\""), "found expected text");
});

View File

@ -7,16 +7,44 @@ We use telemetry to get metrics of usage of the different features and panels in
The process to add metrics to a tool roughly consists in:
1. Adding the probe to Firefox
2. Using the probe in DevTools code
3. Getting approval from the data team
2. Using Histograms.json probes in DevTools code
3. Using Scalars.yaml probes in DevTools code
4. Getting approval from the data team
### 1. Adding the probe to Firefox
The first step involves creating entries for the probe in the file that contains declarations for all data that Firefox might report to Mozilla.
The first step involves creating entries for the probe in one of the files that contain declarations for all data that Firefox might report to Mozilla.
This file is at `toolkit/components/telemetry/Histograms.json`.
These files are:
- `toolkit/components/telemetry/Histograms.json`
- `toolkit/components/telemetry/Scalars.yaml`
If it's the first time you add one of these, it's advised to follow the style of existing entries. Our entries are prepended with `DEVTOOLS_`. For example:
Scalars allow collection of simple values, like counts, booleans and strings and are to be used whenever possible instead of histograms.
Histograms allow collection of multiple different values, but aggregate them into a number of buckets. Each bucket has a value range and a count of how many values we recorded.
Both scalars & histograms allow recording by keys. This allows for more flexible, two-level data collection.
#### Why the different file formats?
The data team chose YAML for `Scalars.yaml` because it is easy to write and provides a number of features not available in JSON including comments, extensible data types, relational anchors, strings without quotation marks, and mapping types preserving key order.
While we previously used JSON for similar purposes in histograms.json, we have used YAML here because it allows for comments and is generally easier to write.
When the YAML format is proven the data team are considering moving the histograms over to YAML format at some point.
If it's the first time you add one of these, it's advised to follow the style of existing entries.
New data types have been added over the years, so it's quite feasible that some of our probes are not the most suitable nowadays.
There's more information about types (and telemetry in general) on [this page](https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Adding_a_new_Telemetry_probe) and [this other page](https://gecko.readthedocs.io/en/latest/toolkit/components/telemetry/telemetry/collection/index.html).
And of course, in case of doubt, ask!
### Adding probes to `Histograms.json`
Our entries are prefixed with `DEVTOOLS_`. For example:
```javascript
"DEVTOOLS_DOM_OPENED_COUNT": {
@ -40,13 +68,41 @@ If it's the first time you add one of these, it's advised to follow the style of
There are different types of probes you can use. These are specified by the `kind` field. Normally we use `count` for counting how many times the tools are opened, and `exponential` for how many times a panel is active.
New data types have been added over the years, so it's quite feasible that some of our probes are not the most suitable nowadays.
### Adding probes to `Scalars.yaml`
There's more information about types (and telemetry in general) on [this page](https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Adding_a_new_Telemetry_probe) and [this other page](https://gecko.readthedocs.io/en/latest/toolkit/components/telemetry/telemetry/collection/index.html).
Our entries are prefixed with `devtools.`. For example:
And of course, in case of doubt, ask!
```javascript
devtools.toolbar.eyedropper:
opened:
bug_numbers:
- 1247985
- 1352115
description: Number of times the DevTools Eyedropper has been opened via the inspector toolbar.
expires: never
kind: uint
notification_emails:
- dev-developer-tools@lists.mozilla.org
release_channel_collection: opt-out
record_in_processes:
- 'main'
### 2. Using the probe in DevTools code
devtools.copy.unique.css.selector:
opened:
bug_numbers:
- 1323700
- 1352115
description: Number of times the DevTools copy unique CSS selector has been used.
expires: "57"
kind: uint
notification_emails:
- dev-developer-tools@lists.mozilla.org
release_channel_collection: opt-out
record_in_processes:
- 'main'
```
### 2. Using Histograms.json probes in DevTools code
Once the probe has been declared in the `Histograms.json` file, you'll need to actually use it in our code.
@ -67,7 +123,7 @@ Then, include that module on each tool that requires telemetry:
let Telemetry = require("devtools/client/shared/telemetry");
```
Create telemetry instance on the tool constructor:
Create a telemetry instance on the tool constructor:
```javascript
this._telemetry = new Telemetry();
@ -87,6 +143,48 @@ this._telemetry.toolClosed("mytoolname");
Note that `mytoolname` is the id we declared in the `telemetry.js` module.
### 3. Using Scalars.yaml probes in DevTools code
Once the probe has been declared in the `Scalars.yaml` file, you'll need to actually use it in our code.
First, you need to give it an id in `devtools/client/shared/telemetry.js`. You will want to follow the style of existing lowercase histogram entries. For example:
```javascript
toolbareyedropper: {
scalar: "devtools.toolbar.eyedropper.opened", // Note that the scalar is lowercase
},
copyuniquecssselector: {
scalar: "devtools.copy.unique.css.selector.opened",
},
```
... would correspond to the probes we declared in the previous section.
Then, include that module on each tool that requires telemetry:
```javascript
let Telemetry = require("devtools/client/shared/telemetry");
```
Create a telemetry instance on the tool constructor:
```javascript
this._telemetry = new Telemetry();
```
And use the instance to report e.g. tool opening...
```javascript
this._telemetry.toolOpened("mytoolname");
```
Notes:
- `mytoolname` is the id we declared in the `Scalars.yaml` module.
- Because we are not logging tool's time opened in `Scalars.yaml` we don't care
about toolClosed. Of course, if there was an accompanying `timerHistogram` field defined
in `telemetry.js` and `histograms.json` then `toolClosed` should also be added.
#### Note on top level panels
The code for the tabs uses their ids to automatically report telemetry when you switch between panels, so you don't need to explicitly call `toolOpened` and `toolClosed` on top level panels.
@ -116,7 +214,7 @@ So watch out for errors.
#### Compile it!
It's strongly recommended that you do a full Firefox build if you have edited `Histograms.json`, as it is processed at build time, and various checks will be run on it to guarantee it is valid.
It's strongly recommended that you do a full Firefox build if you have edited either `Histograms.json` or `Scalars.yaml`, as they are processed at build time, and various checks will be run on it to guarantee it is valid.
```
./mach build
@ -126,7 +224,7 @@ If you use `mach build faster` or artifact builds, the checks will not be perfor
Save yourself some time and run the checks locally.
### 3. Getting approval from the data team
### 4. Getting approval from the data team
This is required before the changes make their way into `mozilla-central`.
@ -162,4 +260,3 @@ It's also recommended to take small steps and run the queries often to detect er
Slow queries will be interrupted by the system, so don't worry about "fetching too much data" or "using too many resources". There's built-in protection to avoid your code eating up the Telemetry database.
Funnily, if you're based in Europe, you might be in luck, as the website tends to be more responsive during European working hours than it is at Pacific working hours, as seemingly there's less people in Europe interacting with it.

View File

@ -6,7 +6,7 @@
const { Cc, Ci, Cu } = require("chrome");
const { getCurrentZoom, getWindowDimensions, getViewportDimensions,
getRootBindingParent } = require("devtools/shared/layout/utils");
getRootBindingParent, loadSheet } = require("devtools/shared/layout/utils");
const { on, emit } = require("sdk/event/core");
const lazyContainer = {};
@ -104,17 +104,14 @@ exports.isXUL = isXUL;
/**
* Inject a helper stylesheet in the window.
*/
var installedHelperSheets = new WeakMap();
var installedHelperSheets = new WeakSet();
function installHelperSheet(win, source, type = "agent") {
function installHelperSheet(win, url = STYLESHEET_URI, type = "agent") {
if (installedHelperSheets.has(win.document)) {
return;
}
let {Style} = require("sdk/stylesheet/style");
let {attach} = require("sdk/content/mod");
let style = Style({source, type});
attach(style, win);
installedHelperSheets.set(win.document, style);
loadSheet(win, url, type);
installedHelperSheets.add(win.document);
}
exports.installHelperSheet = installHelperSheet;
@ -278,15 +275,7 @@ CanvasFrameAnonymousContentHelper.prototype = {
// <style scoped> doesn't work inside anonymous content (see bug 1086532).
// If it did, highlighters.css would be injected as an anonymous content
// node using CanvasFrameAnonymousContentHelper instead.
if (!installedHelperSheets.has(doc)) {
installedHelperSheets.set(doc, true);
let source = "@import url('" + STYLESHEET_URI + "');";
let url = "data:text/css;charset=utf-8," + encodeURIComponent(source);
let winUtils = this.highlighterEnv.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
winUtils.loadSheetUsingURIString(url, winUtils.AGENT_SHEET);
}
installHelperSheet(this.highlighterEnv.window);
let node = this.nodeBuilder();

View File

@ -72,7 +72,8 @@ const {
isNativeAnonymous,
isXBLAnonymous,
isShadowAnonymous,
getFrameElement
getFrameElement,
loadSheet
} = require("devtools/shared/layout/utils");
const {getLayoutChangesObserver, releaseLayoutChangesObserver} = require("devtools/server/actors/reflow");
const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
@ -127,7 +128,7 @@ const PSEUDO_SELECTORS = [
["::selection", 0]
];
var HELPER_SHEET = `
var HELPER_SHEET = `data:text/css;charset=utf-8,
.__fx-devtools-hide-shortcut__ {
visibility: hidden !important;
}
@ -1865,15 +1866,12 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
_installHelperSheet: function (node) {
if (!this.installedHelpers) {
this.installedHelpers = new WeakMap();
this.installedHelpers = new WeakSet();
}
let win = node.rawNode.ownerGlobal;
if (!this.installedHelpers.has(win)) {
let { Style } = require("sdk/stylesheet/style");
let { attach } = require("sdk/content/mod");
let style = Style({source: HELPER_SHEET, type: "agent" });
attach(style, win);
this.installedHelpers.set(win, style);
loadSheet(win, HELPER_SHEET, "agent");
this.installedHelpers.add(win);
}
},

View File

@ -106,8 +106,12 @@ ObjectActor.prototype = {
// to lazily display them when there is a bunch.
// Throws on some MouseEvent object in tests.
try {
// Bug 1163520: Assert on internal functions
if (!["Function", "Proxy"].includes(g.class)) {
if (TYPED_ARRAY_CLASSES.indexOf(g.class) != -1) {
// Bug 1348761: getOwnPropertyNames is unecessary slow on TypedArrays
let length = DevToolsUtils.getProperty(this.obj, "length");
g.ownPropertyLength = length;
} else if (!["Function", "Proxy"].includes(g.class)) {
// Bug 1163520: Assert on internal functions
g.ownPropertyLength = this.obj.getOwnPropertyNames().length;
}
} catch (e) {}

View File

@ -253,7 +253,9 @@ const ProfilerManager = (function () {
* profiler is stopped.
*/
get sharedLibraries() {
return nsIProfilerModule.sharedLibraries;
return {
sharedLibraries: nsIProfilerModule.sharedLibraries
};
},
/**

View File

@ -21,7 +21,8 @@ function run_test() {
}
function test_sharedlibraries(client, actor, callback) {
client.request({ to: actor, type: "sharedLibraries" }, libs => {
client.request({ to: actor, type: "sharedLibraries" }, response => {
const libs = response.sharedLibraries;
do_check_eq(typeof libs, "object");
do_check_true(Array.isArray(libs));
do_check_eq(typeof libs, "object");

View File

@ -7,6 +7,12 @@
const { Ci, Cc } = require("chrome");
const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
const SHEET_TYPE = {
"agent": "AGENT_SHEET",
"user": "USER_SHEET",
"author": "AUTHOR_SHEET"
};
loader.lazyRequireGetter(this, "setIgnoreLayoutChanges", "devtools/server/actors/reflow", true);
exports.setIgnoreLayoutChanges = (...args) =>
this.setIgnoreLayoutChanges(...args);
@ -715,3 +721,49 @@ function getWindowFor(node) {
}
return null;
}
/**
* Synchronously loads a style sheet from `uri` and adds it to the list of
* additional style sheets of the document.
* The sheets added takes effect immediately, and only on the document of the
* `window` given.
*
* @param {DOMWindow} window
* @param {String} url
* @param {String} [type="author"]
*/
function loadSheet(window, url, type = "author") {
if (!(type in SHEET_TYPE)) {
type = "author";
}
let windowUtils = utilsFor(window);
try {
windowUtils.loadSheetUsingURIString(url, windowUtils[SHEET_TYPE[type]]);
} catch (e) {
// The method fails if the url is already loaded.
}
}
exports.loadSheet = loadSheet;
/**
* Remove the document style sheet at `sheetURI` from the list of additional
* style sheets of the document. The removal takes effect immediately.
*
* @param {DOMWindow} window
* @param {String} url
* @param {String} [type="author"]
*/
function removeSheet(window, url, type = "author") {
if (!(type in SHEET_TYPE)) {
type = "author";
}
let windowUtils = utilsFor(window);
try {
windowUtils.removeSheetUsingURIString(url, windowUtils[SHEET_TYPE[type]]);
} catch (e) {
// The method fails if the url is already removed.
}
}
exports.removeSheet = removeSheet;

View File

@ -1012,7 +1012,13 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
if (!CreateAndInitGL(forceEnabled, &failReasons)) {
nsCString text("WebGL creation failed: ");
for (const auto& cur : failReasons) {
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, cur.key);
// Don't try to accumulate using an empty key if |cur.key| is empty.
if (cur.key.IsEmpty()) {
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
NS_LITERAL_CSTRING("FEATURE_FAILURE_REASON_UNKNOWN"));
} else {
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, cur.key);
}
text.AppendASCII("\n* ");
text.Append(cur.info);

View File

@ -146,6 +146,7 @@ TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
bool IMEStateManager::sInstalledMenuKeyboardListener = false;
bool IMEStateManager::sIsGettingNewIMEState = false;
bool IMEStateManager::sCheckForIMEUnawareWebApps = false;
bool IMEStateManager::sInputModeSupported = false;
bool IMEStateManager::sRemoteHasFocus = false;
// static
@ -156,6 +157,11 @@ IMEStateManager::Init()
&sCheckForIMEUnawareWebApps,
"intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition",
false);
Preferences::AddBoolVarCache(
&sInputModeSupported,
"dom.forms.inputmode",
false);
}
// static
@ -1000,7 +1006,7 @@ IMEStateManager::SetIMEState(const IMEState& aState,
context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
}
if (Preferences::GetBool("dom.forms.inputmode", false) ||
if (sInputModeSupported ||
nsContentUtils::IsChromeDoc(aContent->OwnerDoc())) {
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
context.mHTMLInputInputmode);

View File

@ -285,6 +285,7 @@ protected:
static bool sInstalledMenuKeyboardListener;
static bool sIsGettingNewIMEState;
static bool sCheckForIMEUnawareWebApps;
static bool sInputModeSupported;
static bool sRemoteHasFocus;
class MOZ_STACK_CLASS GettingNewIMEStateBlocker final

View File

@ -131,7 +131,7 @@ function UpdateSessionFunc(test, token, sessionType, resolve, reject) {
"k":HexToBase64(key)
});
} else {
bail(token + " couldn't find key for key id " + idHex)("No such key");
reject(`${token} couldn't find key for key id ${idHex}`);
}
}
@ -145,8 +145,7 @@ function UpdateSessionFunc(test, token, sessionType, resolve, reject) {
Log(token, "MediaKeySession update ok!");
resolve(ev.target);
}).catch(function(reason) {
bail(token + " MediaKeySession update failed")(reason);
reject();
reject(`${token} MediaKeySession update failed: ${reason}`);
});
}
}
@ -192,8 +191,12 @@ function AppendTrack(test, ms, track, token, loadParams)
sb.appendBuffer(new Uint8Array(req.response));
});
req.addEventListener("error", function(){info(token + " error fetching " + fragmentFile);});
req.addEventListener("abort", function(){info(token + " aborted fetching " + fragmentFile);});
req.addEventListener("error", function() {
reject(`${token} - ${track.name}: error fetching ${fragmentFile}`);
});
req.addEventListener("abort", function() {
reject(`${token} - ${track.name}: aborted fetching ${fragmentFile}`);
});
Log(token, track.name + ": addNextFragment() fetching next fragment " + fragmentFile);
req.send(null);
@ -238,16 +241,9 @@ function LoadTest(test, elem, token, loadParams)
elem.src = URL.createObjectURL(ms);
return new Promise(function (resolve, reject) {
var firstOpen = true;
ms.addEventListener("sourceopen", function () {
if (!firstOpen) {
Log(token, "sourceopen again?");
return;
}
firstOpen = false;
Log(token, "sourceopen");
return Promise.all(test.tracks.map(function(track) {
Promise.all(test.tracks.map(function(track) {
return AppendTrack(test, ms, track, token, loadParams);
})).then(function() {
if (loadParams && loadParams.noEndOfStream) {
@ -257,10 +253,8 @@ function LoadTest(test, elem, token, loadParams)
ms.endOfStream();
}
resolve();
}).catch(function() {
Log(token, "error while loading tracks");
});
})
}).catch(reject);
}, {once: true});
});
}

View File

@ -1415,13 +1415,6 @@ CompositorD3D11::EndFrame()
return;
}
if (mDevice->GetDeviceRemovedReason() != S_OK) {
gfxCriticalNote << "GFX: D3D11 skip EndFrame with device-removed.";
Compositor::EndFrame();
mCurrentRT = nullptr;
return;
}
LayoutDeviceIntSize oldSize = mSize;
EnsureSize();
if (mSize.width <= 0 || mSize.height <= 0) {

View File

@ -23,7 +23,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
# POSIX
LOCAL_INCLUDES += [
'/toolkit/crashreporter/jsoncpp/include',
'/toolkit/components/jsoncpp/include',
]
USE_LIBS += [

View File

@ -208,5 +208,23 @@ interface imgIRequest : nsIRequest
* underlying call.
*/
void decrementAnimationConsumers();
/**
* Request loading priority boost to requested category, each category
* of request increases priority only one time..
*
* CATEGORY_FRAME_INIT: increase priority when the imgRequest is associated
* with an nsImageFrame.
*
* CATEGORY_SIZE_QUERY: increase priority when size decoding is necessary to
* determine the layout size of the associated nsImageFrame.
*
* CATEGORY_DISPLAY: increase priority when the image is about to be displayed
* in the viewport.
*/
const uint32_t CATEGORY_FRAME_INIT = 1 << 0;
const uint32_t CATEGORY_SIZE_QUERY = 1 << 1;
const uint32_t CATEGORY_DISPLAY = 1 << 2;
void boostPriority(in uint32_t aCategory);
};

View File

@ -541,12 +541,50 @@ imgRequest::AdjustPriority(imgRequestProxy* proxy, int32_t delta)
return;
}
AdjustPriorityInternal(delta);
}
void
imgRequest::AdjustPriorityInternal(int32_t aDelta)
{
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
if (p) {
p->AdjustPriority(delta);
p->AdjustPriority(aDelta);
}
}
void
imgRequest::BoostPriority(uint32_t aCategory)
{
uint32_t newRequestedCategory =
(mBoostCategoriesRequested & aCategory) ^ aCategory;
if (!newRequestedCategory) {
// priority boost for each category can only apply once.
return;
}
MOZ_LOG(gImgLog, LogLevel::Debug,
("[this=%p] imgRequest::BoostPriority for category %x",
this, newRequestedCategory));
int32_t delta = 0;
if (newRequestedCategory & imgIRequest::CATEGORY_FRAME_INIT) {
--delta;
}
if (newRequestedCategory & imgIRequest::CATEGORY_SIZE_QUERY) {
--delta;
}
if (newRequestedCategory & imgIRequest::CATEGORY_DISPLAY) {
delta += nsISupportsPriority::PRIORITY_HIGH;
}
AdjustPriorityInternal(delta);
mBoostCategoriesRequested |= newRequestedCategory;
}
bool
imgRequest::HasTransferredData() const
{

View File

@ -171,6 +171,8 @@ public:
/// of @aProxy.
void AdjustPriority(imgRequestProxy* aProxy, int32_t aDelta);
void BoostPriority(uint32_t aCategory);
/// Returns a weak pointer to the underlying request.
nsIRequest* GetRequest() const { return mRequest; }
@ -223,6 +225,8 @@ private:
/// Returns true if StartDecoding() was called.
bool IsDecodeRequested() const;
void AdjustPriorityInternal(int32_t aDelta);
// Weak reference to parent loader; this request cannot outlive its owner.
imgLoader* mLoader;
nsCOMPtr<nsIRequest> mRequest;
@ -275,6 +279,9 @@ private:
nsresult mImageErrorCode;
// The categories of prioritization strategy that have been requested.
uint32_t mBoostCategoriesRequested = 0;
mutable mozilla::Mutex mMutex;
// Member variables protected by mMutex. Note that *all* flags in our bitfield

View File

@ -718,6 +718,14 @@ imgRequestProxy::GetCORSMode(int32_t* aCorsMode)
return NS_OK;
}
NS_IMETHODIMP
imgRequestProxy::BoostPriority(uint32_t aCategory)
{
NS_ENSURE_STATE(GetOwner() && !mCanceled);
GetOwner()->BoostPriority(aCategory);
return NS_OK;
}
/** nsISupportsPriority methods **/
NS_IMETHODIMP

View File

@ -6,6 +6,7 @@ support-files =
head.js
image.html
imageX2.html
browser_docshell_type_editor/**
[browser_bug666317.js]
skip-if = true || e10s # Bug 1207012 - Permaorange from an uncaught exception that isn't actually turning the suite orange until it hits beta, Bug 948194 - Decoded Images seem to not be discarded on memory-pressure notification with e10s enabled

View File

@ -4,14 +4,31 @@
const Ci = Components.interfaces;
const SIMPLE_HTML = "data:text/html,<html><head></head><body></body></html>";
/**
* Returns the directory where the chrome.manifest file for the test can be found.
*
* @return nsILocalFile of the manifest directory
*/
function getManifestDir() {
let path = getTestFilePath("browser_docshell_type_editor");
let file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(path);
return file;
}
// The following URI is *not* accessible to content, hence loading that URI
// from an unprivileged site should be blocked. If docshell is of appType
// APP_TYPE_EDITOR however the load should be allowed.
// >> chrome://devtools/content/framework/dev-edition-promo/dev-edition-logo.png
// >> chrome://test1/skin/privileged.png
add_task(function* () {
info("docshell of appType APP_TYPE_EDITOR can access privileged images.");
// Load a temporary manifest adding a route to a privileged image
let manifestDir = getManifestDir();
Components.manager.addBootstrappedManifestLocation(manifestDir);
yield BrowserTestUtils.withNewTab({
gBrowser,
url: SIMPLE_HTML
@ -28,6 +45,7 @@ add_task(function* () {
is(rootDocShell.appType, Ci.nsIDocShell.APP_TYPE_EDITOR,
"sanity check: appType after update should be type editor");
return new Promise(resolve => {
let doc = content.document;
let image = doc.createElement("img");
@ -44,15 +62,21 @@ add_task(function* () {
resolve();
}
doc.body.appendChild(image);
image.src = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-logo.png";
image.src = "chrome://test1/skin/privileged.png";
});
});
});
Components.manager.removeBootstrappedManifestLocation(manifestDir);
});
add_task(function* () {
info("docshell of appType APP_TYPE_UNKNOWN can *not* access privileged images.");
// Load a temporary manifest adding a route to a privileged image
let manifestDir = getManifestDir();
Components.manager.addBootstrappedManifestLocation(manifestDir);
yield BrowserTestUtils.withNewTab({
gBrowser,
url: SIMPLE_HTML
@ -85,8 +109,10 @@ add_task(function* () {
resolve();
}
doc.body.appendChild(image);
image.src = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-logo.png";
image.src = "chrome://test1/skin/privileged.png";
});
});
});
Components.manager.removeBootstrappedManifestLocation(manifestDir);
});

View File

@ -0,0 +1 @@
skin test1 test img/

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

View File

@ -9231,41 +9231,6 @@ BytecodeEmitter::isRestParameter(ParseNode* pn)
return false;
}
bool
BytecodeEmitter::emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted)
{
// Emit a pereparation code to optimize the spread call with a rest
// parameter:
//
// function f(...args) {
// g(...args);
// }
//
// If the spread operand is a rest parameter and it's optimizable array,
// skip spread operation and pass it directly to spread call operation.
// See the comment in OptimizeSpreadCall in Interpreter.cpp for the
// optimizable conditons.
if (!isRestParameter(arg0)) {
*emitted = false;
return true;
}
if (!emitTree(arg0))
return false;
if (!emit1(JSOP_OPTIMIZE_SPREADCALL))
return false;
if (!emitJump(JSOP_IFNE, jmp))
return false;
if (!emit1(JSOP_POP))
return false;
*emitted = true;
return true;
}
bool
BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
@ -9423,18 +9388,43 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs
}
} else {
ParseNode* args = pn2->pn_next;
JumpList jmp;
bool optCodeEmitted = false;
if (argc == 1) {
if (!emitOptimizeSpread(args->pn_kid, &jmp, &optCodeEmitted))
bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid);
IfThenElseEmitter ifNotOptimizable(this);
if (emitOptCode) {
// Emit a preparation code to optimize the spread call with a rest
// parameter:
//
// function f(...args) {
// g(...args);
// }
//
// If the spread operand is a rest parameter and it's optimizable
// array, skip spread operation and pass it directly to spread call
// operation. See the comment in OptimizeSpreadCall in
// Interpreter.cpp for the optimizable conditons.
if (!emitTree(args->pn_kid))
return false;
if (!emit1(JSOP_OPTIMIZE_SPREADCALL))
return false;
if (!emit1(JSOP_NOT))
return false;
if (!ifNotOptimizable.emitIf())
return false;
if (!emit1(JSOP_POP))
return false;
}
if (!emitArray(args, argc, JSOP_SPREADCALLARRAY))
return false;
if (optCodeEmitted) {
if (!emitJumpTargetAndPatch(jmp))
if (emitOptCode) {
if (!ifNotOptimizable.emitEnd())
return false;
}

View File

@ -753,7 +753,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter
ValueUsage valueUsage = ValueUsage::WantValue);
bool isRestParameter(ParseNode* pn);
MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted);
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);

View File

@ -2316,6 +2316,16 @@ IonBuilder::inspectOpcode(JSOp op)
pushConstant(MagicValue(JS_IS_CONSTRUCTING));
return Ok();
case JSOP_OPTIMIZE_SPREADCALL:
{
// Assuming optimization isn't available doesn't affect correctness.
// TODO: Investigate dynamic checks.
MDefinition* arr = current->peek(-1);
arr->setImplicitlyUsedUnchecked();
pushConstant(BooleanValue(false));
return Ok();
}
default:
break;
}

View File

@ -336,7 +336,6 @@ ControlFlowGenerator::snoopControlFlow(JSOp op)
case JSOP_TRY:
return processTry();
case JSOP_OPTIMIZE_SPREADCALL:
case JSOP_THROWMSG:
// Not implemented yet.
return ControlStatus::Abort;

View File

@ -236,7 +236,14 @@ private:
bool
xpc_LocalizeContext(JSContext* cx)
{
JS_SetLocaleCallbacks(cx, new XPCLocaleCallbacks());
// We want to assign the locale callbacks only the first time we
// localize the context.
// All consequent calls to this function are result of language changes
// and should not assign it again.
const JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(cx);
if (!lc) {
JS_SetLocaleCallbacks(cx, new XPCLocaleCallbacks());
}
// Set the default locale.

View File

@ -163,6 +163,9 @@ SERVO_BINDING_FUNC(Servo_AnimationValues_Interpolate,
SERVO_BINDING_FUNC(Servo_AnimationValues_IsInterpolable, bool,
RawServoAnimationValueBorrowed from,
RawServoAnimationValueBorrowed to)
SERVO_BINDING_FUNC(Servo_AnimationValues_ComputeDistance, double,
RawServoAnimationValueBorrowed from,
RawServoAnimationValueBorrowed to)
SERVO_BINDING_FUNC(Servo_AnimationValue_Serialize, void,
RawServoAnimationValueBorrowed value,
nsCSSPropertyID property,

View File

@ -224,6 +224,7 @@ skip-if = toolkit == 'android'
skip-if = toolkit == 'android'
[test_initial_storage.html]
[test_keyframes_rules.html]
[test_keyframes_vendor_prefix.html]
[test_load_events_on_stylesheets.html]
[test_logical_properties.html]
[test_media_queries.html]

View File

@ -0,0 +1,193 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>
Test for interaction between prefixed and non-prefixed @keyframes rules with
the same name
</title>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<div id='log'></div>
<script>
/**
* Appends a style element to the document head.
*
* @param t The testharness.js Test object. If provided, this will be used
* to register a cleanup callback to remove the style element
* when the test finishes.
*
* @param rules A dictionary object with selector names and rules to set on
* the style sheet.
*/
function addStyle(t, rules) {
var extraStyle = document.createElement('style');
document.head.appendChild(extraStyle);
if (rules) {
var sheet = extraStyle.sheet;
for (var selector in rules) {
sheet.insertRule(selector + '{' + rules[selector] + '}',
sheet.cssRules.length);
}
}
if (t && typeof t.add_cleanup === 'function') {
t.add_cleanup(function() {
extraStyle.remove();
});
}
}
/**
* Appends a div to the document body.
*
* @param t The testharness.js Test object. If provided, this will be used
* to register a cleanup callback to remove the div when the test
* finishes.
*
* @param attrs A dictionary object with attribute names and values to set on
* the div.
*/
function addDiv(t, attrs) {
var div = document.createElement('div');
if (attrs) {
for (var attrName in attrs) {
div.setAttribute(attrName, attrs[attrName]);
}
}
document.body.appendChild(div);
if (t && typeof t.add_cleanup === 'function') {
t.add_cleanup(function() {
div.remove();
});
}
return div;
}
var isStylo = false;
// 'layout.css.servo.enabled' is not yet defined on gecko, so we need a try
// block.
try {
isStylo = SpecialPowers.getBoolPref('layout.css.servo.enabled');
} catch(e) {
}
test(function(t) {
addStyle(t,
{ 'dummy': '', // XXX bug 1336863 hackaround: a single insertRule is broken
// on stylo.
'@-webkit-keyframes anim': 'from,to { color: rgb(0, 255, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, '-webkit- prefix keyframes');
test(function(t) {
addStyle(t,
{ 'dummy': '', // XXX bug 1336863 hackaround, as above.
'@-moz-keyframes anim': 'from,to { color: rgb(0, 255, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, '-moz- prefix keyframes');
test(function(t) {
addStyle(t,
{ 'dummy': '', // XXX bug 1336863 hackaround, as above.
'@-WEBKIT-keyframes anim': 'from,to { color: rgb(0, 255, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, '-WEBKIT- prefix keyframes');
test(function(t) {
addStyle(t,
{ 'dummy': '', // XXX bug 1336863 hackaround, as above.
'@-MOZ-keyframes anim': 'from,to { color: rgb(0, 255, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, '-MOZ- prefix keyframes');
test(function(t) {
addStyle(t,
{ 'dummy': '', // XXX bug 1336863 hackaround, as above.
'@-webkit-KEYFRAMES anim': 'from,to { color: rgb(0, 255, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, '-webkit- prefix KEYFRAMES');
test(function(t) {
if (!isStylo) {
// FIXME: Bug 1312918: later prefixed rule incorrectly overrides earlier
// non-prefixed on gecko.
return;
}
addStyle(t,
{ '@keyframes anim': 'from,to { color: rgb(0, 255, 0); }',
'@-webkit-keyframes anim': 'from,to { color: rgb(255, 0, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, '-webkit-keyframes should not override earlier non-prefix keyframes');
test(function(t) {
if (!isStylo) {
// FIXME: Bug 1312918: later prefixed rule incorrectly overrides earlier
// non-prefixed on gecko.
return;
}
addStyle(t,
{ '@keyframes anim': 'from,to { color: rgb(0, 255, 0); }',
'@-moz-keyframes anim': 'from,to { color: rgb(255, 0, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, '-moz-keyframes should not override earlier non-prefix keyframes');
test(function(t) {
addStyle(t,
{ '@-moz-keyframes anim': 'from,to { color: rgb(255, 0, 0); }',
'@keyframes anim': 'from,to { color: rgb(0, 255, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, 'non-prefix keyframes should override earlier -moz-keyframes');
test(function(t) {
addStyle(t,
{ '@-webkit-keyframes anim': 'from,to { color: rgb(255, 0, 0); }',
'@keyframes anim': 'from,to { color: rgb(0, 255, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, 'non-prefix keyframes should override earlier -webkit-keyframes');
test(function(t) {
addStyle(t,
{ '@-webkit-keyframes anim': 'from,to { color: rgb(255, 0, 0); }',
'@-moz-keyframes anim': 'from,to { color: rgb(0, 255, 0); }' });
var div = addDiv(t, { style: 'animation: anim 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
addStyle(t,
{ '@-moz-keyframes anim2': 'from,to { color: rgb(255, 0, 0); }',
'@-webkit-keyframes anim2': 'from,to { color: rgb(0, 255, 0); }' });
var div = addDiv(t, { style: 'animation: anim2 100s' });
assert_equals(getComputedStyle(div).color, 'rgb(0, 255, 0)');
}, 'last prefixed keyframes should override earlier prefixed keyframes');
</script>

View File

@ -234,6 +234,9 @@ static const DllBlockInfo sWindowsDllBlocklist[] = {
{ "idmcchandler7.dll", ALL_VERSIONS },
{ "idmcchandler7_64.dll", ALL_VERSIONS },
// Nahimic 2 breaks applicaton update (bug 1356637)
{ "nahimic2devprops.dll", ALL_VERSIONS },
{ nullptr, 0 }
};

View File

@ -48,18 +48,123 @@ namespace net {
//
static LazyLogModule gChannelClassifierLog("nsChannelClassifier");
// Whether channels should be annotated as being on the tracking protection
// list.
static bool sAnnotateChannelEnabled = false;
// Whether the priority of the channels annotated as being on the tracking
// protection list should be lowered.
static bool sLowerNetworkPriority = false;
static bool sIsInited = false;
#undef LOG
#define LOG(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Debug, args)
#define LOG_ENABLED() MOZ_LOG_TEST(gChannelClassifierLog, LogLevel::Debug)
#define URLCLASSIFIER_SKIP_HOSTNAMES "urlclassifier.skipHostnames"
#define URLCLASSIFIER_TRACKING_WHITELIST "urlclassifier.trackingWhitelistTable"
// Put CachedPrefs in anonymous namespace to avoid any collision from outside of
// this file.
namespace {
/**
* It is not recommended to read from Preference everytime a channel is
* connected.
* That is not fast and we should cache preference values and reuse them
*/
class CachedPrefs final
{
public:
static CachedPrefs* GetInstance();
void Init();
bool IsAllowListExample() { return sAllowListExample;}
bool IsLowerNetworkPriority() { return sLowerNetworkPriority;}
bool IsAnnotateChannelEnabled() { return sAnnotateChannelEnabled;}
nsCString GetTrackingWhiteList() { return mTrackingWhitelist; }
void SetTrackingWhiteList(const nsACString& aList) { mTrackingWhitelist = aList; }
nsCString GetSkipHostnames() { return mSkipHostnames; }
void SetSkipHostnames(const nsACString& aHostnames) { mSkipHostnames = aHostnames; }
private:
friend class StaticAutoPtr<CachedPrefs>;
CachedPrefs();
~CachedPrefs();
static void OnPrefsChange(const char* aPrefName, void* );
// Whether channels should be annotated as being on the tracking protection
// list.
static bool sAnnotateChannelEnabled;
// Whether the priority of the channels annotated as being on the tracking
// protection list should be lowered.
static bool sLowerNetworkPriority;
static bool sAllowListExample;
nsCString mTrackingWhitelist;
nsCString mSkipHostnames;
static StaticAutoPtr<CachedPrefs> sInstance;
};
bool CachedPrefs::sAllowListExample = false;
bool CachedPrefs::sLowerNetworkPriority = false;
bool CachedPrefs::sAnnotateChannelEnabled = false;
StaticAutoPtr<CachedPrefs> CachedPrefs::sInstance;
// static
void
CachedPrefs::OnPrefsChange(const char* aPref, void* aClosure)
{
CachedPrefs* prefs = static_cast<CachedPrefs*> (aClosure);
if (!strcmp(aPref, URLCLASSIFIER_SKIP_HOSTNAMES)) {
nsCString skipHostnames = Preferences::GetCString(URLCLASSIFIER_SKIP_HOSTNAMES);
ToLowerCase(skipHostnames);
prefs->SetSkipHostnames(skipHostnames);
} else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_WHITELIST)) {
nsCString trackingWhitelist = Preferences::GetCString(URLCLASSIFIER_TRACKING_WHITELIST);
prefs->SetTrackingWhiteList(trackingWhitelist);
}
}
void
CachedPrefs::Init()
{
Preferences::AddBoolVarCache(&sAnnotateChannelEnabled,
"privacy.trackingprotection.annotate_channels");
Preferences::AddBoolVarCache(&sLowerNetworkPriority,
"privacy.trackingprotection.lower_network_priority");
Preferences::AddBoolVarCache(&sAllowListExample,
"channelclassifier.allowlist_example");
Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
URLCLASSIFIER_SKIP_HOSTNAMES, this);
Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
URLCLASSIFIER_TRACKING_WHITELIST, this);
}
// static
CachedPrefs*
CachedPrefs::GetInstance()
{
if (!sInstance) {
sInstance = new CachedPrefs();
sInstance->Init();
ClearOnShutdown(&sInstance);
}
MOZ_ASSERT(sInstance);
return sInstance;
}
CachedPrefs::CachedPrefs()
{
MOZ_COUNT_CTOR(CachedPrefs);
}
CachedPrefs::~CachedPrefs()
{
MOZ_COUNT_DTOR(CachedPrefs);
Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_SKIP_HOSTNAMES, this);
Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_WHITELIST, this);
}
} // anonymous namespace
NS_IMPL_ISUPPORTS(nsChannelClassifier,
nsIURIClassifierCallback,
nsIObserver)
@ -71,13 +176,6 @@ nsChannelClassifier::nsChannelClassifier(nsIChannel *aChannel)
mTrackingProtectionEnabled(Nothing())
{
MOZ_ASSERT(mChannel);
if (!sIsInited) {
sIsInited = true;
Preferences::AddBoolVarCache(&sAnnotateChannelEnabled,
"privacy.trackingprotection.annotate_channels");
Preferences::AddBoolVarCache(&sLowerNetworkPriority,
"privacy.trackingprotection.lower_network_priority");
}
}
nsresult
@ -156,8 +254,7 @@ nsChannelClassifier::ShouldEnableTrackingProtectionInternal(nsIChannel *aChannel
nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
const char ALLOWLIST_EXAMPLE_PREF[] = "channelclassifier.allowlist_example";
if (!topWinURI && Preferences::GetBool(ALLOWLIST_EXAMPLE_PREF, false)) {
if (!topWinURI && CachedPrefs::GetInstance()->IsAllowListExample()) {
LOG(("nsChannelClassifier[%p]: Allowlisting test domain\n", this));
rv = ios->NewURI(NS_LITERAL_CSTRING("http://allowlisted.example.com"),
nullptr, nullptr, getter_AddRefs(topWinURI));
@ -365,14 +462,11 @@ nsChannelClassifier::StartInternal()
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) return NS_ERROR_UNEXPECTED;
// Skip whitelisted hostnames.
nsAutoCString whitelisted;
Preferences::GetCString("urlclassifier.skipHostnames", &whitelisted);
if (!whitelisted.IsEmpty()) {
ToLowerCase(whitelisted);
nsCString skipHostnames = CachedPrefs::GetInstance()->GetSkipHostnames();
if (!skipHostnames.IsEmpty()) {
LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s",
this, whitelisted.get()));
if (IsHostnameWhitelisted(uri, whitelisted)) {
this, skipHostnames.get()));
if (IsHostnameWhitelisted(uri, skipHostnames)) {
return NS_ERROR_UNEXPECTED;
}
}
@ -411,7 +505,9 @@ nsChannelClassifier::StartInternal()
}
// The classify is running in parent process, no need to give a valid event
// target
rv = uriClassifier->Classify(principal, nullptr, sAnnotateChannelEnabled | trackingProtectionEnabled,
rv = uriClassifier->Classify(principal, nullptr,
CachedPrefs::GetInstance()->IsAnnotateChannelEnabled() ||
trackingProtectionEnabled,
this, &expectCallback);
if (NS_FAILED(rv)) {
return rv;
@ -733,10 +829,8 @@ nsChannelClassifier::IsTrackerWhitelisted(const nsACString& aList,
do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString tables;
Preferences::GetCString("urlclassifier.trackingWhitelistTable", &tables);
if (tables.IsEmpty()) {
nsCString trackingWhitelist = CachedPrefs::GetInstance()->GetTrackingWhiteList();
if (trackingWhitelist.IsEmpty()) {
LOG(("nsChannelClassifier[%p]:IsTrackerWhitelisted whitelist disabled",
this));
return NS_ERROR_TRACKING_URI;
@ -780,7 +874,7 @@ nsChannelClassifier::IsTrackerWhitelisted(const nsACString& aList,
new IsTrackerWhitelistedCallback(this, aList, aProvider, aPrefix,
whitelistEntry);
return uriClassifier->AsyncClassifyLocalWithTables(whitelistURI, tables, cb);
return uriClassifier->AsyncClassifyLocalWithTables(whitelistURI, trackingWhitelist, cb);
}
NS_IMETHODIMP
@ -823,7 +917,7 @@ nsChannelClassifier::OnClassifyCompleteInternal(nsresult aErrorCode,
if (aErrorCode == NS_ERROR_TRACKING_URI &&
!mTrackingProtectionEnabled.valueOr(false)) {
if (sAnnotateChannelEnabled) {
if (CachedPrefs::GetInstance()->IsAnnotateChannelEnabled()) {
nsCOMPtr<nsIParentChannel> parentChannel;
NS_QueryNotificationCallbacks(mChannel, parentChannel);
if (parentChannel) {
@ -837,7 +931,7 @@ nsChannelClassifier::OnClassifyCompleteInternal(nsresult aErrorCode,
}
}
if (sLowerNetworkPriority) {
if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
if (LOG_ENABLED()) {
nsCOMPtr<nsIURI> uri;
mChannel->GetURI(getter_AddRefs(uri));

View File

@ -432,7 +432,6 @@ nsIndexedToHTML::DoOnStartRequest(nsIRequest* request, nsISupports *aContext,
nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(innerUri));
//XXX bug 388553: can't use skinnable icons here due to security restrictions
if (fileURL) {
//buffer.AppendLiteral("chrome://global/skin/dirListing/local.png");
buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
"AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
"ZSBJbWFnZVJlYWR5ccllPAAAAjFJREFUeNqsU8uOElEQPffR"
@ -454,7 +453,6 @@ nsIndexedToHTML::DoOnStartRequest(nsIRequest* request, nsISupports *aContext,
"4pQ1%2FlPF0RGM9Ns91Wmptk0GfB4EJkt77vXYj%2F8m%2B8"
"y%2FkrwABHbz2H9V68DQAAAABJRU5ErkJggg%3D%3D");
} else {
//buffer.AppendLiteral("chrome://global/skin/dirListing/remote.png");
buffer.AppendLiteral("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB"
"AAAAAQCAYAAAAf8%2F9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i"
"ZSBJbWFnZVJlYWR5ccllPAAAAeBJREFUeNqcU81O20AQ%2Ft"

View File

@ -25,6 +25,7 @@ const CONFIG_PREFS = [
"identity.sync.tokenserver.uri",
"identity.fxaccounts.remote.webchannel.uri",
"identity.fxaccounts.settings.uri",
"identity.fxaccounts.settings.devices.uri",
"identity.fxaccounts.remote.signup.uri",
"identity.fxaccounts.remote.signin.uri",
"identity.fxaccounts.remote.force_auth.uri",
@ -153,6 +154,7 @@ this.FxAccountsConfig = {
Services.prefs.setCharPref("identity.fxaccounts.remote.webchannel.uri", rootURL);
Services.prefs.setCharPref("identity.fxaccounts.settings.uri", rootURL + "/settings?service=sync&context=" + contextParam);
Services.prefs.setCharPref("identity.fxaccounts.settings.devices.uri", rootURL + "/settings/clients?service=sync&context=" + contextParam);
Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", rootURL + "/signup?service=sync&context=" + contextParam);
Services.prefs.setCharPref("identity.fxaccounts.remote.signin.uri", rootURL + "/signin?service=sync&context=" + contextParam);
Services.prefs.setCharPref("identity.fxaccounts.remote.force_auth.uri", rootURL + "/force_auth?service=sync&context=" + contextParam);

View File

@ -639,7 +639,7 @@ class BookmarkRepairResponder extends CollectionRepairResponder {
let itemSource = engine.itemSource();
itemSource.ids = repairable.map(item => item.syncId);
log.trace(`checking the server for items`, itemSource.ids);
let itemsResponse = itemSource.get();
let itemsResponse = await itemSource.get();
// If the response failed, don't bother trying to parse the output.
// Throwing here means we abort the repair, which isn't ideal for transient
// errors (eg, no network, 500 service outage etc), but we don't currently

View File

@ -813,7 +813,7 @@ class BookmarkValidator {
return inspectionInfo;
}
_getServerState(engine) {
async _getServerState(engine) {
// XXXXX - todo - we need to capture last-modified of the server here and
// ensure the repairer only applys with if-unmodified-since that date.
let collection = engine.itemSource();
@ -824,7 +824,7 @@ class BookmarkValidator {
item.decrypt(collectionKey);
items.push(item.cleartext);
};
let resp = collection.getBatched();
let resp = await collection.getBatched();
if (!resp.success) {
throw resp;
}
@ -836,7 +836,7 @@ class BookmarkValidator {
let clientTree = await PlacesUtils.promiseBookmarksTree("", {
includeItemIds: true
});
let serverState = this._getServerState(engine);
let serverState = await this._getServerState(engine);
let serverRecordCount = serverState.length;
let result = await this.compareServerWithClient(serverState, clientTree);
let end = Date.now();

View File

@ -59,7 +59,7 @@ class CollectionValidator {
return new CollectionProblemData();
}
getServerItems(engine) {
async getServerItems(engine) {
let collection = engine.itemSource();
let collectionKey = engine.service.collectionKeys.keyForCollection(engine.name);
collection.full = true;
@ -68,7 +68,7 @@ class CollectionValidator {
item.decrypt(collectionKey);
items.push(item.cleartext);
};
let resp = collection.getBatched();
let resp = await collection.getBatched();
if (!resp.success) {
throw resp;
}

View File

@ -968,7 +968,7 @@ SyncEngine.prototype = {
_syncStartup() {
// Determine if we need to wipe on outdated versions
let metaGlobal = this.service.recordManager.get(this.metaURL);
let metaGlobal = Async.promiseSpinningly(this.service.recordManager.get(this.metaURL));
let engines = metaGlobal.payload.engines || {};
let engineData = engines[this.name] || {};
@ -1233,7 +1233,7 @@ SyncEngine.prototype = {
// Only bother getting data from the server if there's new things
if (this.lastModified == null || this.lastModified > this.lastSync) {
let resp = newitems.getBatched();
let resp = Async.promiseSpinningly(newitems.getBatched());
doApplyBatchAndPersistFailed.call(this);
if (!resp.success) {
resp.failureCode = ENGINE_DOWNLOAD_FAIL;
@ -1256,7 +1256,7 @@ SyncEngine.prototype = {
// index: Orders by the sortindex descending (highest weight first).
guidColl.sort = "index";
let guids = guidColl.get();
let guids = Async.promiseSpinningly(guidColl.get());
if (!guids.success)
throw guids;
@ -1289,7 +1289,7 @@ SyncEngine.prototype = {
newitems.ids = fetchBatch.slice(0, batchSize);
// Reuse the existing record handler set earlier
let resp = newitems.get();
let resp = Async.promiseSpinningly(newitems.get());
if (!resp.success) {
resp.failureCode = ENGINE_DOWNLOAD_FAIL;
throw resp;
@ -1760,7 +1760,7 @@ SyncEngine.prototype = {
let doDelete = Utils.bind2(this, function(key, val) {
let coll = new Collection(this.engineURL, this._recordObj, this.service);
coll[key] = val;
coll.delete();
Async.promiseSpinningly(coll.delete());
});
for (let [key, val] of Object.entries(this._delete)) {
@ -1826,7 +1826,7 @@ SyncEngine.prototype = {
// Any failure fetching/decrypting will just result in false
try {
this._log.trace("Trying to decrypt a record from the server..");
test.get();
Async.promiseSpinningly(test.get());
} catch (ex) {
if (Async.isShutdownException(ex)) {
throw ex;
@ -1844,14 +1844,14 @@ SyncEngine.prototype = {
},
wipeServer() {
let response = this.service.resource(this.engineURL).delete();
let response = Async.promiseSpinningly(this.service.resource(this.engineURL).delete());
if (response.status != 200 && response.status != 404) {
throw response;
}
this._resetClient();
},
removeClientData() {
async removeClientData() {
// Implement this method in engines that store client specific data
// on the server.
},

View File

@ -509,9 +509,9 @@ ClientEngine.prototype = {
);
},
removeClientData: function removeClientData() {
async removeClientData() {
let res = this.service.resource(this.engineURL + "/" + this.localID);
res.delete();
await res.delete();
},
// Override the default behavior to delete bad records from the server.

View File

@ -73,9 +73,9 @@ TabEngine.prototype = {
this.hasSyncedThisSession = false;
},
removeClientData() {
async removeClientData() {
let url = this.engineURL + "/" + this.service.clientsEngine.localID;
this.service.resource(url).delete();
await this.service.resource(url).delete();
},
/**

View File

@ -42,12 +42,12 @@ WBORecord.prototype = {
// Get thyself from your URI, then deserialize.
// Set thine 'response' field.
fetch: function fetch(resource) {
async fetch(resource) {
if (!(resource instanceof Resource)) {
throw new Error("First argument must be a Resource instance.");
}
let r = resource.get();
let r = await resource.get();
if (r.success) {
this.deserialize(r); // Warning! Muffles exceptions!
}
@ -55,7 +55,7 @@ WBORecord.prototype = {
return this;
},
upload: function upload(resource) {
upload(resource) {
if (!(resource instanceof Resource)) {
throw new Error("First argument must be a Resource instance.");
}
@ -223,12 +223,12 @@ RecordManager.prototype = {
_recordType: CryptoWrapper,
_logName: "Sync.RecordManager",
import: function RecordMgr_import(url) {
async import(url) {
this._log.trace("Importing record: " + (url.spec ? url.spec : url));
try {
// Clear out the last response with empty object if GET fails
this.response = {};
this.response = this.service.resource(url).get();
this.response = await this.service.resource(url).get();
// Don't parse and save the record on failure
if (!this.response.success)
@ -247,11 +247,11 @@ RecordManager.prototype = {
}
},
get: function RecordMgr_get(url) {
get(url) {
// Use a url string as the key to the hash
let spec = url.spec ? url.spec : url;
if (spec in this._records)
return this._records[spec];
return Promise.resolve(this._records[spec]);
return this.import(url);
},
@ -703,7 +703,7 @@ Collection.prototype = {
// Returns the last response processed, and doesn't run the record handler
// on any items if a non-success status is received while downloading the
// records (or if a network error occurs).
getBatched(batchSize = DEFAULT_DOWNLOAD_BATCH_SIZE) {
async getBatched(batchSize = DEFAULT_DOWNLOAD_BATCH_SIZE) {
let totalLimit = Number(this.limit) || Infinity;
if (batchSize <= 0 || batchSize >= totalLimit) {
// Invalid batch sizes should arguably be an error, but they're easy to handle
@ -733,7 +733,7 @@ Collection.prototype = {
}
this._log.trace("Performing batched GET", { limit: this.limit, offset: this.offset });
// Actually perform the request
resp = this.get();
resp = await this.get();
if (!resp.success) {
break;
}
@ -996,7 +996,8 @@ PostQueue.prototype = {
}
this.queued = "";
this.numQueued = 0;
let response = this.poster(queued, headers, batch, !!(finalBatchPost && this.batchID !== null));
let response = Async.promiseSpinningly(
this.poster(queued, headers, batch, !!(finalBatchPost && this.batchID !== null)));
if (!response.success) {
this.log.trace("Server error response during a batch", response);

View File

@ -133,7 +133,8 @@ AsyncResource.prototype = {
// through. It is never called directly, only {{{_doRequest}}} uses it
// to obtain a request channel.
//
_createRequest: function Res__createRequest(method) {
_createRequest(method) {
this.method = method;
let channel = NetUtil.newChannel({uri: this.spec, loadUsingSystemPrincipal: true})
.QueryInterface(Ci.nsIRequest)
.QueryInterface(Ci.nsIHttpChannel);
@ -172,55 +173,55 @@ AsyncResource.prototype = {
return channel;
},
_onProgress: function Res__onProgress(channel) {},
_onProgress(channel) {},
_doRequest: function _doRequest(action, data, callback) {
_doRequest(action, data) {
this._log.trace("In _doRequest.");
this._callback = callback;
let channel = this._createRequest(action);
return new Promise((resolve, reject) => {
this._deferred = { resolve, reject };
let channel = this._createRequest(action);
if ("undefined" != typeof(data))
this._data = data;
if ("undefined" != typeof(data))
this._data = data;
// PUT and POST are treated differently because they have payload data.
if ("PUT" == action || "POST" == action) {
// Convert non-string bodies into JSON
if (this._data.constructor.toString() != String)
this._data = JSON.stringify(this._data);
// PUT and POST are treated differently because they have payload data.
if ("PUT" == action || "POST" == action) {
// Convert non-string bodies into JSON
if (this._data.constructor.toString() != String)
this._data = JSON.stringify(this._data);
this._log.debug(action + " Length: " + this._data.length);
this._log.trace(action + " Body: " + this._data);
this._log.debug(action + " Length: " + this._data.length);
this._log.trace(action + " Body: " + this._data);
let type = ("content-type" in this._headers) ?
this._headers["content-type"] : "text/plain";
let type = ("content-type" in this._headers) ?
this._headers["content-type"] : "text/plain";
let stream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
stream.setData(this._data, this._data.length);
let stream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
stream.setData(this._data, this._data.length);
channel.QueryInterface(Ci.nsIUploadChannel);
channel.setUploadStream(stream, type, this._data.length);
}
channel.QueryInterface(Ci.nsIUploadChannel);
channel.setUploadStream(stream, type, this._data.length);
}
// Setup a channel listener so that the actual network operation
// is performed asynchronously.
let listener = new ChannelListener(this._onComplete, this._onProgress,
this._log, this.ABORT_TIMEOUT);
channel.requestMethod = action;
try {
// Setup a channel listener so that the actual network operation
// is performed asynchronously.
let listener = new ChannelListener(this._onComplete, this._onProgress,
this._log, this.ABORT_TIMEOUT);
channel.requestMethod = action;
channel.asyncOpen2(listener);
} catch (ex) {
// asyncOpen2 can throw in a bunch of cases -- e.g., a forbidden port.
this._log.warn("Caught an error in asyncOpen2", ex);
CommonUtils.nextTick(callback.bind(this, ex));
}
});
},
_onComplete: function _onComplete(error, data, channel) {
this._log.trace("In _onComplete. Error is " + error + ".");
_onComplete(ex, data, channel) {
this._log.trace("In _onComplete. Error is " + ex + ".");
if (error) {
this._callback(error);
if (ex) {
if (!Async.isShutdownException(ex)) {
this._log.warn("${action} request to ${url} failed: ${ex}",
{ action: this.method, url: this.uri.spec, ex});
}
this._deferred.reject(ex);
return;
}
@ -324,117 +325,29 @@ AsyncResource.prototype = {
}
}.bind(this));
this._callback(null, ret);
this._deferred.resolve(ret);
},
get: function get(callback) {
this._doRequest("GET", undefined, callback);
get() {
return this._doRequest("GET", undefined);
},
put: function put(data, callback) {
if (typeof data == "function")
[data, callback] = [undefined, data];
this._doRequest("PUT", data, callback);
put(data) {
return this._doRequest("PUT", data);
},
post: function post(data, callback) {
if (typeof data == "function")
[data, callback] = [undefined, data];
this._doRequest("POST", data, callback);
post(data) {
return this._doRequest("POST", data);
},
delete: function delete_(callback) {
this._doRequest("DELETE", undefined, callback);
delete() {
return this._doRequest("DELETE", undefined);
}
};
/*
* Represent a remote network resource, identified by a URI, with a
* synchronous API.
*
* 'Resource' is not recommended for new code. Use the asynchronous API of
* 'AsyncResource' instead.
*/
this.Resource = function Resource(uri) {
AsyncResource.call(this, uri);
}
Resource.prototype = {
__proto__: AsyncResource.prototype,
_logName: "Sync.Resource",
// ** {{{ Resource._request }}} **
//
// Perform a particular HTTP request on the resource. This method
// is never called directly, but is used by the high-level
// {{{get}}}, {{{put}}}, {{{post}}} and {{delete}} methods.
_request: function Res__request(action, data) {
let cb = Async.makeSyncCallback();
function callback(error, ret) {
if (error)
cb.throw(error);
else
cb(ret);
}
// The channel listener might get a failure code
try {
this._doRequest(action, data, callback);
return Async.waitForSyncCallback(cb);
} catch (ex) {
if (Async.isShutdownException(ex)) {
throw ex;
}
this._log.warn("${action} request to ${url} failed: ${ex}",
{ action, url: this.uri.spec, ex });
// Combine the channel stack with this request stack. Need to create
// a new error object for that.
let error = Error(ex.message);
error.result = ex.result;
let chanStack = [];
if (ex.stack)
chanStack = ex.stack.trim().split(/\n/).slice(1);
let requestStack = error.stack.split(/\n/).slice(1);
// Strip out the args for the last 2 frames because they're usually HUGE!
for (let i = 0; i <= 1; i++)
requestStack[i] = requestStack[i].replace(/\(".*"\)@/, "(...)@");
error.stack = chanStack.concat(requestStack).join("\n");
throw error;
}
},
// ** {{{ Resource.get }}} **
//
// Perform an asynchronous HTTP GET for this resource.
get: function Res_get() {
return this._request("GET");
},
// ** {{{ Resource.put }}} **
//
// Perform a HTTP PUT for this resource.
put: function Res_put(data) {
return this._request("PUT", data);
},
// ** {{{ Resource.post }}} **
//
// Perform a HTTP POST for this resource.
post: function Res_post(data) {
return this._request("POST", data);
},
// ** {{{ Resource.delete }}} **
//
// Perform a HTTP DELETE for this resource.
delete: function Res_delete() {
return this._request("DELETE");
}
};
// TODO: We still export both "Resource" and "AsyncRecourse" as the same
// object, but we should decide on one and unify all references.
this.Resource = AsyncResource;
// = ChannelListener =
//

View File

@ -206,7 +206,7 @@ Sync11Service.prototype = {
// Fetch keys.
let cryptoKeys = new CryptoWrapper(CRYPTO_COLLECTION, KEYS_WBO);
try {
let cryptoResp = cryptoKeys.fetch(this.resource(this.cryptoKeysURL)).response;
let cryptoResp = Async.promiseSpinningly(cryptoKeys.fetch(this.resource(this.cryptoKeysURL))).response;
// Save out the ciphertext for when we reupload. If there's a bug in
// CollectionKeyManager, this will prevent us from uploading junk.
@ -465,7 +465,7 @@ Sync11Service.prototype = {
this._log.trace("In _fetchInfo: " + infoURL);
let info;
try {
info = this.resource(infoURL).get();
info = Async.promiseSpinningly(this.resource(infoURL).get());
} catch (ex) {
this.errorHandler.checkServerError(ex);
throw ex;
@ -518,7 +518,7 @@ Sync11Service.prototype = {
if (infoCollections && (CRYPTO_COLLECTION in infoCollections)) {
try {
cryptoKeys = new CryptoWrapper(CRYPTO_COLLECTION, KEYS_WBO);
let cryptoResp = cryptoKeys.fetch(this.resource(this.cryptoKeysURL)).response;
let cryptoResp = Async.promiseSpinningly(cryptoKeys.fetch(this.resource(this.cryptoKeysURL))).response;
if (cryptoResp.success) {
this.handleFetchedKeys(syncKeyBundle, cryptoKeys);
@ -613,7 +613,7 @@ Sync11Service.prototype = {
}
// Fetch collection info on every startup.
let test = this.resource(this.infoURL).get();
let test = Async.promiseSpinningly(this.resource(this.infoURL).get());
switch (test.status) {
case 200:
@ -717,7 +717,7 @@ Sync11Service.prototype = {
// Download and install them.
let cryptoKeys = new CryptoWrapper(CRYPTO_COLLECTION, KEYS_WBO);
let cryptoResp = cryptoKeys.fetch(this.resource(this.cryptoKeysURL)).response;
let cryptoResp = Async.promiseSpinningly(cryptoKeys.fetch(this.resource(this.cryptoKeysURL))).response;
if (cryptoResp.status != 200) {
this._log.warn("Failed to download keys.");
throw new Error("Symmetric key download failed.");
@ -739,7 +739,11 @@ Sync11Service.prototype = {
// Clear client-specific data from the server, including disabled engines.
for (let engine of [this.clientsEngine].concat(this.engineManager.getAll())) {
try {
engine.removeClientData();
// Note the additional Promise.resolve here is to handle the fact that
// some 3rd party engines probably don't return a promise. We can
// probably nuke this once webextensions become mandatory as then
// no 3rd party engines will be allowed to exist.
Async.promiseSpinningly(Promise.resolve().then(() => engine.removeClientData()));
} catch (ex) {
this._log.warn(`Deleting client data for ${engine.name} failed`, ex);
}
@ -780,7 +784,7 @@ Sync11Service.prototype = {
this._clusterManager = this.identity.createClusterManager(this);
Svc.Obs.notify("weave:service:start-over:finish");
} catch (err) {
this._log.error("startOver failed to re-initialize the identity manager: " + err);
this._log.error("startOver failed to re-initialize the identity manager", err);
// Still send the observer notification so the current state is
// reflected in the UI.
Svc.Obs.notify("weave:service:start-over:finish");
@ -847,7 +851,7 @@ Sync11Service.prototype = {
this._log.debug("Fetching server configuration", infoURL);
let configResponse;
try {
configResponse = this.resource(infoURL).get();
configResponse = Async.promiseSpinningly(this.resource(infoURL).get());
} catch (ex) {
// This is probably a network or similar error.
this._log.warn("Failed to fetch info/configuration", ex);
@ -877,7 +881,7 @@ Sync11Service.prototype = {
}
this._log.debug("Fetching global metadata record");
let meta = this.recordManager.get(this.metaURL);
let meta = Async.promiseSpinningly(this.recordManager.get(this.metaURL));
// Checking modified time of the meta record.
if (infoResponse &&
@ -892,7 +896,7 @@ Sync11Service.prototype = {
this.recordManager.del(this.metaURL);
// ... fetch the current record from the server, and COPY THE FLAGS.
let newMeta = this.recordManager.get(this.metaURL);
let newMeta = Async.promiseSpinningly(this.recordManager.get(this.metaURL));
// If we got a 401, we do not want to create a new meta/global - we
// should be able to get the existing meta after we get a new node.
@ -1092,7 +1096,7 @@ Sync11Service.prototype = {
// Now let's update our declined engines (but only if we have a metaURL;
// if Sync failed due to no node we will not have one)
if (this.metaURL) {
let meta = this.recordManager.get(this.metaURL);
let meta = Async.promiseSpinningly(this.recordManager.get(this.metaURL));
if (!meta) {
this._log.warn("No meta/global; can't update declined state.");
return;
@ -1134,7 +1138,7 @@ Sync11Service.prototype = {
this._log.debug("Uploading meta/global", meta);
let res = this.resource(this.metaURL);
res.setHeader("X-If-Unmodified-Since", meta.modified);
let response = res.put(meta);
let response = Async.promiseSpinningly(res.put(meta));
if (!response.success) {
throw response;
}
@ -1154,7 +1158,7 @@ Sync11Service.prototype = {
this._log.debug(`Uploading crypto/keys (lastModified: ${lastModified})`);
let res = this.resource(this.cryptoKeysURL);
res.setHeader("X-If-Unmodified-Since", lastModified);
return res.put(cryptoKeys);
return Async.promiseSpinningly(res.put(cryptoKeys));
},
_freshStart: function _freshStart() {
@ -1197,7 +1201,7 @@ Sync11Service.prototype = {
let res = this.resource(this.storageURL.slice(0, -1));
res.setHeader("X-Confirm-Delete", "1");
try {
response = res.delete();
response = Async.promiseSpinningly(res.delete());
} catch (ex) {
this._log.debug("Failed to wipe server", ex);
histogram.add(false);
@ -1217,7 +1221,7 @@ Sync11Service.prototype = {
for (let name of collections) {
let url = this.storageURL + name;
try {
response = this.resource(url).delete();
response = Async.promiseSpinningly(this.resource(url).delete());
} catch (ex) {
this._log.debug("Failed to wipe '" + name + "' collection", ex);
histogram.add(false);

View File

@ -180,7 +180,7 @@ EngineSynchronizer.prototype = {
}
// Upload meta/global if any engines changed anything.
let meta = this.service.recordManager.get(this.service.metaURL);
let meta = Async.promiseSpinningly(this.service.recordManager.get(this.service.metaURL));
if (meta.isNew || meta.changed) {
this._log.info("meta/global changed locally: reuploading.");
try {
@ -342,7 +342,7 @@ EngineSynchronizer.prototype = {
},
_updateEnabledEngines() {
let meta = this.service.recordManager.get(this.service.metaURL);
let meta = Async.promiseSpinningly(this.service.recordManager.get(this.service.metaURL));
let numClients = this.service.scheduler.numClients;
let engineManager = this.service.engineManager;

View File

@ -100,7 +100,7 @@ const EHTestsCommon = {
let newSyncKeyBundle = new SyncKeyBundle("johndoe", "23456234562345623456234562");
let keys = Service.collectionKeys.asWBO();
keys.encrypt(newSyncKeyBundle);
keys.upload(Service.resource(Service.cryptoKeysURL));
return keys.upload(Service.resource(Service.cryptoKeysURL));
},
async setUp(server) {
@ -108,10 +108,11 @@ const EHTestsCommon = {
return EHTestsCommon.generateAndUploadKeys()
},
generateAndUploadKeys() {
async generateAndUploadKeys() {
generateNewKeys(Service.collectionKeys);
let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.identity.syncKeyBundle);
return serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success;
let response = await serverKeys.upload(Service.resource(Service.cryptoKeysURL));
return response.success;
}
};

View File

@ -723,7 +723,7 @@ add_task(async function test_misreconciled_root() {
getBatched() {
return this.get();
},
get() {
async get() {
this.recordHandler(encrypted);
return {success: true}
},

View File

@ -92,11 +92,11 @@ add_task(async function test_bad_hmac() {
return !wbo || !wbo.payload;
}
function uploadNewKeys() {
async function uploadNewKeys() {
generateNewKeys(Service.collectionKeys);
let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.identity.syncKeyBundle);
ok(serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success);
ok((await serverKeys.upload(Service.resource(Service.cryptoKeysURL))).success);
}
try {
@ -126,7 +126,7 @@ add_task(async function test_bad_hmac() {
generateNewKeys(Service.collectionKeys);
let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.identity.syncKeyBundle);
ok(serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success);
ok((await serverKeys.upload(Service.resource(Service.cryptoKeysURL))).success);
_("Sync.");
engine._sync();
@ -161,14 +161,14 @@ add_task(async function test_bad_hmac() {
deletedItems = [];
check_clients_count(0);
uploadNewKeys();
await uploadNewKeys();
// Sync once to upload a record.
engine._sync();
check_clients_count(1);
// Generate and upload new keys, so the old client record is wrong.
uploadNewKeys();
await uploadNewKeys();
// Create a new client record and new keys. Now our keys are wrong, as well
// as the object on the server. We'll download the new keys and also delete
@ -317,7 +317,7 @@ add_task(async function test_sync() {
ok(engine.lastRecordUpload > lastweek);
_("Remove client record.");
engine.removeClientData();
await engine.removeClientData();
equal(clientWBO().payload, undefined);
_("Time travel one day back, no record uploaded.");

View File

@ -26,7 +26,7 @@ function get_test_collection_info({ totalRecords, batchSize, lastModified,
let requests = [];
let responses = [];
let sawRecord = false;
coll.get = function() {
coll.get = async function() {
ok(!sawRecord); // make sure we call record handler after all requests.
let limit = +this.limit;
let offset = 0;
@ -76,7 +76,7 @@ function get_test_collection_info({ totalRecords, batchSize, lastModified,
return { records, responses, requests, coll };
}
add_test(function test_success() {
add_task(async function test_success() {
const totalRecords = 11;
const batchSize = 2;
const lastModified = "111111";
@ -85,7 +85,7 @@ add_test(function test_success() {
batchSize,
lastModified,
});
let response = coll.getBatched(batchSize);
let response = await coll.getBatched(batchSize);
equal(requests.length, Math.ceil(totalRecords / batchSize));
@ -116,11 +116,9 @@ add_test(function test_success() {
ok(!coll._headers["x-if-unmodified-since"]);
ok(!coll.offset);
ok(!coll.limit || (coll.limit == Infinity));
run_next_test();
});
add_test(function test_total_limit() {
add_task(async function test_total_limit() {
_("getBatched respects the (initial) value of the limit property");
const totalRecords = 100;
const recordLimit = 11;
@ -132,7 +130,7 @@ add_test(function test_total_limit() {
lastModified,
});
coll.limit = recordLimit;
coll.getBatched(batchSize);
await coll.getBatched(batchSize);
equal(requests.length, Math.ceil(recordLimit / batchSize));
equal(records.length, recordLimit);
@ -147,11 +145,9 @@ add_test(function test_total_limit() {
}
equal(coll._limit, recordLimit);
run_next_test();
});
add_test(function test_412() {
add_task(async function test_412() {
_("We shouldn't record records if we get a 412 in the middle of a batch");
const totalRecords = 11;
const batchSize = 2;
@ -162,7 +158,7 @@ add_test(function test_412() {
lastModified,
interruptedAfter: 3
});
let response = coll.getBatched(batchSize);
let response = await coll.getBatched(batchSize);
equal(requests.length, 3);
equal(records.length, 0); // record handler shouldn't be called for anything
@ -172,10 +168,9 @@ add_test(function test_412() {
ok(!response.success);
equal(response.status, 412);
run_next_test();
});
add_test(function test_get_throws() {
add_task(async function test_get_throws() {
_("We shouldn't record records if get() throws for some reason");
const totalRecords = 11;
const batchSize = 2;
@ -187,9 +182,8 @@ add_test(function test_get_throws() {
throwAfter: 3
});
throws(() => coll.getBatched(batchSize), "Some Network Error");
await Assert.rejects(coll.getBatched(batchSize), "Some Network Error");
equal(requests.length, 3);
equal(records.length, 0);
run_next_test();
});

View File

@ -71,7 +71,7 @@ add_task(async function test_locally_changed_keys() {
let m = new WBORecord("meta", "global");
m.payload = {"syncID": "foooooooooooooooooooooooooo",
"storageVersion": STORAGE_VERSION};
m.upload(Service.resource(Service.metaURL));
await m.upload(Service.resource(Service.metaURL));
_("New meta/global: " + JSON.stringify(johndoe.collection("meta").wbo("global")));
@ -79,7 +79,7 @@ add_task(async function test_locally_changed_keys() {
generateNewKeys(Service.collectionKeys);
let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.identity.syncKeyBundle);
do_check_true(serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success);
do_check_true((await serverKeys.upload(Service.resource(Service.cryptoKeysURL))).success);
// Check that login works.
do_check_true(Service.login());
@ -123,7 +123,7 @@ add_task(async function test_locally_changed_keys() {
// Check that we can decrypt one.
let rec = new CryptoWrapper("history", "record-no--0");
rec.fetch(Service.resource(Service.storageURL + "history/record-no--0"));
await rec.fetch(Service.resource(Service.storageURL + "history/record-no--0"));
_(JSON.stringify(rec));
do_check_true(!!rec.decrypt(liveKeys));

View File

@ -120,7 +120,7 @@ add_task(async function test_credentials_changed_logout() {
do_check_eq(Status.sync, SYNC_SUCCEEDED);
do_check_true(Service.isLoggedIn);
EHTestsCommon.generateCredentialsChangedFailure();
await EHTestsCommon.generateCredentialsChangedFailure();
let ping = await sync_and_validate_telem(true);
equal(ping.status.sync, CREDENTIALS_CHANGED);
@ -397,7 +397,7 @@ add_task(async function test_sync_syncAndReportErrors_non_network_error() {
do_check_eq(Status.sync, SYNC_SUCCEEDED);
do_check_true(Service.isLoggedIn);
EHTestsCommon.generateCredentialsChangedFailure();
await EHTestsCommon.generateCredentialsChangedFailure();
let promiseObserved = promiseOneObserver("weave:ui:sync:error");
@ -450,7 +450,7 @@ add_task(async function test_sync_syncAndReportErrors_prolonged_non_network_erro
do_check_eq(Status.sync, SYNC_SUCCEEDED);
do_check_true(Service.isLoggedIn);
EHTestsCommon.generateCredentialsChangedFailure();
await EHTestsCommon.generateCredentialsChangedFailure();
let promiseObserved = promiseOneObserver("weave:ui:sync:error");
@ -579,7 +579,7 @@ add_task(async function test_sync_prolonged_non_network_error() {
do_check_eq(Status.sync, SYNC_SUCCEEDED);
do_check_true(Service.isLoggedIn);
EHTestsCommon.generateCredentialsChangedFailure();
await EHTestsCommon.generateCredentialsChangedFailure();
let promiseObserved = promiseOneObserver("weave:ui:sync:error");
@ -668,7 +668,7 @@ add_task(async function test_sync_non_network_error() {
do_check_eq(Status.sync, SYNC_SUCCEEDED);
do_check_true(Service.isLoggedIn);
EHTestsCommon.generateCredentialsChangedFailure();
await EHTestsCommon.generateCredentialsChangedFailure();
let promiseObserved = promiseOneObserver("weave:ui:sync:error");

View File

@ -57,12 +57,12 @@ async function setUp(server) {
new FakeCryptoService();
}
function generateAndUploadKeys(server) {
async function generateAndUploadKeys(server) {
generateNewKeys(Service.collectionKeys);
let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.identity.syncKeyBundle);
let res = Service.resource(server.baseURI + "/1.1/johndoe/storage/crypto/keys");
return serverKeys.upload(res).success;
return (await serverKeys.upload(res)).success;
}
@ -81,7 +81,7 @@ add_task(async function test_backoff500() {
do_check_false(Status.enforceBackoff);
// Forcibly create and upload keys here -- otherwise we don't get to the 500!
do_check_true(generateAndUploadKeys(server));
do_check_true(await generateAndUploadKeys(server));
Service.login();
Service.sync();
@ -116,7 +116,7 @@ add_task(async function test_backoff503() {
try {
do_check_false(Status.enforceBackoff);
do_check_true(generateAndUploadKeys(server));
do_check_true(await generateAndUploadKeys(server));
Service.login();
Service.sync();
@ -150,7 +150,7 @@ add_task(async function test_overQuota() {
try {
do_check_eq(Status.sync, SYNC_SUCCEEDED);
do_check_true(generateAndUploadKeys(server));
do_check_true(await generateAndUploadKeys(server));
Service.login();
Service.sync();
@ -230,7 +230,7 @@ add_task(async function test_engine_networkError() {
try {
do_check_eq(Status.sync, SYNC_SUCCEEDED);
do_check_true(generateAndUploadKeys(server));
do_check_true(await generateAndUploadKeys(server));
Service.login();
Service.sync();
@ -259,7 +259,7 @@ add_task(async function test_resource_timeout() {
try {
do_check_eq(Status.sync, SYNC_SUCCEEDED);
do_check_true(generateAndUploadKeys(server));
do_check_true(await generateAndUploadKeys(server));
Service.login();
Service.sync();

View File

@ -22,7 +22,7 @@ function makePostQueue(config, lastModTime, responseGenerator) {
thisPost.headers = headers;
}
stats.posts.push(thisPost);
return responseGenerator.next().value;
return Promise.resolve(responseGenerator.next().value);
}
let done = () => {}

View File

@ -7,8 +7,9 @@ Cu.import("resource://services-sync/service.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://testing-common/services/sync/utils.js");
initTestLogging("Trace");
function test_toJSON() {
add_test(function test_toJSON() {
_("Create a record, for now without a TTL.");
let wbo = new WBORecord("coll", "a_record");
wbo.modified = 12345;
@ -26,10 +27,11 @@ function test_toJSON() {
wbo.ttl = 30 * 60;
json = JSON.parse(JSON.stringify(wbo));
do_check_eq(json.ttl, 30 * 60);
}
run_next_test();
});
function test_fetch() {
add_task(async function test_fetch() {
let record = {id: "asdf-1234-asdf-1234",
modified: 2454725.98283,
payload: JSON.stringify({cheese: "roquefort"})};
@ -46,12 +48,11 @@ function test_fetch() {
"/record2": httpd_handler(200, "OK", JSON.stringify(record2)),
"/coll": httpd_handler(200, "OK", JSON.stringify(coll))
});
do_test_pending();
try {
_("Fetching a WBO record");
let rec = new WBORecord("coll", "record");
rec.fetch(Service.resource(server.baseURI + "/record"));
await rec.fetch(Service.resource(server.baseURI + "/record"));
do_check_eq(rec.id, "asdf-1234-asdf-1234"); // NOT "record"!
do_check_eq(rec.modified, 2454725.98283);
@ -59,7 +60,7 @@ function test_fetch() {
do_check_eq(rec.payload.cheese, "roquefort");
_("Fetching a WBO record using the record manager");
let rec2 = Service.recordManager.get(server.baseURI + "/record2");
let rec2 = await Service.recordManager.get(server.baseURI + "/record2");
do_check_eq(rec2.id, "record2");
do_check_eq(rec2.modified, 2454725.98284);
do_check_eq(typeof(rec2.payload), "object");
@ -72,13 +73,6 @@ function test_fetch() {
do_check_eq(rec3.collection, "tabs");
} finally {
server.stop(do_test_finished);
await promiseStopServer(server);
}
}
function run_test() {
initTestLogging("Trace");
test_toJSON();
test_fetch();
}
});

View File

@ -148,11 +148,9 @@ function server_headers(metadata, response) {
response.bodyOutputStream.write(body, body.length);
}
function run_test() {
add_task(async function test() {
initTestLogging("Trace");
do_test_pending();
let logger = Log.repository.getLogger("Test");
Log.repository.rootLogger.addAppender(new Log.DumpAppender());
@ -179,7 +177,7 @@ function run_test() {
PACSystemSettings.PACURI = server.baseURI + "/pac1";
installFakePAC();
let proxiedRes = new Resource(server.baseURI + "/open");
let content = proxiedRes.get();
let content = await proxiedRes.get();
do_check_true(pacFetched);
do_check_true(fetched);
do_check_eq(content, "This path exists");
@ -198,7 +196,7 @@ function run_test() {
do_check_eq(res.data, null);
_("GET a non-password-protected resource");
content = res.get();
content = await res.get();
do_check_eq(content, "This path exists");
do_check_eq(content.status, 200);
do_check_true(content.success);
@ -231,7 +229,7 @@ function run_test() {
_("GET a password protected resource (test that it'll fail w/o pass, no throw)");
let res2 = new Resource(server.baseURI + "/protected");
content = res2.get();
content = await res2.get();
do_check_eq(content, "This path exists and is protected - failed");
do_check_eq(content.status, 401);
do_check_false(content.success);
@ -244,14 +242,14 @@ function run_test() {
let auth = browseridManager.getResourceAuthenticator();
res3.authenticator = auth;
do_check_eq(res3.authenticator, auth);
content = res3.get();
content = await res3.get();
do_check_eq(content, "This path exists and is protected");
do_check_eq(content.status, 200);
do_check_true(content.success);
_("GET a non-existent resource (test that it'll fail, but not throw)");
let res4 = new Resource(server.baseURI + "/404");
content = res4.get();
content = await res4.get();
do_check_eq(content, "File not found");
do_check_eq(content.status, 404);
do_check_false(content.success);
@ -263,66 +261,66 @@ function run_test() {
_("PUT to a resource (string)");
let res5 = new Resource(server.baseURI + "/upload");
content = res5.put(JSON.stringify(sample_data));
content = await res5.put(JSON.stringify(sample_data));
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res5.data, content);
_("PUT to a resource (object)");
content = res5.put(sample_data);
content = await res5.put(sample_data);
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res5.data, content);
_("PUT without data arg (uses resource.data) (string)");
res5.data = JSON.stringify(sample_data);
content = res5.put();
content = await res5.put();
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res5.data, content);
_("PUT without data arg (uses resource.data) (object)");
res5.data = sample_data;
content = res5.put();
content = await res5.put();
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res5.data, content);
_("POST to a resource (string)");
content = res5.post(JSON.stringify(sample_data));
content = await res5.post(JSON.stringify(sample_data));
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res5.data, content);
_("POST to a resource (object)");
content = res5.post(sample_data);
content = await res5.post(sample_data);
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res5.data, content);
_("POST without data arg (uses resource.data) (string)");
res5.data = JSON.stringify(sample_data);
content = res5.post();
content = await res5.post();
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res5.data, content);
_("POST without data arg (uses resource.data) (object)");
res5.data = sample_data;
content = res5.post();
content = await res5.post();
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res5.data, content);
_("DELETE a resource");
let res6 = new Resource(server.baseURI + "/delete");
content = res6.delete();
content = await res6.delete();
do_check_eq(content, "This resource has been deleted")
do_check_eq(content.status, 200);
_("JSON conversion of response body");
let res7 = new Resource(server.baseURI + "/json");
content = res7.get();
content = await res7.get();
do_check_eq(content, JSON.stringify(sample_data));
do_check_eq(content.status, 200);
do_check_eq(JSON.stringify(content.obj), JSON.stringify(sample_data));
@ -332,26 +330,26 @@ function run_test() {
// X-Weave-Timestamp header, AsyncResource.serverTime is null.
do_check_eq(AsyncResource.serverTime, null);
let res8 = new Resource(server.baseURI + "/timestamp");
content = res8.get();
content = await res8.get();
do_check_eq(AsyncResource.serverTime, TIMESTAMP);
_("GET: no special request headers");
let res9 = new Resource(server.baseURI + "/headers");
content = res9.get();
content = await res9.get();
do_check_eq(content, "{}");
_("PUT: Content-Type defaults to text/plain");
content = res9.put("data");
content = await res9.put("data");
do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
_("POST: Content-Type defaults to text/plain");
content = res9.post("data");
content = await res9.post("data");
do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
_("setHeader(): setting simple header");
res9.setHeader("X-What-Is-Weave", "awesome");
do_check_eq(res9.headers["x-what-is-weave"], "awesome");
content = res9.get();
content = await res9.get();
do_check_eq(content, JSON.stringify({"x-what-is-weave": "awesome"}));
_("setHeader(): setting multiple headers, overwriting existing header");
@ -359,21 +357,21 @@ function run_test() {
res9.setHeader("X-Another-Header", "hello world");
do_check_eq(res9.headers["x-what-is-weave"], "more awesomer");
do_check_eq(res9.headers["x-another-header"], "hello world");
content = res9.get();
content = await res9.get();
do_check_eq(content, JSON.stringify({"x-another-header": "hello world",
"x-what-is-weave": "more awesomer"}));
_("Setting headers object");
res9.headers = {};
content = res9.get();
content = await res9.get();
do_check_eq(content, "{}");
_("PUT/POST: override default Content-Type");
res9.setHeader("Content-Type", "application/foobar");
do_check_eq(res9.headers["content-type"], "application/foobar");
content = res9.put("data");
content = await res9.put("data");
do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
content = res9.post("data");
content = await res9.post("data");
do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
@ -385,7 +383,7 @@ function run_test() {
Observers.add("weave:service:backoff:interval", onBackoff);
let res10 = new Resource(server.baseURI + "/backoff");
content = res10.get();
content = await res10.get();
do_check_eq(backoffInterval, 600);
@ -397,12 +395,12 @@ function run_test() {
Observers.add("weave:service:quota:remaining", onQuota);
res10 = new Resource(server.baseURI + "/quota-error");
content = res10.get();
content = await res10.get();
do_check_eq(content.status, 400);
do_check_eq(quotaValue, undefined); // HTTP 400, so no observer notification.
res10 = new Resource(server.baseURI + "/quota-notice");
content = res10.get();
content = await res10.get();
do_check_eq(content.status, 200);
do_check_eq(quotaValue, 1048576);
@ -411,7 +409,7 @@ function run_test() {
let error;
let res11 = new Resource("http://localhost:12345/does/not/exist");
try {
content = res11.get();
content = await res11.get();
} catch (ex) {
error = ex;
}
@ -430,14 +428,14 @@ function run_test() {
res18._log.warn = function(msg) { warnings.push(msg) };
error = undefined;
try {
content = res18.get();
content = await res18.get();
} catch (ex) {
error = ex;
}
// It throws and logs.
do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI);
do_check_eq(error, "Error: NS_ERROR_MALFORMED_URI");
do_check_eq(error.message, "NS_ERROR_MALFORMED_URI");
// Note the strings haven't been formatted yet, but that's OK for this test.
do_check_eq(warnings.pop(), "${action} request to ${url} failed: ${ex}");
do_check_eq(warnings.pop(),
@ -455,14 +453,14 @@ function run_test() {
res18._log.warn = function(msg) { warnings.push(msg) };
error = undefined;
try {
content = res18.get();
content = await res18.get();
} catch (ex) {
error = ex;
}
// It throws and logs.
do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
do_check_eq(error, "Error: NS_ERROR_XPC_JS_THREW_STRING");
do_check_eq(error.message, "NS_ERROR_XPC_JS_THREW_STRING");
do_check_eq(warnings.pop(), "${action} request to ${url} failed: ${ex}");
do_check_eq(warnings.pop(),
"Got exception calling onProgress handler during fetch of " +
@ -475,7 +473,7 @@ function run_test() {
res19.ABORT_TIMEOUT = 0;
error = undefined;
try {
content = res19.get();
content = await res19.get();
} catch (ex) {
error = ex;
}
@ -496,4 +494,4 @@ function run_test() {
uri2.query = query;
do_check_eq(uri1.query, uri2.query);
server.stop(do_test_finished);
}
});

View File

@ -163,7 +163,7 @@ function run_test() {
// This apparently has to come first in order for our PAC URL to be hit.
// Don't put any other HTTP requests earlier in the file!
add_test(function test_proxy_auth_redirect() {
add_task(async function test_proxy_auth_redirect() {
_("Ensure that a proxy auth redirect (which switches out our channel) " +
"doesn't break AsyncResource.");
let server = httpd_setup({
@ -174,18 +174,16 @@ add_test(function test_proxy_auth_redirect() {
PACSystemSettings.PACURI = server.baseURI + "/pac2";
installFakePAC();
let res = new AsyncResource(server.baseURI + "/open");
res.get(function(error, result) {
do_check_true(!error);
do_check_true(pacFetched);
do_check_true(fetched);
do_check_eq("This path exists", result);
pacFetched = fetched = false;
uninstallFakePAC();
server.stop(run_next_test);
});
let result = await res.get();
do_check_true(pacFetched);
do_check_true(fetched);
do_check_eq("This path exists", result);
pacFetched = fetched = false;
uninstallFakePAC();
await promiseStopServer(server);
});
add_test(function test_new_channel() {
add_task(async function test_new_channel() {
_("Ensure a redirect to a new channel is handled properly.");
let resourceRequested = false;
@ -210,15 +208,13 @@ add_test(function test_new_channel() {
locationURL = server.baseURI + "/resource";
let request = new AsyncResource(server.baseURI + "/redirect");
request.get(function onRequest(error, content) {
do_check_null(error);
do_check_true(resourceRequested);
do_check_eq(200, content.status);
do_check_true("content-type" in content.headers);
do_check_eq("text/plain", content.headers["content-type"]);
let content = await request.get()
do_check_true(resourceRequested);
do_check_eq(200, content.status);
do_check_true("content-type" in content.headers);
do_check_eq("text/plain", content.headers["content-type"]);
server.stop(run_next_test);
});
await promiseStopServer(server);
});
@ -259,42 +255,38 @@ add_test(function test_members() {
run_next_test();
});
add_test(function test_get() {
add_task(async function test_get() {
_("GET a non-password-protected resource");
let res = new AsyncResource(server.baseURI + "/open");
res.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "This path exists");
do_check_eq(content.status, 200);
do_check_true(content.success);
// res.data has been updated with the result from the request
do_check_eq(res.data, content);
let content = await res.get();
do_check_eq(content, "This path exists");
do_check_eq(content.status, 200);
do_check_true(content.success);
// res.data has been updated with the result from the request
do_check_eq(res.data, content);
// Observe logging messages.
let resLogger = res._log;
let dbg = resLogger.debug;
let debugMessages = [];
resLogger.debug = function(msg) {
debugMessages.push(msg);
dbg.call(this, msg);
}
// Observe logging messages.
let resLogger = res._log;
let dbg = resLogger.debug;
let debugMessages = [];
resLogger.debug = function(msg) {
debugMessages.push(msg);
dbg.call(this, msg);
}
// Since we didn't receive proper JSON data, accessing content.obj
// will result in a SyntaxError from JSON.parse
let didThrow = false;
try {
content.obj;
} catch (ex) {
didThrow = true;
}
do_check_true(didThrow);
do_check_eq(debugMessages.length, 1);
do_check_eq(debugMessages[0],
"Parse fail: Response body starts: \"\"This path exists\"\".");
resLogger.debug = dbg;
run_next_test();
});
// Since we didn't receive proper JSON data, accessing content.obj
// will result in a SyntaxError from JSON.parse
let didThrow = false;
try {
content.obj;
} catch (ex) {
didThrow = true;
}
do_check_true(didThrow);
do_check_eq(debugMessages.length, 1);
do_check_eq(debugMessages[0],
"Parse fail: Response body starts: \"\"This path exists\"\".");
resLogger.debug = dbg;
});
add_test(function test_basicauth() {
@ -307,19 +299,16 @@ add_test(function test_basicauth() {
run_next_test();
});
add_test(function test_get_protected_fail() {
add_task(async function test_get_protected_fail() {
_("GET a password protected resource (test that it'll fail w/o pass, no throw)");
let res2 = new AsyncResource(server.baseURI + "/protected");
res2.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "This path exists and is protected - failed");
do_check_eq(content.status, 401);
do_check_false(content.success);
run_next_test();
});
let content = await res2.get()
do_check_eq(content, "This path exists and is protected - failed");
do_check_eq(content.status, 401);
do_check_false(content.success);
});
add_test(function test_get_protected_success() {
add_task(async function test_get_protected_success() {
_("GET a password protected resource");
let identityConfig = makeIdentityConfig();
let browseridManager = new BrowserIDManager();
@ -328,262 +317,197 @@ add_test(function test_get_protected_success() {
let res3 = new AsyncResource(server.baseURI + "/protected");
res3.authenticator = auth;
do_check_eq(res3.authenticator, auth);
res3.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "This path exists and is protected");
do_check_eq(content.status, 200);
do_check_true(content.success);
run_next_test();
});
let content = await res3.get();
do_check_eq(content, "This path exists and is protected");
do_check_eq(content.status, 200);
do_check_true(content.success);
});
add_test(function test_get_404() {
add_task(async function test_get_404() {
_("GET a non-existent resource (test that it'll fail, but not throw)");
let res4 = new AsyncResource(server.baseURI + "/404");
res4.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "File not found");
do_check_eq(content.status, 404);
do_check_false(content.success);
let content = await res4.get();
do_check_eq(content, "File not found");
do_check_eq(content.status, 404);
do_check_false(content.success);
// Check some headers of the 404 response
do_check_eq(content.headers.connection, "close");
do_check_eq(content.headers.server, "httpd.js");
do_check_eq(content.headers["content-length"], 14);
run_next_test();
});
// Check some headers of the 404 response
do_check_eq(content.headers.connection, "close");
do_check_eq(content.headers.server, "httpd.js");
do_check_eq(content.headers["content-length"], 14);
});
add_test(function test_put_string() {
add_task(async function test_put_string() {
_("PUT to a resource (string)");
let res_upload = new AsyncResource(server.baseURI + "/upload");
res_upload.put(JSON.stringify(sample_data), function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
run_next_test();
});
let content = await res_upload.put(JSON.stringify(sample_data));
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
});
add_test(function test_put_object() {
add_task(async function test_put_object() {
_("PUT to a resource (object)");
let res_upload = new AsyncResource(server.baseURI + "/upload");
res_upload.put(sample_data, function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
run_next_test();
});
let content = await res_upload.put(sample_data);
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
});
add_test(function test_put_data_string() {
add_task(async function test_put_data_string() {
_("PUT without data arg (uses resource.data) (string)");
let res_upload = new AsyncResource(server.baseURI + "/upload");
res_upload.data = JSON.stringify(sample_data);
res_upload.put(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
run_next_test();
});
let content = await res_upload.put();
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
});
add_test(function test_put_data_object() {
add_task(async function test_put_data_object() {
_("PUT without data arg (uses resource.data) (object)");
let res_upload = new AsyncResource(server.baseURI + "/upload");
res_upload.data = sample_data;
res_upload.put(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
run_next_test();
});
let content = await res_upload.put();
do_check_eq(content, "Valid data upload via PUT");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
});
add_test(function test_post_string() {
add_task(async function test_post_string() {
_("POST to a resource (string)");
let res_upload = new AsyncResource(server.baseURI + "/upload");
res_upload.post(JSON.stringify(sample_data), function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
run_next_test();
});
let content = await res_upload.post(JSON.stringify(sample_data));
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
});
add_test(function test_post_object() {
add_task(async function test_post_object() {
_("POST to a resource (object)");
let res_upload = new AsyncResource(server.baseURI + "/upload");
res_upload.post(sample_data, function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
run_next_test();
});
let content = await res_upload.post(sample_data);
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
});
add_test(function test_post_data_string() {
add_task(async function test_post_data_string() {
_("POST without data arg (uses resource.data) (string)");
let res_upload = new AsyncResource(server.baseURI + "/upload");
res_upload.data = JSON.stringify(sample_data);
res_upload.post(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
run_next_test();
});
let content = await res_upload.post();
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
});
add_test(function test_post_data_object() {
add_task(async function test_post_data_object() {
_("POST without data arg (uses resource.data) (object)");
let res_upload = new AsyncResource(server.baseURI + "/upload");
res_upload.data = sample_data;
res_upload.post(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
run_next_test();
});
let content = await res_upload.post();
do_check_eq(content, "Valid data upload via POST");
do_check_eq(content.status, 200);
do_check_eq(res_upload.data, content);
});
add_test(function test_delete() {
add_task(async function test_delete() {
_("DELETE a resource");
let res6 = new AsyncResource(server.baseURI + "/delete");
res6.delete(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "This resource has been deleted");
do_check_eq(content.status, 200);
run_next_test();
});
let content = await res6.delete();
do_check_eq(content, "This resource has been deleted");
do_check_eq(content.status, 200);
});
add_test(function test_json_body() {
add_task(async function test_json_body() {
_("JSON conversion of response body");
let res7 = new AsyncResource(server.baseURI + "/json");
res7.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, JSON.stringify(sample_data));
do_check_eq(content.status, 200);
do_check_eq(JSON.stringify(content.obj), JSON.stringify(sample_data));
run_next_test();
});
let content = await res7.get();
do_check_eq(content, JSON.stringify(sample_data));
do_check_eq(content.status, 200);
do_check_eq(JSON.stringify(content.obj), JSON.stringify(sample_data));
});
add_test(function test_weave_timestamp() {
add_task(async function test_weave_timestamp() {
_("X-Weave-Timestamp header updates AsyncResource.serverTime");
// Before having received any response containing the
// X-Weave-Timestamp header, AsyncResource.serverTime is null.
do_check_eq(AsyncResource.serverTime, null);
let res8 = new AsyncResource(server.baseURI + "/timestamp");
res8.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(AsyncResource.serverTime, TIMESTAMP);
run_next_test();
});
await res8.get();
do_check_eq(AsyncResource.serverTime, TIMESTAMP);
});
add_test(function test_get_no_headers() {
add_task(async function test_get_no_headers() {
_("GET: no special request headers");
let res_headers = new AsyncResource(server.baseURI + "/headers");
res_headers.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "{}");
run_next_test();
});
let content = await res_headers.get();
do_check_eq(content, "{}");
});
add_test(function test_put_default_content_type() {
add_task(async function test_put_default_content_type() {
_("PUT: Content-Type defaults to text/plain");
let res_headers = new AsyncResource(server.baseURI + "/headers");
res_headers.put("data", function(error, content) {
do_check_eq(error, null);
do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
run_next_test();
});
let content = await res_headers.put("data");
do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
});
add_test(function test_post_default_content_type() {
add_task(async function test_post_default_content_type() {
_("POST: Content-Type defaults to text/plain");
let res_headers = new AsyncResource(server.baseURI + "/headers");
res_headers.post("data", function(error, content) {
do_check_eq(error, null);
do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
run_next_test();
});
let content = await res_headers.post("data");
do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
});
add_test(function test_setHeader() {
add_task(async function test_setHeader() {
_("setHeader(): setting simple header");
let res_headers = new AsyncResource(server.baseURI + "/headers");
res_headers.setHeader("X-What-Is-Weave", "awesome");
do_check_eq(res_headers.headers["x-what-is-weave"], "awesome");
res_headers.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, JSON.stringify({"x-what-is-weave": "awesome"}));
run_next_test();
});
let content = await res_headers.get();
do_check_eq(content, JSON.stringify({"x-what-is-weave": "awesome"}));
});
add_test(function test_setHeader_overwrite() {
add_task(async function test_setHeader_overwrite() {
_("setHeader(): setting multiple headers, overwriting existing header");
let res_headers = new AsyncResource(server.baseURI + "/headers");
res_headers.setHeader("X-WHAT-is-Weave", "more awesomer");
res_headers.setHeader("X-Another-Header", "hello world");
do_check_eq(res_headers.headers["x-what-is-weave"], "more awesomer");
do_check_eq(res_headers.headers["x-another-header"], "hello world");
res_headers.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, JSON.stringify({"x-another-header": "hello world",
"x-what-is-weave": "more awesomer"}));
run_next_test();
});
let content = await res_headers.get();
do_check_eq(content, JSON.stringify({"x-another-header": "hello world",
"x-what-is-weave": "more awesomer"}));
});
add_test(function test_headers_object() {
add_task(async function test_headers_object() {
_("Setting headers object");
let res_headers = new AsyncResource(server.baseURI + "/headers");
res_headers.headers = {};
res_headers.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content, "{}");
run_next_test();
});
let content = await res_headers.get();
do_check_eq(content, "{}");
});
add_test(function test_put_override_content_type() {
add_task(async function test_put_override_content_type() {
_("PUT: override default Content-Type");
let res_headers = new AsyncResource(server.baseURI + "/headers");
res_headers.setHeader("Content-Type", "application/foobar");
do_check_eq(res_headers.headers["content-type"], "application/foobar");
res_headers.put("data", function(error, content) {
do_check_eq(error, null);
do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
run_next_test();
});
let content = await res_headers.put("data");
do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
});
add_test(function test_post_override_content_type() {
add_task(async function test_post_override_content_type() {
_("POST: override default Content-Type");
let res_headers = new AsyncResource(server.baseURI + "/headers");
res_headers.setHeader("Content-Type", "application/foobar");
res_headers.post("data", function(error, content) {
do_check_eq(error, null);
do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
run_next_test();
});
let content = await res_headers.post("data");
do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
});
add_test(function test_weave_backoff() {
add_task(async function test_weave_backoff() {
_("X-Weave-Backoff header notifies observer");
let backoffInterval;
function onBackoff(subject, data) {
@ -592,46 +516,37 @@ add_test(function test_weave_backoff() {
Observers.add("weave:service:backoff:interval", onBackoff);
let res10 = new AsyncResource(server.baseURI + "/backoff");
res10.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(backoffInterval, 600);
run_next_test();
});
await res10.get();
do_check_eq(backoffInterval, 600);
});
add_test(function test_quota_error() {
add_task(async function test_quota_error() {
_("X-Weave-Quota-Remaining header notifies observer on successful requests.");
let res10 = new AsyncResource(server.baseURI + "/quota-error");
res10.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content.status, 400);
do_check_eq(quotaValue, undefined); // HTTP 400, so no observer notification.
run_next_test();
});
let content = await res10.get();
do_check_eq(content.status, 400);
do_check_eq(quotaValue, undefined); // HTTP 400, so no observer notification.
});
add_test(function test_quota_notice() {
add_task(async function test_quota_notice() {
let res10 = new AsyncResource(server.baseURI + "/quota-notice");
res10.get(function(error, content) {
do_check_eq(error, null);
do_check_eq(content.status, 200);
do_check_eq(quotaValue, 1048576);
run_next_test();
});
let content = await res10.get();
do_check_eq(content.status, 200);
do_check_eq(quotaValue, 1048576);
});
add_test(function test_preserve_exceptions() {
add_task(async function test_preserve_exceptions() {
_("Error handling in ChannelListener etc. preserves exception information");
let res11 = new AsyncResource("http://localhost:12345/does/not/exist");
res11.get(function(error, content) {
await Assert.rejects(res11.get(), error => {
do_check_neq(error, null);
do_check_eq(error.result, Cr.NS_ERROR_CONNECTION_REFUSED);
do_check_eq(error.message, "NS_ERROR_CONNECTION_REFUSED");
run_next_test();
return true;
});
});
add_test(function test_xpc_exception_handling() {
add_task(async function test_xpc_exception_handling() {
_("Exception handling inside fetches.");
let res14 = new AsyncResource(server.baseURI + "/json");
res14._onProgress = function(rec) {
@ -641,19 +556,19 @@ add_test(function test_xpc_exception_handling() {
let warnings = [];
res14._log.warn = function(msg) { warnings.push(msg); };
res14.get(function(error, content) {
await Assert.rejects(res14.get(), error => {
do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI);
do_check_eq(error.message, "NS_ERROR_MALFORMED_URI");
do_check_eq(content, null);
do_check_eq(warnings.pop(),
"Got exception calling onProgress handler during fetch of " +
server.baseURI + "/json");
run_next_test();
return true;
});
do_check_eq(warnings.pop(),
"${action} request to ${url} failed: ${ex}");
do_check_eq(warnings.pop(),
"Got exception calling onProgress handler during fetch of " +
server.baseURI + "/json");
});
add_test(function test_js_exception_handling() {
add_task(async function test_js_exception_handling() {
_("JS exception handling inside fetches.");
let res15 = new AsyncResource(server.baseURI + "/json");
res15._onProgress = function(rec) {
@ -662,25 +577,25 @@ add_test(function test_js_exception_handling() {
let warnings = [];
res15._log.warn = function(msg) { warnings.push(msg); };
res15.get(function(error, content) {
await Assert.rejects(res15.get(), error => {
do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
do_check_eq(error.message, "NS_ERROR_XPC_JS_THREW_STRING");
do_check_eq(content, null);
do_check_eq(warnings.pop(),
"Got exception calling onProgress handler during fetch of " +
server.baseURI + "/json");
run_next_test();
return true;
});
do_check_eq(warnings.pop(),
"${action} request to ${url} failed: ${ex}");
do_check_eq(warnings.pop(),
"Got exception calling onProgress handler during fetch of " +
server.baseURI + "/json");
});
add_test(function test_timeout() {
add_task(async function test_timeout() {
_("Ensure channel timeouts are thrown appropriately.");
let res19 = new AsyncResource(server.baseURI + "/json");
res19.ABORT_TIMEOUT = 0;
res19.get(function(error, content) {
await Assert.rejects(res19.get(), error => {
do_check_eq(error.result, Cr.NS_ERROR_NET_TIMEOUT);
run_next_test();
return true;
});
});
@ -703,21 +618,6 @@ add_test(function test_uri_construction() {
run_next_test();
});
add_test(function test_not_sending_cookie() {
let cookieSer = Cc["@mozilla.org/cookieService;1"]
.getService(Ci.nsICookieService);
let uri = CommonUtils.makeURI(server.baseURI);
cookieSer.setCookieString(uri, null, "test=test; path=/;", null);
let res = new AsyncResource(server.baseURI + "/test");
res.get(function(error) {
do_check_null(error);
do_check_true(this.response.success);
do_check_eq("COOKIE!", this.response.body);
server.stop(run_next_test);
});
});
/**
* End of tests that rely on a single HTTP server.
* All tests after this point must begin and end their own.

View File

@ -46,7 +46,7 @@ function triggerRedirect() {
prefs.setCharPref("autoconfig_url", "data:text/plain," + PROXY_FUNCTION);
}
add_test(function test_headers_copied() {
add_task(async function test_headers_copied() {
triggerRedirect();
_("Issuing request.");
@ -54,12 +54,12 @@ add_test(function test_headers_copied() {
resource.setHeader("Authorization", "Basic foobar");
resource.setHeader("X-Foo", "foofoo");
let result = resource.get(TEST_URL);
let result = await resource.get(TEST_URL);
_("Result: " + result);
do_check_eq(result, BODY);
do_check_eq(auth, "Basic foobar");
do_check_eq(foo, "foofoo");
httpServer.stop(run_next_test);
await promiseStopServer(httpServer);
});

View File

@ -57,39 +57,33 @@ add_test(function test_fetchInfo() {
run_next_test();
});
add_test(function test_desktop_post() {
add_task(async function test_desktop_post() {
_("Testing direct Resource POST.");
let r = new AsyncResource(server.baseURI + "/1.1/johndoe/storage/meta/global");
r.post("foo=bar", function(error, content) {
_("User-Agent: " + ua);
do_check_eq(ua, expectedUA + ".desktop");
ua = "";
run_next_test();
});
await r.post("foo=bar");
_("User-Agent: " + ua);
do_check_eq(ua, expectedUA + ".desktop");
ua = "";
});
add_test(function test_desktop_get() {
add_task(async function test_desktop_get() {
_("Testing async.");
Svc.Prefs.set("client.type", "desktop");
let r = new AsyncResource(server.baseURI + "/1.1/johndoe/storage/meta/global");
r.get(function(error, content) {
_("User-Agent: " + ua);
do_check_eq(ua, expectedUA + ".desktop");
ua = "";
run_next_test();
});
await r.get();
_("User-Agent: " + ua);
do_check_eq(ua, expectedUA + ".desktop");
ua = "";
});
add_test(function test_mobile_get() {
add_task(async function test_mobile_get() {
_("Testing mobile.");
Svc.Prefs.set("client.type", "mobile");
let r = new AsyncResource(server.baseURI + "/1.1/johndoe/storage/meta/global");
r.get(function(error, content) {
_("User-Agent: " + ua);
do_check_eq(ua, expectedUA + ".mobile");
ua = "";
run_next_test();
});
await r.get();
_("User-Agent: " + ua);
do_check_eq(ua, expectedUA + ".mobile");
ua = "";
});
add_test(function tear_down() {

View File

@ -113,11 +113,11 @@ add_task(async function v4_upgrade() {
let serverResp;
function retrieve_server_default() {
async function retrieve_server_default() {
serverKeys = serverResp = serverDecrypted = null;
serverKeys = new CryptoWrapper("crypto", "keys");
serverResp = serverKeys.fetch(Service.resource(Service.cryptoKeysURL)).response;
serverResp = (await serverKeys.fetch(Service.resource(Service.cryptoKeysURL))).response;
do_check_true(serverResp.success);
serverDecrypted = serverKeys.decrypt(Service.identity.syncKeyBundle);
@ -127,8 +127,8 @@ add_task(async function v4_upgrade() {
return serverDecrypted.default;
}
function retrieve_and_compare_default(should_succeed) {
let serverDefault = retrieve_server_default();
async function retrieve_and_compare_default(should_succeed) {
let serverDefault = await retrieve_server_default();
let localDefault = Service.collectionKeys.keyForCollection().keyPairB64;
_("Retrieved keyBundle: " + JSON.stringify(serverDefault));
@ -141,26 +141,26 @@ add_task(async function v4_upgrade() {
}
// Uses the objects set above.
function set_server_keys(pair) {
async function set_server_keys(pair) {
serverDecrypted.default = pair;
serverKeys.cleartext = serverDecrypted;
serverKeys.encrypt(Service.identity.syncKeyBundle);
serverKeys.upload(Service.resource(Service.cryptoKeysURL));
await serverKeys.upload(Service.resource(Service.cryptoKeysURL));
}
_("Checking we have the latest keys.");
retrieve_and_compare_default(true);
await retrieve_and_compare_default(true);
_("Update keys on server.");
set_server_keys(["KaaaaaaaaaaaHAtfmuRY0XEJ7LXfFuqvF7opFdBD/MY=",
"aaaaaaaaaaaapxMO6TEWtLIOv9dj6kBAJdzhWDkkkis="]);
await set_server_keys(["KaaaaaaaaaaaHAtfmuRY0XEJ7LXfFuqvF7opFdBD/MY=",
"aaaaaaaaaaaapxMO6TEWtLIOv9dj6kBAJdzhWDkkkis="]);
_("Checking that we no longer have the latest keys.");
retrieve_and_compare_default(false);
await retrieve_and_compare_default(false);
_("Indeed, they're what we set them to...");
do_check_eq("KaaaaaaaaaaaHAtfmuRY0XEJ7LXfFuqvF7opFdBD/MY=",
retrieve_server_default()[0]);
(await retrieve_server_default())[0]);
_("Sync. Should download changed keys automatically.");
let oldClientsModified = collections.clients;
@ -175,7 +175,7 @@ add_task(async function v4_upgrade() {
do_check_true(collections.tabs > oldTabsModified);
_("... and keys will now match.");
retrieve_and_compare_default(true);
await retrieve_and_compare_default(true);
// Clean up.
Service.startOver();
@ -237,12 +237,12 @@ add_task(async function v5_upgrade() {
// Test an upgrade where the contents of the server would cause us to error
// -- keys decrypted with a different sync key, for example.
_("Testing v4 -> v5 (or similar) upgrade.");
function update_server_keys(syncKeyBundle, wboName, collWBO) {
async function update_server_keys(syncKeyBundle, wboName, collWBO) {
generateNewKeys(Service.collectionKeys);
let serverKeys = Service.collectionKeys.asWBO("crypto", wboName);
serverKeys.encrypt(syncKeyBundle);
let res = Service.resource(Service.storageURL + collWBO);
do_check_true(serverKeys.upload(res).success);
do_check_true((await serverKeys.upload(res)).success);
}
_("Bumping version.");
@ -250,14 +250,14 @@ add_task(async function v5_upgrade() {
let m = new WBORecord("meta", "global");
m.payload = {"syncID": "foooooooooooooooooooooooooo",
"storageVersion": STORAGE_VERSION + 1};
m.upload(Service.resource(Service.metaURL));
await m.upload(Service.resource(Service.metaURL));
_("New meta/global: " + JSON.stringify(meta_global));
// Fill the keys with bad data.
let badKeys = new SyncKeyBundle("foobar", "aaaaaaaaaaaaaaaaaaaaaaaaaa");
update_server_keys(badKeys, "keys", "crypto/keys"); // v4
update_server_keys(badKeys, "bulk", "crypto/bulk"); // v5
await update_server_keys(badKeys, "keys", "crypto/keys"); // v4
await update_server_keys(badKeys, "bulk", "crypto/bulk"); // v5
_("Generating new keys.");
generateNewKeys(Service.collectionKeys);

View File

@ -14,7 +14,7 @@ BlaEngine.prototype = {
__proto__: SyncEngine.prototype,
removed: false,
removeClientData() {
async removeClientData() {
this.removed = true;
}

View File

@ -85,7 +85,7 @@ add_task(async function run_test() {
Service.login();
_("Checking that remoteSetup returns true when credentials have changed.");
Service.recordManager.get(Service.metaURL).payload.syncID = "foobar";
(await Service.recordManager.get(Service.metaURL)).payload.syncID = "foobar";
do_check_true(Service._remoteSetup());
let returnStatusCode = (method, code) => (oldMethod) => (req, res) => {

View File

@ -265,7 +265,7 @@ add_task(async function test_enabledRemotely() {
_("Upload some keys to avoid a fresh start.");
let wbo = Service.collectionKeys.generateNewKeysWBO();
wbo.encrypt(Service.identity.syncKeyBundle);
do_check_eq(200, wbo.upload(Service.resource(Service.cryptoKeysURL)).status);
do_check_eq(200, (await wbo.upload(Service.resource(Service.cryptoKeysURL))).status);
_("Engine is disabled.");
do_check_false(engine.enabled);

View File

@ -99,7 +99,7 @@ add_task(async function test_syncStartup_emptyOrOutdatedGlobalsResetsSync() {
// Confirm initial environment
do_check_eq(engine._tracker.changedIDs["rekolok"], undefined);
let metaGlobal = Service.recordManager.get(engine.metaURL);
let metaGlobal = await Service.recordManager.get(engine.metaURL);
do_check_eq(metaGlobal.payload.engines, undefined);
do_check_true(!!collection.payload("flying"));
do_check_true(!!collection.payload("scotsman"));

View File

@ -63,7 +63,7 @@ async function setUp(server) {
generateNewKeys(Service.collectionKeys);
let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.identity.syncKeyBundle);
let result = serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success;
let result = (await serverKeys.upload(Service.resource(Service.cryptoKeysURL))).success;
return result;
}

View File

@ -38,35 +38,33 @@ function getWarningMessages(log) {
return warnMessages;
}
add_test(function test_resource_logs_content_length_mismatch() {
add_task(async function test_resource_logs_content_length_mismatch() {
_("Issuing request.");
let httpServer = httpd_setup({"/content": contentHandler});
let resource = new Resource(httpServer.baseURI + "/content");
let warnMessages = getWarningMessages(resource._log);
let result = resource.get();
let result = await resource.get();
notEqual(warnMessages.length, 0, "test that a warning was logged");
notEqual(result.length, contentLength);
equal(result, BODY);
httpServer.stop(run_next_test);
await promiseStopServer(httpServer);
});
add_test(function test_async_resource_logs_content_length_mismatch() {
add_task(async function test_async_resource_logs_content_length_mismatch() {
_("Issuing request.");
let httpServer = httpd_setup({"/content": contentHandler});
let asyncResource = new AsyncResource(httpServer.baseURI + "/content");
let warnMessages = getWarningMessages(asyncResource._log);
asyncResource.get(function(error, content) {
equal(error, null);
equal(content, BODY);
notEqual(warnMessages.length, 0, "test that warning was logged");
notEqual(content.length, contentLength);
httpServer.stop(run_next_test);
});
let content = await asyncResource.get();
equal(content, BODY);
notEqual(warnMessages.length, 0, "test that warning was logged");
notEqual(content.length, contentLength);
await promiseStopServer(httpServer);
});
add_test(function test_sync_storage_request_logs_content_length_mismatch() {

View File

@ -672,7 +672,7 @@ var TPS = {
Logger.logInfo(`About to perform validation for "${engineName}"`);
let engine = Weave.Service.engineManager.get(engineName);
let validator = new ValidatorType(engine);
let serverRecords = validator.getServerItems(engine);
let serverRecords = Async.promiseSpinningly(validator.getServerItems(engine));
let clientRecords = Async.promiseSpinningly(validator.getClientItems());
try {
// This substantially improves the logs for addons while not making a

View File

@ -16,9 +16,9 @@ use gecko_bindings::structs::RawGeckoPresContextOwned;
use media_queries::MediaType;
use parser::ParserContext;
use properties::ComputedValues;
use std::ascii::AsciiExt;
use std::fmt::{self, Write};
use std::sync::Arc;
use str::starts_with_ignore_ascii_case;
use string_cache::Atom;
use style_traits::ToCss;
use style_traits::viewport::ViewportConstraints;
@ -340,11 +340,6 @@ impl MediaExpressionValue {
}
}
fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
string.len() > prefix.len() &&
string[0..prefix.len()].eq_ignore_ascii_case(prefix)
}
fn find_feature<F>(mut f: F) -> Option<&'static nsMediaFeature>
where F: FnMut(&'static nsMediaFeature) -> bool,
{

View File

@ -1753,6 +1753,13 @@ extern "C" {
RawServoAnimationValueBorrowed)
-> bool;
}
extern "C" {
pub fn Servo_AnimationValues_ComputeDistance(from:
RawServoAnimationValueBorrowed,
to:
RawServoAnimationValueBorrowed)
-> f64;
}
extern "C" {
pub fn Servo_AnimationValue_Serialize(value:
RawServoAnimationValueBorrowed,

View File

@ -18,7 +18,7 @@ use shared_lock::{SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
use std::fmt;
use std::sync::Arc;
use style_traits::ToCss;
use stylesheets::{CssRuleType, MemoryHoleReporter, Stylesheet};
use stylesheets::{CssRuleType, MemoryHoleReporter, Stylesheet, VendorPrefix};
/// A number from 0 to 1, indicating the percentage of the animation when this
/// keyframe should run.
@ -239,6 +239,8 @@ pub struct KeyframesAnimation {
pub steps: Vec<KeyframesStep>,
/// The properties that change in this animation.
pub properties_changed: Vec<TransitionProperty>,
/// Vendor prefix type the @keyframes has.
pub vendor_prefix: Option<VendorPrefix>,
}
/// Get all the animated properties in a keyframes animation.
@ -275,11 +277,14 @@ impl KeyframesAnimation {
///
/// Otherwise, this will compute and sort the steps used for the animation,
/// and return the animation object.
pub fn from_keyframes(keyframes: &[Arc<Locked<Keyframe>>], guard: &SharedRwLockReadGuard)
pub fn from_keyframes(keyframes: &[Arc<Locked<Keyframe>>],
vendor_prefix: Option<VendorPrefix>,
guard: &SharedRwLockReadGuard)
-> Self {
let mut result = KeyframesAnimation {
steps: vec![],
properties_changed: vec![],
vendor_prefix: vendor_prefix,
};
if keyframes.is_empty() {

View File

@ -95,6 +95,19 @@
self.0.interpolate(&other.0, progress).map(T)
}
}
use properties::animated_properties::ComputeDistance;
impl ComputeDistance for T {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_squared_distance(&other.0)
}
}
% endif
}
@ -785,3 +798,41 @@
}
}
</%def>
/// Macro for defining ComputeDistance trait for tuple struct which has Option<T>,
/// e.g. struct T(pub Option<Au>).
<%def name="impl_compute_distance_for_option_tuple(value_for_none)">
impl ComputeDistance for T {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&T(Some(ref this)), &T(Some(ref other))) => {
this.compute_distance(other)
},
(&T(Some(ref value)), &T(None)) |
(&T(None), &T(Some(ref value)))=> {
value.compute_distance(&${value_for_none})
},
(&T(None), &T(None)) => {
Ok(0.0)
},
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&T(Some(ref this)), &T(Some(ref other))) => {
this.compute_squared_distance(other)
},
(&T(Some(ref value)), &T(None)) |
(&T(None), &T(Some(ref value))) => {
value.compute_squared_distance(&${value_for_none})
},
(&T(None), &T(None)) => {
Ok(0.0)
},
}
}
}
</%def>

View File

@ -10,7 +10,7 @@ use euclid::{Point2D, Size2D};
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
use properties::{CSSWideKeyword, PropertyDeclaration};
use properties::longhands;
use properties::longhands::background_size::computed_value::T as BackgroundSize;
use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
use properties::longhands::font_weight::computed_value::T as FontWeight;
use properties::longhands::line_height::computed_value::T as LineHeight;
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
@ -696,14 +696,14 @@ impl Interpolate for VerticalAlign {
}
}
}
impl Interpolate for BackgroundSize {
impl Interpolate for BackgroundSizeList {
#[inline]
fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
self.0.interpolate(&other.0, progress).map(BackgroundSize)
self.0.interpolate(&other.0, progress).map(BackgroundSizeList)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-color
impl Interpolate for RGBA {
#[inline]
@ -2032,3 +2032,614 @@ impl<T, U> Interpolate for Either<T, U>
}
}
}
/// We support ComputeDistance for an API in gecko to test the transition per property.
impl ComputeDistance for AnimationValue {
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
% for prop in data.longhands:
% if prop.animatable:
% if prop.animation_type == "normal":
(&AnimationValue::${prop.camel_case}(ref from),
&AnimationValue::${prop.camel_case}(ref to)) => {
from.compute_distance(to)
},
% else:
(&AnimationValue::${prop.camel_case}(ref _from),
&AnimationValue::${prop.camel_case}(ref _to)) => {
Err(())
},
% endif
% endif
% endfor
_ => {
panic!("Expected compute_distance of computed values of the same \
property, got: {:?}, {:?}", self, other);
}
}
}
}
/// A trait used to implement [compute_distance].
/// In order to compute the Euclidean distance of a list, we need to compute squared distance
/// for each element, so the vector can sum it and then get its squared root as the distance.
pub trait ComputeDistance: Sized {
/// Compute distance between a value and another for a given property.
fn compute_distance(&self, other: &Self) -> Result<f64, ()>;
/// Compute squared distance between a value and another for a given property.
/// This is used for list or if there are many components in a property value.
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_distance(other).map(|d| d * d)
}
}
impl<T: ComputeDistance> ComputeDistance for Vec<T> {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
if self.len() != other.len() {
return Err(());
}
let mut squared_dist = 0.0f64;
for (this, other) in self.iter().zip(other) {
let diff = try!(this.compute_squared_distance(other));
squared_dist += diff;
}
Ok(squared_dist)
}
}
impl ComputeDistance for Au {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
}
impl ComputeDistance for Auto {
#[inline]
fn compute_distance(&self, _other: &Self) -> Result<f64, ()> {
Err(())
}
}
impl ComputeDistance for Normal {
#[inline]
fn compute_distance(&self, _other: &Self) -> Result<f64, ()> {
Err(())
}
}
impl <T> ComputeDistance for Option<T>
where T: ComputeDistance,
{
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&Some(ref this), &Some(ref other)) => {
this.compute_distance(other)
},
_ => Err(()),
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&Some(ref this), &Some(ref other)) => {
this.compute_squared_distance(other)
},
_ => Err(()),
}
}
}
impl ComputeDistance for f32 {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok((*self - *other).abs() as f64)
}
}
impl ComputeDistance for f64 {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok((*self - *other).abs())
}
}
impl ComputeDistance for i32 {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
Ok((*self - *other).abs() as f64)
}
}
impl ComputeDistance for Visibility {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
if *self == *other {
Ok(0.0)
} else {
Ok(1.0)
}
}
}
/// https://www.w3.org/TR/smil-animation/#animateColorElement says we should use Euclidean RGB-cube distance.
impl ComputeDistance for RGBA {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
fn clamp(val: f32) -> f32 {
val.max(0.).min(1.)
}
let start_a = clamp(self.alpha_f32());
let end_a = clamp(other.alpha_f32());
let start = [ start_a,
self.red_f32() * start_a,
self.green_f32() * start_a,
self.blue_f32() * start_a ];
let end = [ end_a,
other.red_f32() * end_a,
other.green_f32() * end_a,
other.blue_f32() * end_a ];
let diff = start.iter().zip(&end)
.fold(0.0f64, |n, (&a, &b)| {
let diff = (a - b) as f64;
n + diff * diff
});
Ok(diff)
}
}
impl ComputeDistance for CSSParserColor {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sq| sq.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(CSSParserColor::RGBA(ref this), CSSParserColor::RGBA(ref other)) => {
this.compute_squared_distance(other)
},
_ => Ok(0.0),
}
}
}
impl ComputeDistance for CalcLengthOrPercentage {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sq| sq.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
let length_diff = (self.length().0 - other.length().0) as f64;
let percentage_diff = (self.percentage() - other.percentage()) as f64;
Ok(length_diff * length_diff + percentage_diff * percentage_diff)
}
}
impl ComputeDistance for LengthOrPercentage {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentage::Length(ref this),
LengthOrPercentage::Length(ref other)) => {
this.compute_distance(other)
},
(LengthOrPercentage::Percentage(ref this),
LengthOrPercentage::Percentage(ref other)) => {
this.compute_distance(other)
},
(this, other) => {
let this: CalcLengthOrPercentage = From::from(this);
let other: CalcLengthOrPercentage = From::from(other);
this.compute_distance(&other)
}
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentage::Length(ref this),
LengthOrPercentage::Length(ref other)) => {
let diff = (this.0 - other.0) as f64;
Ok(diff * diff)
},
(LengthOrPercentage::Percentage(ref this),
LengthOrPercentage::Percentage(ref other)) => {
let diff = (this - other) as f64;
Ok(diff * diff)
},
(this, other) => {
let this: CalcLengthOrPercentage = From::from(this);
let other: CalcLengthOrPercentage = From::from(other);
let length_diff = (this.length().0 - other.length().0) as f64;
let percentage_diff = (this.percentage() - other.percentage()) as f64;
Ok(length_diff * length_diff + percentage_diff * percentage_diff)
}
}
}
}
impl ComputeDistance for LengthOrPercentageOrAuto {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentageOrAuto::Length(ref this),
LengthOrPercentageOrAuto::Length(ref other)) => {
this.compute_distance(other)
},
(LengthOrPercentageOrAuto::Percentage(ref this),
LengthOrPercentageOrAuto::Percentage(ref other)) => {
this.compute_distance(other)
},
(this, other) => {
// If one of the element is Auto, Option<> will be None, and the returned distance is Err(())
let this: Option<CalcLengthOrPercentage> = From::from(this);
let other: Option<CalcLengthOrPercentage> = From::from(other);
this.compute_distance(&other)
}
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentageOrAuto::Length(ref this),
LengthOrPercentageOrAuto::Length(ref other)) => {
let diff = (this.0 - other.0) as f64;
Ok(diff * diff)
},
(LengthOrPercentageOrAuto::Percentage(ref this),
LengthOrPercentageOrAuto::Percentage(ref other)) => {
let diff = (this - other) as f64;
Ok(diff * diff)
},
(this, other) => {
let this: Option<CalcLengthOrPercentage> = From::from(this);
let other: Option<CalcLengthOrPercentage> = From::from(other);
if this.is_none() || other.is_none() {
Err(())
} else {
let length_diff = (this.unwrap().length().0 - other.unwrap().length().0) as f64;
let percentage_diff = (this.unwrap().percentage() - other.unwrap().percentage()) as f64;
Ok(length_diff * length_diff + percentage_diff * percentage_diff)
}
}
}
}
}
impl ComputeDistance for LengthOrPercentageOrNone {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LengthOrPercentageOrNone::Length(ref this),
LengthOrPercentageOrNone::Length(ref other)) => {
this.compute_distance(other)
},
(LengthOrPercentageOrNone::Percentage(ref this),
LengthOrPercentageOrNone::Percentage(ref other)) => {
this.compute_distance(other)
},
_ => Err(())
}
}
}
impl ComputeDistance for LengthOrNone {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(Either::First(ref length), Either::First(ref other)) => {
length.compute_distance(other)
},
_ => Err(()),
}
}
}
impl ComputeDistance for MinLength {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(MinLength::LengthOrPercentage(ref this),
MinLength::LengthOrPercentage(ref other)) => {
this.compute_distance(other)
},
_ => Err(()),
}
}
}
impl ComputeDistance for MaxLength {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(MaxLength::LengthOrPercentage(ref this),
MaxLength::LengthOrPercentage(ref other)) => {
this.compute_distance(other)
},
_ => Err(()),
}
}
}
impl ComputeDistance for VerticalAlign {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(VerticalAlign::LengthOrPercentage(ref this),
VerticalAlign::LengthOrPercentage(ref other)) => {
this.compute_distance(other)
},
_ => Err(()),
}
}
}
impl ComputeDistance for BorderRadiusSize {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(try!(self.0.width.compute_squared_distance(&other.0.width)) +
try!(self.0.height.compute_squared_distance(&other.0.height)))
}
}
impl ComputeDistance for BackgroundSizeList {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_squared_distance(&other.0)
}
}
impl ComputeDistance for LineHeight {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(LineHeight::Length(ref this),
LineHeight::Length(ref other)) => {
this.compute_distance(other)
},
(LineHeight::Number(ref this),
LineHeight::Number(ref other)) => {
this.compute_distance(other)
},
_ => Err(()),
}
}
}
impl ComputeDistance for FontWeight {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
let a = (*self as u32) as f64;
let b = (*other as u32) as f64;
a.compute_distance(&b)
}
}
impl ComputeDistance for Position {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(try!(self.horizontal.compute_squared_distance(&other.horizontal)) +
try!(self.vertical.compute_squared_distance(&other.vertical)))
}
}
impl ComputeDistance for HorizontalPosition {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_squared_distance(&other.0)
}
}
impl ComputeDistance for VerticalPosition {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_distance(&other.0)
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
self.0.compute_squared_distance(&other.0)
}
}
impl ComputeDistance for ClipRect {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
let list = [ try!(self.top.compute_distance(&other.top)),
try!(self.right.compute_distance(&other.right)),
try!(self.bottom.compute_distance(&other.bottom)),
try!(self.left.compute_distance(&other.left)) ];
Ok(list.iter().fold(0.0f64, |sum, diff| sum + diff * diff))
}
}
impl ComputeDistance for TextShadow {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
let list = [ try!(self.offset_x.compute_distance(&other.offset_x)),
try!(self.offset_y.compute_distance(&other.offset_y)),
try!(self.blur_radius.compute_distance(&other.blur_radius)),
try!(self.color.compute_distance(&other.color)) ];
Ok(list.iter().fold(0.0f64, |sum, diff| sum + diff * diff))
}
}
impl ComputeDistance for TextShadowList {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
let zero = TextShadow {
offset_x: Au(0),
offset_y: Au(0),
blur_radius: Au(0),
color: CSSParserColor::RGBA(RGBA::transparent()),
};
let max_len = cmp::max(self.0.len(), other.0.len());
let mut diff_squared = 0.0f64;
for i in 0..max_len {
diff_squared += match (self.0.get(i), other.0.get(i)) {
(Some(shadow), Some(other)) => {
try!(shadow.compute_squared_distance(other))
},
(Some(shadow), None) |
(None, Some(shadow)) => {
try!(shadow.compute_squared_distance(&zero))
},
(None, None) => unreachable!(),
};
}
Ok(diff_squared)
}
}
impl ComputeDistance for BoxShadow {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
if self.inset != other.inset {
return Err(());
}
let list = [ try!(self.offset_x.compute_distance(&other.offset_x)),
try!(self.offset_y.compute_distance(&other.offset_y)),
try!(self.color.compute_distance(&other.color)),
try!(self.spread_radius.compute_distance(&other.spread_radius)),
try!(self.blur_radius.compute_distance(&other.blur_radius)) ];
Ok(list.iter().fold(0.0f64, |sum, diff| sum + diff * diff))
}
}
impl ComputeDistance for BoxShadowList {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
// The inset value must change
let mut zero = BoxShadow {
offset_x: Au(0),
offset_y: Au(0),
spread_radius: Au(0),
blur_radius: Au(0),
color: CSSParserColor::RGBA(RGBA::transparent()),
inset: false,
};
let max_len = cmp::max(self.0.len(), other.0.len());
let mut diff_squared = 0.0f64;
for i in 0..max_len {
diff_squared += match (self.0.get(i), other.0.get(i)) {
(Some(shadow), Some(other)) => {
try!(shadow.compute_squared_distance(other))
},
(Some(shadow), None) |
(None, Some(shadow)) => {
zero.inset = shadow.inset;
try!(shadow.compute_squared_distance(&zero))
}
(None, None) => unreachable!(),
};
}
Ok(diff_squared)
}
}
impl ComputeDistance for TransformList {
#[inline]
fn compute_distance(&self, _other: &Self) -> Result<f64, ()> {
Err(())
}
}
impl<T, U> ComputeDistance for Either<T, U>
where T: ComputeDistance, U: ComputeDistance
{
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&Either::First(ref this), &Either::First(ref other)) => {
this.compute_distance(other)
},
(&Either::Second(ref this), &Either::Second(ref other)) => {
this.compute_distance(other)
},
_ => Err(())
}
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&Either::First(ref this), &Either::First(ref other)) => {
this.compute_squared_distance(other)
},
(&Either::Second(ref this), &Either::Second(ref other)) => {
this.compute_squared_distance(other)
},
_ => Err(())
}
}
}

View File

@ -334,7 +334,7 @@ ${helpers.single_keyword("background-origin",
#[allow(missing_docs)]
pub mod computed_value {
use values::computed::LengthOrPercentageOrAuto;
use properties::animated_properties::{Interpolate, RepeatableListInterpolate};
use properties::animated_properties::{ComputeDistance, Interpolate, RepeatableListInterpolate};
#[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -367,6 +367,24 @@ ${helpers.single_keyword("background-origin",
}
}
}
impl ComputeDistance for T {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
match (self, other) {
(&T::Explicit(ref me), &T::Explicit(ref other)) => {
Ok(try!(me.width.compute_squared_distance(&other.width)) +
try!(me.height.compute_squared_distance(&other.height)))
},
_ => Err(())
}
}
}
}
impl ToCss for computed_value::T {

View File

@ -2197,7 +2197,7 @@ ${helpers.single_keyword("transform-style",
use values::specified::{NoCalcLength, LengthOrPercentage, Percentage};
pub mod computed_value {
use properties::animated_properties::Interpolate;
use properties::animated_properties::{ComputeDistance, Interpolate};
use values::computed::{Length, LengthOrPercentage};
#[derive(Clone, Copy, Debug, PartialEq)]
@ -2209,6 +2209,7 @@ ${helpers.single_keyword("transform-style",
}
impl Interpolate for T {
#[inline]
fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
Ok(T {
horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)),
@ -2217,6 +2218,20 @@ ${helpers.single_keyword("transform-style",
})
}
}
impl ComputeDistance for T {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sd| sd.sqrt())
}
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
Ok(try!(self.horizontal.compute_squared_distance(&other.horizontal)) +
try!(self.vertical.compute_squared_distance(&other.vertical)) +
try!(self.depth.compute_squared_distance(&other.depth)))
}
}
}
impl HasViewportPercentage for SpecifiedValue {

View File

@ -818,7 +818,7 @@ ${helpers.single_keyword("font-variant-caps",
}
pub mod computed_value {
use properties::animated_properties::Interpolate;
use properties::animated_properties::{ComputeDistance, Interpolate};
use std::fmt;
use style_traits::ToCss;
use values::CSSFloat;
@ -850,6 +850,17 @@ ${helpers.single_keyword("font-variant-caps",
}
}
}
impl ComputeDistance for T {
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
match (*self, *other) {
(T::Number(ref number), T::Number(ref other)) =>
number.compute_distance(other),
_ => Err(()),
}
}
}
}
#[inline]

View File

@ -439,13 +439,14 @@ ${helpers.single_keyword("text-align-last",
pub mod computed_value {
use app_units::Au;
use properties::animated_properties::Interpolate;
use properties::animated_properties::{ComputeDistance, Interpolate};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct T(pub Option<Au>);
${helpers.impl_interpolate_for_option_tuple('Au(0)')}
${helpers.impl_compute_distance_for_option_tuple('Au(0)')}
}
impl ToCss for computed_value::T {
@ -523,13 +524,14 @@ ${helpers.single_keyword("text-align-last",
}
pub mod computed_value {
use properties::animated_properties::Interpolate;
use properties::animated_properties::{ComputeDistance, Interpolate};
use values::computed::LengthOrPercentage;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct T(pub Option<LengthOrPercentage>);
${helpers.impl_interpolate_for_option_tuple('LengthOrPercentage::zero()')}
${helpers.impl_compute_distance_for_option_tuple('LengthOrPercentage::zero()')}
}
impl ToCss for computed_value::T {

Some files were not shown because too many files have changed in this diff Show More