From 25a9e2348cdccaf88660303a21f23315209038d2 Mon Sep 17 00:00:00 2001 From: Matthew Noorenberghe Date: Thu, 30 Nov 2017 18:13:21 -0800 Subject: [PATCH 001/219] Bug 1418226 - Create a store for payment dialog unprivileged UI state. r=jaws MozReview-Commit-ID: HHEYdXahhcI --HG-- extra : rebase_source : 09dbe6851331dad7b7e81b58c65ac6f6c2eb28f6 --- .../static/browser_all_files_referenced.js | 3 + toolkit/components/payments/.eslintrc.js | 2 +- toolkit/components/payments/jar.mn | 3 +- toolkit/components/payments/moz.build | 2 + .../components/payments/res/PaymentsStore.js | 92 +++++++++++++ .../payments/test/unit/.eslintrc.js | 7 + toolkit/components/payments/test/unit/head.js | 11 ++ .../payments/test/unit/test_PaymentsStore.js | 126 ++++++++++++++++++ .../payments/test/unit/xpcshell.ini | 4 + 9 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 toolkit/components/payments/res/PaymentsStore.js create mode 100644 toolkit/components/payments/test/unit/.eslintrc.js create mode 100644 toolkit/components/payments/test/unit/head.js create mode 100644 toolkit/components/payments/test/unit/test_PaymentsStore.js create mode 100644 toolkit/components/payments/test/unit/xpcshell.ini diff --git a/browser/base/content/test/static/browser_all_files_referenced.js b/browser/base/content/test/static/browser_all_files_referenced.js index 602dc584694d..e48c9e388f2e 100644 --- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -20,6 +20,9 @@ var gExceptionPaths = [ "resource://gre/defaults/pref/", "resource://shield-recipe-client/node_modules/jexl/lib/", + // These resources are referenced using relative paths from html files. + "resource://payments/", + // https://github.com/mozilla/normandy/issues/577 "resource://shield-recipe-client/test/", diff --git a/toolkit/components/payments/.eslintrc.js b/toolkit/components/payments/.eslintrc.js index 3beca6564bfb..06f0bb37ea47 100644 --- a/toolkit/components/payments/.eslintrc.js +++ b/toolkit/components/payments/.eslintrc.js @@ -30,7 +30,7 @@ module.exports = { "max-len": ["error", 100], "max-nested-callbacks": ["error", 4], "new-parens": "error", - "no-console": "error", + "no-console": ["error", { allow: ["error"] }], "no-fallthrough": "error", "no-multi-str": "error", "no-multiple-empty-lines": ["error", { diff --git a/toolkit/components/payments/jar.mn b/toolkit/components/payments/jar.mn index a05a66c1d82b..d577756f2caa 100644 --- a/toolkit/components/payments/jar.mn +++ b/toolkit/components/payments/jar.mn @@ -10,8 +10,9 @@ toolkit.jar: content/payments/paymentDialog.xhtml (content/paymentDialog.xhtml) % resource payments %res/payments/ res/payments (res/paymentRequest.*) + res/payments/components/ (res/components/*.js) res/payments/debugging.html (res/debugging.html) res/payments/debugging.js (res/debugging.js) - res/payments/components/ (res/components/*.js) res/payments/mixins/ (res/mixins/*.js) + res/payments/PaymentsStore.js (res/PaymentsStore.js) res/payments/vendor/ (res/vendor/*) diff --git a/toolkit/components/payments/moz.build b/toolkit/components/payments/moz.build index ad93a1b8a3ee..b461b7fb3d18 100644 --- a/toolkit/components/payments/moz.build +++ b/toolkit/components/payments/moz.build @@ -23,3 +23,5 @@ SPHINX_TREES['docs'] = 'docs' TESTING_JS_MODULES += [ 'test/PaymentTestUtils.jsm', ] + +XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini'] diff --git a/toolkit/components/payments/res/PaymentsStore.js b/toolkit/components/payments/res/PaymentsStore.js new file mode 100644 index 000000000000..a1b6518aa375 --- /dev/null +++ b/toolkit/components/payments/res/PaymentsStore.js @@ -0,0 +1,92 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/** + * The PaymentsStore class provides lightweight storage with an async publish/subscribe mechanism. + * Synchronous state changes are batched to improve application performance and to reduce partial + * state propagation. + */ + +/* exported PaymentsStore */ + +class PaymentsStore { + /** + * @param {object} [defaultState = {}] The initial state of the store. + */ + constructor(defaultState = {}) { + this._state = defaultState; + this._nextNotifification = 0; + this._subscribers = new Set(); + } + + /** + * Get the current state as a shallow clone with a shallow freeze. + * You shouldn't modify any part of the returned state object as that would bypass notifying + * subscribers and could lead to subscribers assuming old state. + * + * @returns {Object} containing the current state + */ + getState() { + return Object.freeze(Object.assign({}, this._state)); + } + + /** + * Augment the current state with the keys of `obj` and asynchronously notify + * state subscribers. As a result, multiple synchronous state changes will lead + * to a single subscriber notification which leads to better performance and + * reduces partial state changes. + * + * @param {Object} obj The object to augment the state with. Keys in the object + * will be shallow copied with Object.assign. + * + * @example If the state is currently {a:3} then setState({b:"abc"}) will result in a state of + * {a:3, b:"abc"}. + */ + async setState(obj) { + Object.assign(this._state, obj); + let thisChangeNum = ++this._nextNotifification; + + // Let any synchronous setState calls that happen after the current setState call + // complete first. + // Their effects on the state will be batched up before the callback is actually called below. + await Promise.resolve(); + + // Don't notify for state changes that are no longer the most recent. We only want to call the + // callback once with the latest state. + if (thisChangeNum !== this._nextNotifification) { + return; + } + + for (let subscriber of this._subscribers) { + try { + subscriber.stateChangeCallback(this.getState()); + } catch (ex) { + console.error(ex); + } + } + } + + /** + * Subscribe the object to state changes notifications via a `stateChangeCallback` method. + * + * @param {Object} component to receive state change callbacks via a `stateChangeCallback` method. + * If the component is already subscribed, do nothing. + */ + subscribe(component) { + if (this._subscribers.has(component)) { + return; + } + + this._subscribers.add(component); + } + + /** + * @param {Object} component to stop receiving state change callbacks. + */ + unsubscribe(component) { + this._subscribers.delete(component); + } +} diff --git a/toolkit/components/payments/test/unit/.eslintrc.js b/toolkit/components/payments/test/unit/.eslintrc.js new file mode 100644 index 000000000000..70fe35407782 --- /dev/null +++ b/toolkit/components/payments/test/unit/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/xpcshell-test" + ] +}; diff --git a/toolkit/components/payments/test/unit/head.js b/toolkit/components/payments/test/unit/head.js new file mode 100644 index 000000000000..29f5aea01eee --- /dev/null +++ b/toolkit/components/payments/test/unit/head.js @@ -0,0 +1,11 @@ +const {interfaces: Ci, classes: Cc, results: Cr, utils: Cu} = Components; + +Cu.import("resource://gre/modules/Services.jsm"); + +// ================================================ +// Load mocking/stubbing library, sinon +// docs: http://sinonjs.org/releases/v2.3.2/ +Cu.import("resource://gre/modules/Timer.jsm"); +Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this); +/* globals sinon */ +// ================================================ diff --git a/toolkit/components/payments/test/unit/test_PaymentsStore.js b/toolkit/components/payments/test/unit/test_PaymentsStore.js new file mode 100644 index 000000000000..686cfed32fa1 --- /dev/null +++ b/toolkit/components/payments/test/unit/test_PaymentsStore.js @@ -0,0 +1,126 @@ +"use strict"; + +/* import-globals-from ../../res/PaymentsStore.js */ +Services.scriptloader.loadSubScript("resource://payments/PaymentsStore.js", this); + +add_task(async function test_defaultState() { + do_check_true(!!PaymentsStore, "Check PaymentsStore import"); + let ps = new PaymentsStore({ + foo: "bar", + }); + + let state = ps.getState(); + do_check_true(!!state, "Check state is truthy"); + do_check_eq(state.foo, "bar", "Check .foo"); + + Assert.throws(() => state.foo = "new", TypeError, "Assigning to existing prop. should throw"); + Assert.throws(() => state.other = "something", TypeError, "Adding a new prop. should throw"); + Assert.throws(() => delete state.foo, TypeError, "Deleting a prop. should throw"); +}); + +add_task(async function test_setState() { + let ps = new PaymentsStore({}); + + ps.setState({ + one: "one", + }); + let state = ps.getState(); + do_check_eq(Object.keys(state).length, 1, "Should only have 1 prop. set"); + do_check_eq(state.one, "one", "Check .one"); + + ps.setState({ + two: 2, + }); + state = ps.getState(); + do_check_eq(Object.keys(state).length, 2, "Should have 2 props. set"); + do_check_eq(state.one, "one", "Check .one"); + do_check_eq(state.two, 2, "Check .two"); + + ps.setState({ + one: "a", + two: "b", + }); + state = ps.getState(); + do_check_eq(state.one, "a", "Check .one"); + do_check_eq(state.two, "b", "Check .two"); + + do_print("check consecutive setState for the same prop"); + ps.setState({ + one: "c", + }); + ps.setState({ + one: "d", + }); + state = ps.getState(); + do_check_eq(Object.keys(state).length, 2, "Should have 2 props. set"); + do_check_eq(state.one, "d", "Check .one"); + do_check_eq(state.two, "b", "Check .two"); +}); + +add_task(async function test_subscribe_unsubscribe() { + let ps = new PaymentsStore({}); + let subscriber = { + stateChangePromise: null, + _stateChangeResolver: null, + + reset() { + this.stateChangePromise = new Promise(resolve => { + this._stateChangeResolver = resolve; + }); + }, + + stateChangeCallback(state) { + this._stateChangeResolver(state); + this.stateChangePromise = new Promise(resolve => { + this._stateChangeResolver = resolve; + }); + }, + }; + + sinon.spy(subscriber, "stateChangeCallback"); + subscriber.reset(); + ps.subscribe(subscriber); + do_print("subscribe the same listener twice to ensure it still doesn't call the callback"); + ps.subscribe(subscriber); + do_check_true(subscriber.stateChangeCallback.notCalled, + "Check not called synchronously when subscribing"); + + let changePromise = subscriber.stateChangePromise; + ps.setState({ + a: 1, + }); + do_check_true(subscriber.stateChangeCallback.notCalled, + "Check not called synchronously for changes"); + let state = await changePromise; + do_check_eq(state, subscriber.stateChangeCallback.getCall(0).args[0], + "Check resolved state is last state"); + do_check_eq(JSON.stringify(state), `{"a":1}`, "Check callback state"); + + do_print("Testing consecutive setState"); + subscriber.reset(); + subscriber.stateChangeCallback.reset(); + changePromise = subscriber.stateChangePromise; + ps.setState({ + a: 2, + }); + ps.setState({ + a: 3, + }); + do_check_true(subscriber.stateChangeCallback.notCalled, + "Check not called synchronously for changes"); + state = await changePromise; + do_check_eq(state, subscriber.stateChangeCallback.getCall(0).args[0], + "Check resolved state is last state"); + do_check_eq(JSON.stringify(subscriber.stateChangeCallback.getCall(0).args[0]), `{"a":3}`, + "Check callback state matches second setState"); + + do_print("test unsubscribe"); + subscriber.stateChangeCallback = function unexpectedChange() { + do_check_true(false, "stateChangeCallback shouldn't be called after unsubscribing"); + }; + ps.unsubscribe(subscriber); + ps.setState({ + a: 4, + }); + await Promise.resolve("giving a chance for the callback to be called"); +}); diff --git a/toolkit/components/payments/test/unit/xpcshell.ini b/toolkit/components/payments/test/unit/xpcshell.ini new file mode 100644 index 000000000000..66707caf446b --- /dev/null +++ b/toolkit/components/payments/test/unit/xpcshell.ini @@ -0,0 +1,4 @@ +[DEFAULT] +head = head.js + +[test_PaymentsStore.js] From 50105cec036ce6ba60241df2435bc17c1489e81c Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Fri, 1 Dec 2017 10:54:13 +0800 Subject: [PATCH 002/219] Bug 1417300 - adjust sample time before calculating the total decoded duration. r=jya We do not want to perform the adjustment of the timestamp after reading the ogg chain but before. Otherwise the parent's mDecodedAudioDuration would be adjusted causing the sample's time to be twice the value it should be. MozReview-Commit-ID: 50VrOCzrwFg --HG-- extra : rebase_source : 48b1efecb138ca7fa0e4c0584e21be1b6850cce3 --- dom/media/ogg/OggDemuxer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dom/media/ogg/OggDemuxer.cpp b/dom/media/ogg/OggDemuxer.cpp index 588e84a957aa..55c9222e54bb 100644 --- a/dom/media/ogg/OggDemuxer.cpp +++ b/dom/media/ogg/OggDemuxer.cpp @@ -1348,13 +1348,17 @@ OggTrackDemuxer::NextSample() if (mType == TrackInfo::kAudioTrack) { data->mTrackInfo = mParent->mSharedAudioTrackInfo; } + // We do not want to perform the adjustment of the timestamp after reading + // the ogg chain but before. Otherwise the parent's mDecodedAudioDuration + // would be adjusted causing the sample's time to be twice the value it + // should be. + data->mTime += mParent->mDecodedAudioDuration; if (eos) { // We've encountered an end of bitstream packet; check for a chained // bitstream following this one. // This will also update mSharedAudioTrackInfo. mParent->ReadOggChain(data->GetEndTime()); } - data->mTime += mParent->mDecodedAudioDuration; return data; } From 95e86e857a436768ccb5c8996e8a902a8cfc5c4b Mon Sep 17 00:00:00 2001 From: Morris Tseng Date: Thu, 30 Nov 2017 10:36:46 +0800 Subject: [PATCH 003/219] Bug 1421930 - Add crashtest. r=mattwoodrow MozReview-Commit-ID: BXTREZhY3fK --HG-- extra : rebase_source : e2ec2186e9b1b39aa5c9b2fdd1bc871412ec35e5 --- layout/painting/crashtests/1419917.html | 15 +++++++++++++++ layout/painting/crashtests/crashtests.list | 1 + 2 files changed, 16 insertions(+) create mode 100644 layout/painting/crashtests/1419917.html diff --git a/layout/painting/crashtests/1419917.html b/layout/painting/crashtests/1419917.html new file mode 100644 index 000000000000..a60dd15403a7 --- /dev/null +++ b/layout/painting/crashtests/1419917.html @@ -0,0 +1,15 @@ + + + + + + diff --git a/layout/painting/crashtests/crashtests.list b/layout/painting/crashtests/crashtests.list index 60c19c30f09d..6df48b95f9fe 100644 --- a/layout/painting/crashtests/crashtests.list +++ b/layout/painting/crashtests/crashtests.list @@ -4,3 +4,4 @@ load 1413073-1.html load 1413073-2.html load 1405881-1.html load 1418722-1.html +load 1419917.html From f8dabfd76dd14c56dfd0c623120b68abfc058110 Mon Sep 17 00:00:00 2001 From: Nevin Chen Date: Thu, 30 Nov 2017 17:58:05 +0800 Subject: [PATCH 004/219] Bug 1421946 - Make Switchboard accept duplicated experiment names. r=maliu MozReview-Commit-ID: IL2WLraknMt --HG-- extra : rebase_source : 1ac61425d5be8ffd18b237519036b21218f35e9d --- .../gecko/switchboard/SwitchBoard.java | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/switchboard/SwitchBoard.java b/mobile/android/base/java/org/mozilla/gecko/switchboard/SwitchBoard.java index e7361a6ef808..d78663060f99 100644 --- a/mobile/android/base/java/org/mozilla/gecko/switchboard/SwitchBoard.java +++ b/mobile/android/base/java/org/mozilla/gecko/switchboard/SwitchBoard.java @@ -142,34 +142,25 @@ public class SwitchBoard { } try { - // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA); - JSONObject experiment = null; + // Allow repeated experiment names. Only return false after we've iterated all experiments and can't find a match. for (int i = 0; i < experiments.length(); i++) { JSONObject entry = experiments.getJSONObject(i); final String name = entry.getString(KEY_NAME); - if (name.equals(experimentName)) { - experiment = entry; - break; + final boolean isTarget = name.equals(experimentName); + if (isTarget) { + final boolean isMatch = isMatch(c, entry.optJSONObject(KEY_MATCH)); + final JSONObject buckets = entry.getJSONObject(KEY_BUCKETS); + final boolean isInBucket = isInBucket(c, buckets.getInt(KEY_MIN), buckets.getInt(KEY_MAX)); + if (isMatch && isInBucket) { + return true; + } } } - if (experiment == null) { - return false; - } + return false; - if (!isMatch(c, experiment.optJSONObject(KEY_MATCH))) { - return false; - } - - final JSONObject buckets = experiment.getJSONObject(KEY_BUCKETS); - final boolean inExperiment = isInBucket(c, buckets.getInt(KEY_MIN), buckets.getInt(KEY_MAX)); - - if (DEBUG) { - Log.d(TAG, experimentName + " = " + inExperiment); - } - return inExperiment; } catch (JSONException e) { // If the experiment name is not found in the JSON, just return false. // There is no need to log an error, since we don't really care if an From 2f6593bbd3bfedf0eed4b3d682b5c0ef673eea40 Mon Sep 17 00:00:00 2001 From: Kilik Kuo Date: Fri, 1 Dec 2017 10:59:57 +0800 Subject: [PATCH 005/219] Bug 1421920 - Remove unnecessary script in test_eme_unsetMediaKeys_then_capture.html for further debugging. r=jwwang There're several causes for Bug 1414464. Removing unnecessary script in this test to eliminate the uncertainty for further debugging. MozReview-Commit-ID: IWvvlzJ24gw --HG-- extra : rebase_source : 0307c8c1874be276f906a617bcaded9c27885d60 --- .../test/test_eme_unsetMediaKeys_then_capture.html | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/dom/media/test/test_eme_unsetMediaKeys_then_capture.html b/dom/media/test/test_eme_unsetMediaKeys_then_capture.html index b128420c70dd..7229be60b068 100644 --- a/dom/media/test/test_eme_unsetMediaKeys_then_capture.html +++ b/dom/media/test/test_eme_unsetMediaKeys_then_capture.html @@ -37,14 +37,9 @@ function startTest(test, token) function onVideoEnded(ev) { ok(true, TimeStamp(token) + " (ENCRYPTED) content playback ended."); - v.removeEventListener("ended", onVideoEnded); function playClearVideo() { - var p1 = once(v, 'ended', (e) => { - ok(true, TimeStamp(token) + " (CLEAR) content playback ended."); - console.log(" bipbop.mp4 playback ended !!"); - }); - var p2 = once(v, 'loadeddata', (e) => { + var p1 = once(v, 'loadeddata', (e) => { ok(true, TimeStamp(token) + " Receiving event 'loadeddata' for (CLEAR) content."); canvasElem = document.createElement('canvas'); document.body.appendChild(canvasElem); @@ -62,7 +57,7 @@ function startTest(test, token) }); v.src = 'bipbop_225w_175kbps.mp4'; v.play(); - Promise.all([p1, p2, closeSessions()]).then(() => { + Promise.all([p1]).then(() => { manager.finished(token); }, () => { ok(false, TimeStamp(token) + " Something wrong."); @@ -70,7 +65,7 @@ function startTest(test, token) }); } - Promise.all(sessions.map(s => s.close())) + closeSessions() .then(() => { v.setMediaKeys(null) .then(() => { @@ -82,7 +77,7 @@ function startTest(test, token) }); } - v.addEventListener("ended", onVideoEnded); + once(v, "ended", onVideoEnded); // Create a MediaKeys object and set to HTMLMediaElement then start the playback. Promise.all([ From fd883fdb12508e03895062f49f4eb6aa9acceff3 Mon Sep 17 00:00:00 2001 From: Thomas Nguyen Date: Mon, 23 Oct 2017 18:14:28 +0800 Subject: [PATCH 006/219] Bug 1345433 - Bring back assertion that history entries need a valid triggeringPrincipal r=JanH,smaug MozReview-Commit-ID: 9GfGIxkqfhM --HG-- extra : rebase_source : 9568fb89e43400453d42980a267aee733d551e13 --- docshell/base/nsDocShell.cpp | 4 +++- docshell/shistory/nsSHEntry.cpp | 5 +++++ mobile/android/chrome/content/browser.js | 5 ++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index b852c914bcc0..6c7872eaf0b2 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -13028,8 +13028,10 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) srcdoc = VoidString(); } + // If there is no valid triggeringPrincipal, we deny the load + MOZ_ASSERT(triggeringPrincipal, "need a valid triggeringPrincipal to load from history"); if (!triggeringPrincipal) { - triggeringPrincipal = nsContentUtils::GetSystemPrincipal(); + return NS_ERROR_FAILURE; } // Passing nullptr as aSourceDocShell gives the same behaviour as before diff --git a/docshell/shistory/nsSHEntry.cpp b/docshell/shistory/nsSHEntry.cpp index e92d67a4a8d0..ab86d08876bf 100644 --- a/docshell/shistory/nsSHEntry.cpp +++ b/docshell/shistory/nsSHEntry.cpp @@ -550,6 +550,11 @@ nsSHEntry::GetTriggeringPrincipal(nsIPrincipal** aTriggeringPrincipal) NS_IMETHODIMP nsSHEntry::SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal) { + MOZ_ASSERT(aTriggeringPrincipal, "need a valid triggeringPrincipal"); + if (!aTriggeringPrincipal) { + return NS_ERROR_FAILURE; + } + mShared->mTriggeringPrincipal = aTriggeringPrincipal; return NS_OK; } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 3ea937791267..6f2d3f42f91b 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -115,6 +115,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "FontEnumerator", "@mozilla.org/gfx/fontenumerator;1", "nsIFontEnumerator"); +XPCOMUtils.defineLazyModuleGetter(this, "Utils", "resource://gre/modules/sessionstore/Utils.jsm"); + var GlobalEventDispatcher = EventDispatcher.instance; var WindowEventDispatcher = EventDispatcher.for(window); @@ -3689,7 +3691,8 @@ Tab.prototype = { this.browser.__SS_data = { entries: [{ url: uri, - title: truncate(title, MAX_TITLE_LENGTH) + title: truncate(title, MAX_TITLE_LENGTH), + triggeringPrincipal_base64: Utils.SERIALIZED_SYSTEMPRINCIPAL }], index: 1, desktopMode: this.desktopMode, From d67c09f7dd0a1b7e91a5dd82e0268e1be8bc59a9 Mon Sep 17 00:00:00 2001 From: Thomas Nguyen Date: Mon, 23 Oct 2017 18:15:40 +0800 Subject: [PATCH 007/219] Bug 1345433 - Ensure tests load pass valid triggeringPrincipal. r=ckerschb,JanH MozReview-Commit-ID: LWcP7drDPwL --HG-- extra : rebase_source : 3741530254b3cb2627f798cf22eacf64c29a8b9e --- .../browser/browser_ext_tabs_discarded.js | 5 ++++- .../test/browser/browser_ext_tabs_lazy.js | 7 ++++-- .../test/browser_906076_lazy_tabs.js | 22 +++++++++---------- .../test/browser_tab_label_during_restore.js | 8 +++---- .../test/browser_scratchpad_sessions.js | 4 ++-- .../org/mozilla/gecko/tests/SessionTest.java | 10 +++++++++ 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js index ea9b08bba1b8..e9f46a955a7c 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js @@ -3,7 +3,10 @@ /* global gBrowser SessionStore */ "use strict"; -let lazyTabState = {entries: [{url: "http://example.com/", title: "Example Domain"}]}; +const {Utils} = Cu.import("resource://gre/modules/sessionstore/Utils.jsm", {}); +const triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL; + +let lazyTabState = {entries: [{url: "http://example.com/", triggeringPrincipal_base64, title: "Example Domain"}]}; add_task(async function test_discarded() { let extension = ExtensionTestUtils.loadExtension({ diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js b/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js index 96556f284ee2..d0c7030aab42 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js @@ -1,10 +1,13 @@ "use strict"; +const {Utils} = Cu.import("resource://gre/modules/sessionstore/Utils.jsm", {}); +const triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL; + const SESSION = { windows: [{ tabs: [ - {entries: [{url: "about:blank"}]}, - {entries: [{url: "https://example.com/"}]}, + {entries: [{url: "about:blank", triggeringPrincipal_base64}]}, + {entries: [{url: "https://example.com/", triggeringPrincipal_base64}]}, ], }], }; diff --git a/browser/components/sessionstore/test/browser_906076_lazy_tabs.js b/browser/components/sessionstore/test/browser_906076_lazy_tabs.js index d92b7e0a3edd..60acdc7ea340 100644 --- a/browser/components/sessionstore/test/browser_906076_lazy_tabs.js +++ b/browser/components/sessionstore/test/browser_906076_lazy_tabs.js @@ -4,16 +4,16 @@ const TEST_STATE = { windows: [{ tabs: [ - { entries: [{ url: "http://example.com" }] }, - { entries: [{ url: "http://example.com" }] }, - { entries: [{ url: "http://example.com" }] }, - { entries: [{ url: "http://example.com" }] }, - { entries: [{ url: "http://example.com" }] }, - { entries: [{ url: "http://example.com" }] }, - { entries: [{ url: "http://example.com" }] }, - { entries: [{ url: "http://example.com" }] }, - { entries: [{ url: "http://example.com" }] }, - { entries: [{ url: "http://example.com" }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, + { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] }, ] }] }; @@ -21,7 +21,7 @@ const TEST_STATE = { const TEST_STATE_2 = { windows: [{ tabs: [ - { entries: [{ url: "about:robots" }] + { entries: [{ url: "about:robots", triggeringPrincipal_base64 }] }, { entries: [], userTypedValue: "http://example.com", diff --git a/browser/components/sessionstore/test/browser_tab_label_during_restore.js b/browser/components/sessionstore/test/browser_tab_label_during_restore.js index 0d9917a18a1d..fb6617d67ab8 100644 --- a/browser/components/sessionstore/test/browser_tab_label_during_restore.js +++ b/browser/components/sessionstore/test/browser_tab_label_during_restore.js @@ -40,10 +40,10 @@ add_task(async function() { await promiseBrowserState({ windows: [{ tabs: [ - { entries: [{ url: REMOTE_URL }] }, - { entries: [{ url: ABOUT_ROBOTS_URI }] }, - { entries: [{ url: REMOTE_URL }] }, - { entries: [{ url: NO_TITLE_URL }] }, + { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] }, + { entries: [{ url: ABOUT_ROBOTS_URI, triggeringPrincipal_base64 }] }, + { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] }, + { entries: [{ url: NO_TITLE_URL, triggeringPrincipal_base64 }] }, ] }] }); diff --git a/devtools/client/scratchpad/test/browser_scratchpad_sessions.js b/devtools/client/scratchpad/test/browser_scratchpad_sessions.js index a6d1728ddd9e..12410771d62b 100644 --- a/devtools/client/scratchpad/test/browser_scratchpad_sessions.js +++ b/devtools/client/scratchpad/test/browser_scratchpad_sessions.js @@ -3,13 +3,13 @@ "use strict"; const {Utils} = Cu.import("resource://gre/modules/sessionstore/Utils.jsm", {}); -const triggeringPrincipalBase64 = Utils.SERIALIZED_SYSTEMPRINCIPAL; +const triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL; const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); const testState = { windows: [{ tabs: [ - { entries: [{ url: "about:blank", triggeringPrincipalBase64 }] }, + { entries: [{ url: "about:blank", triggeringPrincipal_base64 }] }, ] }], scratchpads: [ diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/SessionTest.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/SessionTest.java index 35a4739c2145..1c5ac11d3f47 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/SessionTest.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/SessionTest.java @@ -56,6 +56,7 @@ public abstract class SessionTest extends UITest { protected class PageInfo { private final String url; private final String title; + private final String triggeringPrincipal_base64; public PageInfo(String key) { if (key.startsWith("about:")) { @@ -64,6 +65,8 @@ public abstract class SessionTest extends UITest { url = getPage(key); } title = key; + triggeringPrincipal_base64 = + "SmIS26zLEdO3ZQBgsLbOywAAAAAAAAAAwAAAAAAAAEY="; } } @@ -271,6 +274,8 @@ public abstract class SessionTest extends UITest { final JSONObject entry = new JSONObject(); entry.put("url", page.url); entry.put("title", page.title); + entry.put("triggeringPrincipal_base64", + page.triggeringPrincipal_base64); entries.put(entry); } @@ -363,12 +368,17 @@ public abstract class SessionTest extends UITest { final JSONObject entry = entries.getJSONObject(j); final String url = entry.getString("url"); final String title = entry.optString("title"); + final String principal = + entry.getString("triggeringPrincipal_base64"); final PageInfo page = pages[j]; asserter.is(url, page.url, "URL in JSON matches session URL"); if (!page.url.startsWith("about:")) { asserter.is(title, page.title, "title in JSON matches session title"); } + + asserter.is(principal, page.triggeringPrincipal_base64, + "principal in JSON matches session principal"); } } } catch (JSONException e) { From a407c868302f121447bfa720a3528447734b59ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Delphine=20Leb=C3=A9del?= Date: Thu, 30 Nov 2017 18:13:43 -0800 Subject: [PATCH 008/219] Bug 1422200 - Add vi to maemo-locales for multi-locale builds, r=flod MozReview-Commit-ID: FFJGTFPi17E --HG-- extra : rebase_source : 828a0d93ed114e724d38793f239462982fcd9b9b --- mobile/android/locales/maemo-locales | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/android/locales/maemo-locales b/mobile/android/locales/maemo-locales index 3476dc405182..045d2848f519 100644 --- a/mobile/android/locales/maemo-locales +++ b/mobile/android/locales/maemo-locales @@ -83,6 +83,7 @@ tr uk ur uz +vi wo xh zam From cfd1a43f56e983ae99b9a6770520e930acf25f8f Mon Sep 17 00:00:00 2001 From: Nicolas Chevobbe Date: Wed, 29 Nov 2017 16:55:00 +0100 Subject: [PATCH 009/219] Bug 1408936 - Enable browser_webconsole_eval_in_debugger_stackframe.js in new console frontend; r=bgrins. MozReview-Commit-ID: 6IiuekmfENG --HG-- extra : rebase_source : c190b35c66dfb93db86aa38f6c23e48e2be15ab7 --- .../test/mochitest/browser.ini | 1 - ..._webconsole_eval_in_debugger_stackframe.js | 213 ++++++------------ 2 files changed, 70 insertions(+), 144 deletions(-) diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini index 4403ebdbe633..62a7e6e911ee 100644 --- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini +++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini @@ -276,7 +276,6 @@ skip-if = true # Bug 1403907 [browser_webconsole_errors_after_page_reload.js] skip-if = true # Bug 1408935 [browser_webconsole_eval_in_debugger_stackframe.js] -skip-if = true # Bug 1408936 [browser_webconsole_eval_in_debugger_stackframe2.js] skip-if = true # Bug 1408893 [browser_webconsole_execution_scope.js] diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_eval_in_debugger_stackframe.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_eval_in_debugger_stackframe.js index bc923ff4495b..3a0cddde9f5c 100644 --- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_eval_in_debugger_stackframe.js +++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_eval_in_debugger_stackframe.js @@ -9,149 +9,76 @@ "use strict"; const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + - "test/test-eval-in-stackframe.html"; + "new-console-output/test/mochitest/test-eval-in-stackframe.html"; -var gWebConsole, gJSTerm, gDebuggerWin, gThread, gDebuggerController; -var gStackframes; +add_task(async function () { + // Force the old debugger UI since it's directly used (see Bug 1301705). + await pushPref("devtools.debugger.new-debugger-frontend", false); -// Force the old debugger UI since it's directly used (see Bug 1301705) -Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); -registerCleanupFunction(function* () { - Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); -}); - -function test() { - loadTab(TEST_URI).then(() => { - openConsole().then(consoleOpened); - }); -} - -function consoleOpened(hud) { - gWebConsole = hud; - gJSTerm = hud.jsterm; - gJSTerm.execute("foo").then(onExecuteFoo); -} - -function onExecuteFoo() { - isnot(gWebConsole.outputNode.textContent.indexOf("globalFooBug783499"), -1, - "|foo| value is correct"); - - gJSTerm.clearOutput(); - - // Test for Bug 690529 - Web Console and Scratchpad should evaluate - // expressions in the scope of the content window, not in a sandbox. - executeSoon(() => { - gJSTerm.execute("foo2 = 'newFoo'; window.foo2").then(onNewFoo2); - }); -} - -function onNewFoo2(msg) { - is(gWebConsole.outputNode.textContent.indexOf("undefined"), -1, - "|undefined| is not displayed after adding |foo2|"); - - ok(msg, "output result found"); - - isnot(msg.textContent.indexOf("newFoo"), -1, - "'newFoo' is displayed after adding |foo2|"); - - gJSTerm.clearOutput(); - - info("openDebugger"); - executeSoon(() => openDebugger().then(debuggerOpened)); -} - -function debuggerOpened(aResult) { - gDebuggerWin = aResult.panelWin; - gDebuggerController = gDebuggerWin.DebuggerController; - gThread = gDebuggerController.activeThread; - gStackframes = gDebuggerController.StackFrames; - - info("openConsole"); - executeSoon(() => - openConsole().then(() => - gJSTerm.execute("foo + foo2").then(onExecuteFooAndFoo2) - ) - ); -} - -function onExecuteFooAndFoo2() { - let expected = "globalFooBug783499newFoo"; - isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1, - "|foo + foo2| is displayed after starting the debugger"); - - executeSoon(() => { - gJSTerm.clearOutput(); - - info("openDebugger"); - openDebugger().then(() => { - gThread.addOneTimeListener("framesadded", onFramesAdded); - - info("firstCall()"); - ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { - content.wrappedJSObject.firstCall(); - }); - }); - }); -} - -function onFramesAdded() { - info("onFramesAdded, openConsole() now"); - executeSoon(() => - openConsole().then(() => - gJSTerm.execute("foo + foo2").then(onExecuteFooAndFoo2InSecondCall) - ) - ); -} - -function onExecuteFooAndFoo2InSecondCall() { - let expected = "globalFooBug783499foo2SecondCall"; - isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1, - "|foo + foo2| from |secondCall()|"); - - function runOpenConsole() { - openConsole().then(() => { - gJSTerm.execute("foo + foo2 + foo3").then(onExecuteFoo23InFirstCall); - }); - } - - executeSoon(() => { - gJSTerm.clearOutput(); - - info("openDebugger and selectFrame(1)"); - - openDebugger().then(() => { - gStackframes.selectFrame(1); - - info("openConsole"); - executeSoon(() => runOpenConsole()); - }); - }); -} - -function onExecuteFoo23InFirstCall() { - let expected = "fooFirstCallnewFoofoo3FirstCall"; - isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1, - "|foo + foo2 + foo3| from |firstCall()|"); - - executeSoon(() => - gJSTerm.execute("foo = 'abba'; foo3 = 'bug783499'; foo + foo3").then( - onExecuteFooAndFoo3ChangesInFirstCall)); -} - -var onExecuteFooAndFoo3ChangesInFirstCall = Task.async(function*() { - let expected = "abbabug783499"; - isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1, - "|foo + foo3| updated in |firstCall()|"); - - yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { - is(content.wrappedJSObject.foo, "globalFooBug783499", - "|foo| in content window"); - is(content.wrappedJSObject.foo2, "newFoo", "|foo2| in content window"); - ok(!content.wrappedJSObject.foo3, - "|foo3| was not added to the content window"); - }); - - gWebConsole = gJSTerm = gDebuggerWin = gThread = gDebuggerController = - gStackframes = null; - executeSoon(finishTest); + info("open the console"); + const hud = await openNewTabAndConsole(TEST_URI); + const {jsterm} = hud; + + info("Check `foo` value"); + let onResultMessage = waitForMessage(hud, "globalFooBug783499"); + jsterm.execute("foo"); + await onResultMessage; + ok(true, "|foo| value is correct"); + + info("Assign and check `foo2` value"); + onResultMessage = waitForMessage(hud, "newFoo"); + jsterm.execute("foo2 = 'newFoo'; window.foo2"); + await onResultMessage; + ok(true, "'newFoo' is displayed after adding `foo2`"); + + info("Open the debugger and then select the console again"); + const {panel} = await openDebugger(); + const {activeThread, StackFrames: stackFrames} = panel.panelWin.DebuggerController; + + await openConsole(); + + info("Check `foo + foo2` value"); + onResultMessage = waitForMessage(hud, "globalFooBug783499newFoo"); + jsterm.execute("foo + foo2"); + await onResultMessage; + + info("Select the debugger again"); + await openDebugger(); + + const onFirstCallFramesAdded = activeThread.addOneTimeListener("framesadded"); + // firstCall calls secondCall, which has a debugger statement, so we'll be paused. + ContentTask.spawn(gBrowser.selectedBrowser, {}, function () { + content.wrappedJSObject.firstCall(); + }); + await onFirstCallFramesAdded; + + info("frames added, select the console again"); + await openConsole(); + + info("Check `foo + foo2` value when paused"); + onResultMessage = waitForMessage(hud, "globalFooBug783499foo2SecondCall"); + jsterm.execute("foo + foo2"); + ok(true, "`foo + foo2` from `secondCall()`"); + + info("select the debugger and select the frame (1)"); + await openDebugger(); + stackFrames.selectFrame(1); + await openConsole(); + + info("Check `foo + foo2 + foo3` value when paused on a given frame"); + onResultMessage = waitForMessage(hud, "fooFirstCallnewFoofoo3FirstCall"); + jsterm.execute("foo + foo2 + foo3"); + await onResultMessage; + ok(true, "`foo + foo2 + foo3` from `firstCall()`"); + + onResultMessage = waitForMessage(hud, "abbabug783499"); + jsterm.execute("foo = 'abba'; foo3 = 'bug783499'; foo + foo3"); + await onResultMessage; + ok(true, "`foo + foo3` updated in `firstCall()`"); + + await ContentTask.spawn(gBrowser.selectedBrowser, null, function() { + is(content.wrappedJSObject.foo, "globalFooBug783499", "`foo` in content window"); + is(content.wrappedJSObject.foo2, "newFoo", "`foo2` in content window"); + ok(!content.wrappedJSObject.foo3, "`foo3` was not added to the content window"); + }); }); From a2b4c53cf7138ac26b9d84d2cf3537e75267a571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 Nov 2017 14:18:24 +0100 Subject: [PATCH 010/219] Bug 1421932 - Update compare-locales to 2.5.1, python-fluent to 0.4.4. r=Pike MozReview-Commit-ID: GedJNGewU9R --HG-- extra : rebase_source : ba770bbb6b5bfcd17b7cee74c07372e7a823e51b --- .../compare_locales/__init__.py | 2 +- .../compare-locales/compare_locales/checks.py | 25 +- .../compare_locales/compare.py | 5 +- .../compare-locales/compare_locales/merge.py | 115 + .../compare-locales/compare_locales/parser.py | 425 ++-- .../compare-locales/compare_locales/paths.py | 16 +- .../compare_locales/plurals.py | 160 ++ .../compare_locales/tests/test_defines.py | 112 +- .../compare_locales/tests/test_dtd.py | 72 +- .../compare_locales/tests/test_ftl.py | 76 +- .../compare_locales/tests/test_merge.py | 4 +- .../tests/test_merge_comments.py | 186 ++ .../compare_locales/tests/test_merge_dtd.py | 133 ++ .../compare_locales/tests/test_merge_ftl.py | 316 +++ .../tests/test_merge_messages.py | 93 + .../tests/test_merge_properties.py | 41 + .../tests/test_merge_unknown.py | 21 + .../tests/test_merge_whitespace.py | 76 + .../compare_locales/tests/test_parser.py | 24 +- .../compare_locales/tests/test_properties.py | 47 +- third_party/python/fluent/PKG-INFO | 16 - .../python/fluent/fluent/migrate/__init__.py | 5 +- .../python/fluent/fluent/migrate/context.py | 174 +- .../python/fluent/fluent/migrate/errors.py | 10 + .../python/fluent/fluent/migrate/helpers.py | 6 - .../python/fluent/fluent/migrate/merge.py | 7 +- .../fluent/fluent/migrate/transforms.py | 79 +- third_party/python/fluent/setup.cfg | 4 - third_party/python/fluent/setup.py | 24 - third_party/python/fluent/tools/fluentfmt.py | 23 + .../python/fluent/tools/migrate/README.md | 39 + .../python/fluent/tools/migrate/blame.py | 60 + .../fluent/tools/migrate/examples/__init__.py | 0 .../tools/migrate/examples/about_dialog.ftl | 12 + .../tools/migrate/examples/about_dialog.py | 85 + .../migrate/examples/about_downloads.ftl | 38 + .../tools/migrate/examples/about_downloads.py | 179 ++ .../fluent/tools/migrate/examples/brand.ftl | 13 + .../tools/migrate/examples/bug_1291693.py | 1917 +++++++++++++++++ .../fluent/tools/migrate/examples/menubar.ftl | 336 +++ .../fluent/tools/migrate/examples/toolbar.ftl | 24 + .../fluent/tools/migrate/migrate-l10n.py | 124 ++ third_party/python/fluent/tools/parse.py | 25 + third_party/python/fluent/tools/serialize.py | 23 + 44 files changed, 4738 insertions(+), 434 deletions(-) create mode 100644 third_party/python/compare-locales/compare_locales/merge.py create mode 100644 third_party/python/compare-locales/compare_locales/plurals.py create mode 100644 third_party/python/compare-locales/compare_locales/tests/test_merge_comments.py create mode 100644 third_party/python/compare-locales/compare_locales/tests/test_merge_dtd.py create mode 100644 third_party/python/compare-locales/compare_locales/tests/test_merge_ftl.py create mode 100644 third_party/python/compare-locales/compare_locales/tests/test_merge_messages.py create mode 100644 third_party/python/compare-locales/compare_locales/tests/test_merge_properties.py create mode 100644 third_party/python/compare-locales/compare_locales/tests/test_merge_unknown.py create mode 100644 third_party/python/compare-locales/compare_locales/tests/test_merge_whitespace.py delete mode 100644 third_party/python/fluent/PKG-INFO create mode 100644 third_party/python/fluent/fluent/migrate/errors.py delete mode 100644 third_party/python/fluent/setup.cfg delete mode 100644 third_party/python/fluent/setup.py create mode 100755 third_party/python/fluent/tools/fluentfmt.py create mode 100644 third_party/python/fluent/tools/migrate/README.md create mode 100644 third_party/python/fluent/tools/migrate/blame.py create mode 100644 third_party/python/fluent/tools/migrate/examples/__init__.py create mode 100644 third_party/python/fluent/tools/migrate/examples/about_dialog.ftl create mode 100644 third_party/python/fluent/tools/migrate/examples/about_dialog.py create mode 100644 third_party/python/fluent/tools/migrate/examples/about_downloads.ftl create mode 100644 third_party/python/fluent/tools/migrate/examples/about_downloads.py create mode 100644 third_party/python/fluent/tools/migrate/examples/brand.ftl create mode 100644 third_party/python/fluent/tools/migrate/examples/bug_1291693.py create mode 100644 third_party/python/fluent/tools/migrate/examples/menubar.ftl create mode 100644 third_party/python/fluent/tools/migrate/examples/toolbar.ftl create mode 100755 third_party/python/fluent/tools/migrate/migrate-l10n.py create mode 100755 third_party/python/fluent/tools/parse.py create mode 100755 third_party/python/fluent/tools/serialize.py diff --git a/third_party/python/compare-locales/compare_locales/__init__.py b/third_party/python/compare-locales/compare_locales/__init__.py index 5647aa0233b2..97438c17666d 100644 --- a/third_party/python/compare-locales/compare_locales/__init__.py +++ b/third_party/python/compare-locales/compare_locales/__init__.py @@ -1 +1 @@ -version = "2.1" +version = "2.5.1" diff --git a/third_party/python/compare-locales/compare_locales/checks.py b/third_party/python/compare-locales/compare_locales/checks.py index 2d0642a15878..e4c59c8915de 100644 --- a/third_party/python/compare-locales/compare_locales/checks.py +++ b/third_party/python/compare-locales/compare_locales/checks.py @@ -213,7 +213,8 @@ class DTDChecker(Checker): if self.__known_entities is None and self.reference is not None: self.__known_entities = set() for ent in self.reference: - self.__known_entities.update(self.entities_for_value(ent.val)) + self.__known_entities.update( + self.entities_for_value(ent.raw_val)) return self.__known_entities if self.__known_entities is not None \ else self.entities_for_value(refValue) @@ -248,7 +249,7 @@ class DTDChecker(Checker): Return a checker that offers just those entities. """ - refValue, l10nValue = refEnt.val, l10nEnt.val + refValue, l10nValue = refEnt.raw_val, l10nEnt.raw_val # find entities the refValue references, # reusing markup from DTDParser. reflist = self.known_entities(refValue) @@ -434,19 +435,15 @@ class FluentChecker(Checker): ''' pattern = re.compile('.*\.ftl') - # Positions yielded by FluentChecker.check are absolute offsets from the - # beginning of the file. This is different from the base Checker behavior - # which yields offsets from the beginning of the current entity's value. def check(self, refEnt, l10nEnt): ref_entry = refEnt.entry l10n_entry = l10nEnt.entry # verify that values match, either both have a value or none if ref_entry.value is not None and l10n_entry.value is None: - yield ('error', l10n_entry.span.start, - 'Missing value', 'fluent') + yield ('error', 0, 'Missing value', 'fluent') if ref_entry.value is None and l10n_entry.value is not None: - yield ('error', l10n_entry.value.span.start, - 'Obsolete value', 'fluent') + offset = l10n_entry.value.span.start - l10n_entry.span.start + yield ('error', offset, 'Obsolete value', 'fluent') # verify that we're having the same set of attributes ref_attr_names = set((attr.id.name for attr in ref_entry.attributes)) @@ -461,9 +458,12 @@ class FluentChecker(Checker): # only warn to not trigger a merge skip for attr_name, cnt in l10n_attr_counts.items(): if cnt > 1: + offset = ( + l10n_entry.attributes[l10n_pos[attr_name]].span.start + - l10n_entry.span.start) yield ( 'warning', - l10n_entry.attributes[l10n_pos[attr_name]].span.start, + offset, 'Attribute "{}" occurs {} times'.format( attr_name, cnt), 'fluent') @@ -471,8 +471,7 @@ class FluentChecker(Checker): missing_attr_names = sorted(ref_attr_names - l10n_attr_names, key=lambda k: ref_pos[k]) for attr_name in missing_attr_names: - yield ('error', l10n_entry.span.start, - 'Missing attribute: ' + attr_name, 'fluent') + yield ('error', 0, 'Missing attribute: ' + attr_name, 'fluent') obsolete_attr_names = sorted(l10n_attr_names - ref_attr_names, key=lambda k: l10n_pos[k]) @@ -482,7 +481,7 @@ class FluentChecker(Checker): if attr.id.name in obsolete_attr_names ] for attr in obsolete_attrs: - yield ('error', attr.span.start, + yield ('error', attr.span.start - l10n_entry.span.start, 'Obsolete attribute: ' + attr.id.name, 'fluent') diff --git a/third_party/python/compare-locales/compare_locales/compare.py b/third_party/python/compare-locales/compare_locales/compare.py index ef1c2bdd052e..ca9a867e4c88 100644 --- a/third_party/python/compare-locales/compare_locales/compare.py +++ b/third_party/python/compare-locales/compare_locales/compare.py @@ -10,10 +10,7 @@ import shutil import re from collections import defaultdict -try: - from json import dumps -except: - from simplejson import dumps +from json import dumps from compare_locales import parser from compare_locales import paths, mozpath diff --git a/third_party/python/compare-locales/compare_locales/merge.py b/third_party/python/compare-locales/compare_locales/merge.py new file mode 100644 index 000000000000..22978bd756ae --- /dev/null +++ b/third_party/python/compare-locales/compare_locales/merge.py @@ -0,0 +1,115 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +'Merge resources across channels.' + +from collections import OrderedDict, defaultdict +from codecs import encode + + +from compare_locales import parser as cl +from compare_locales.compare import AddRemove + + +class MergeNotSupportedError(ValueError): + pass + + +def merge_channels(name, *resources): + try: + parser = cl.getParser(name) + except UserWarning: + raise MergeNotSupportedError( + 'Unsupported file format ({}).'.format(name)) + + # A map of comments to the keys of entities they belong to. + comments = {} + + def parse_resource(resource): + # The counter dict keeps track of number of identical comments. + counter = defaultdict(int) + parser.readContents(resource) + pairs = [get_key_value(entity, counter) for entity in parser.walk()] + return OrderedDict(pairs) + + def get_key_value(entity, counter): + if isinstance(entity, cl.Comment): + counter[entity.all] += 1 + # Use the (value, index) tuple as the key. AddRemove will + # de-deplicate identical comments at the same index. + return ((entity.all, counter[entity.all]), entity) + + if isinstance(entity, cl.Whitespace): + # Use the Whitespace instance as the key so that it's always + # unique. Adjecent whitespace will be folded into the longer one in + # prune. + return (entity, entity) + + # When comments change, AddRemove gives us one 'add' and one 'delete' + # (because a comment's key is its content). In merge_two we'll try to + # de-duplicate comments by looking at the entity they belong to. Set + # up the back-reference from the comment to its entity here. + if isinstance(entity, cl.Entity) and entity.pre_comment: + comments[entity.pre_comment] = entity.key + + return (entity.key, entity) + + entities = reduce( + lambda x, y: merge_two(comments, x, y), + map(parse_resource, resources)) + + return encode(serialize_legacy_resource(entities), parser.encoding) + + +def merge_two(comments, newer, older): + diff = AddRemove() + diff.set_left(newer.keys()) + diff.set_right(older.keys()) + + def get_entity(key): + entity = newer.get(key, None) + + # Always prefer the newer version. + if entity is not None: + return entity + + entity = older.get(key) + + # If it's an old comment attached to an entity, try to find that + # entity in newer and return None to use its comment instead in prune. + if isinstance(entity, cl.Comment) and entity in comments: + next_entity = newer.get(comments[entity], None) + if next_entity is not None and next_entity.pre_comment: + # We'll prune this before returning the merged result. + return None + + return entity + + # Create a flat sequence of all entities in order reported by AddRemove. + contents = [(key, get_entity(key)) for _, key in diff] + + def prune(acc, cur): + _, entity = cur + if entity is None: + # Prune Nones which stand for duplicated comments. + return acc + + if len(acc) and isinstance(entity, cl.Whitespace): + _, prev_entity = acc[-1] + + if isinstance(prev_entity, cl.Whitespace): + # Prefer the longer whitespace. + if len(entity.all) > len(prev_entity.all): + acc[-1] = (entity, entity) + return acc + + acc.append(cur) + return acc + + pruned = reduce(prune, contents, []) + return OrderedDict(pruned) + + +def serialize_legacy_resource(entities): + return "".join((entity.all for entity in entities.values())) diff --git a/third_party/python/compare-locales/compare_locales/parser.py b/third_party/python/compare-locales/compare_locales/parser.py index d800fe7f935d..e85d7ac75c3c 100644 --- a/third_party/python/compare-locales/compare_locales/parser.py +++ b/third_party/python/compare-locales/compare_locales/parser.py @@ -8,6 +8,13 @@ import codecs from collections import Counter import logging +try: + from html import unescape as html_unescape +except ImportError: + from HTMLParser import HTMLParser + html_parser = HTMLParser() + html_unescape = html_parser.unescape + from fluent.syntax import FluentParser as FTLParser from fluent.syntax import ast as ftl @@ -32,28 +39,20 @@ class EntityBase(object): Abstraction layer for a localizable entity. Currently supported are grammars of the form: - 1: pre white space - 2: entity definition - 3: entity key (name) - 4: entity value - 5: post white space - <--[1] + 1: entity definition + 2: entity key (name) + 3: entity value + - <-------[2]---------> + <--- definition ----> ''' - def __init__(self, ctx, pre_comment, - span, pre_ws_span, def_span, - key_span, val_span, post_span): + def __init__(self, ctx, pre_comment, span, key_span, val_span): self.ctx = ctx self.span = span - self.pre_ws_span = pre_ws_span - self.def_span = def_span self.key_span = key_span self.val_span = val_span - self.post_span = post_span self.pre_comment = pre_comment - pass def position(self, offset=0): """Get the 1-based line and column of the character @@ -65,7 +64,7 @@ class EntityBase(object): pos = self.span[1] else: pos = self.span[0] + offset - return self.ctx.lines(pos)[0] + return self.ctx.linecol(pos) def value_position(self, offset=0): """Get the 1-based line and column of the character @@ -73,41 +72,32 @@ class EntityBase(object): If offset is negative, return the end of the value. """ + assert self.val_span is not None if offset < 0: pos = self.val_span[1] else: pos = self.val_span[0] + offset - return self.ctx.lines(pos)[0] + return self.ctx.linecol(pos) # getter helpers def get_all(self): return self.ctx.contents[self.span[0]:self.span[1]] - def get_pre_ws(self): - return self.ctx.contents[self.pre_ws_span[0]:self.pre_ws_span[1]] - - def get_def(self): - return self.ctx.contents[self.def_span[0]:self.def_span[1]] - def get_key(self): return self.ctx.contents[self.key_span[0]:self.key_span[1]] def get_raw_val(self): + if self.val_span is None: + return None return self.ctx.contents[self.val_span[0]:self.val_span[1]] - def get_post(self): - return self.ctx.contents[self.post_span[0]:self.post_span[1]] - # getters all = property(get_all) - pre_ws = property(get_pre_ws) - definition = property(get_def) key = property(get_key) val = property(get_raw_val) raw_val = property(get_raw_val) - post = property(get_post) def __repr__(self): return self.key @@ -132,22 +122,15 @@ class Entity(EntityBase): class Comment(EntityBase): - def __init__(self, ctx, span, pre_ws_span, def_span, - post_span): + def __init__(self, ctx, span): self.ctx = ctx self.span = span - self.pre_ws_span = pre_ws_span - self.def_span = def_span - self.post_span = post_span + self.val_span = None @property def key(self): return None - @property - def val(self): - return None - def __repr__(self): return self.all @@ -163,7 +146,6 @@ class Junk(object): def __init__(self, ctx, span): self.ctx = ctx self.span = span - self.pre_ws = self.definition = self.post = '' self.__class__.junkid += 1 self.key = '_junk_%d_%d-%d' % (self.__class__.junkid, span[0], span[1]) @@ -177,7 +159,7 @@ class Junk(object): pos = self.span[1] else: pos = self.span[0] + offset - return self.ctx.lines(pos)[0] + return self.ctx.linecol(pos) # getter helpers def get_all(self): @@ -185,6 +167,7 @@ class Junk(object): # getters all = property(get_all) + raw_val = property(get_all) val = property(get_all) def __repr__(self): @@ -197,9 +180,7 @@ class Whitespace(EntityBase): ''' def __init__(self, ctx, span): self.ctx = ctx - self.key_span = self.val_span = self.span = span - self.def_span = self.pre_ws_span = (span[0], span[0]) - self.post_span = (span[1], span[1]) + self.span = self.key_span = self.val_span = span def __repr__(self): return self.raw_val @@ -207,26 +188,28 @@ class Whitespace(EntityBase): class Parser(object): capabilities = CAN_SKIP | CAN_MERGE - tail = re.compile('\s+\Z') + reWhitespace = re.compile('\s+', re.M) class Context(object): "Fixture for content and line numbers" def __init__(self, contents): self.contents = contents + # Subclasses may use bitmasks to keep state. + self.state = 0 self._lines = None - def lines(self, *positions): - # return line and column tuples, 1-based + def linecol(self, position): + "Returns 1-based line and column numbers." if self._lines is None: nl = re.compile('\n', re.M) self._lines = [m.end() for m in nl.finditer(self.contents)] - line_nrs = [bisect.bisect(self._lines, p) for p in positions] - # compute columns - pos_ = [ - (1 + line, 1 + p - (self._lines[line-1] if line else 0)) - for line, p in zip(line_nrs, positions)] - return pos_ + + line_offset = bisect.bisect(self._lines, position) + line_start = self._lines[line_offset - 1] if line_offset else 0 + col_offset = position - line_start + + return line_offset + 1, col_offset + 1 def __init__(self): if not hasattr(self, 'encoding'): @@ -251,65 +234,56 @@ class Parser(object): self.ctx = Parser.Context(contents) def parse(self): - l = [] - m = {} - for e in self: - m[e.key] = len(l) - l.append(e) - return (l, m) + list_ = list(self) + map_ = dict((e.key, i) for i, e in enumerate(list_)) + return (list_, map_) def __iter__(self): - return self.walk(onlyEntities=True) + return self.walk(only_localizable=True) - def walk(self, onlyEntities=False): + def walk(self, only_localizable=False): if not self.ctx: # loading file failed, or we just didn't load anything return ctx = self.ctx contents = ctx.contents - offset = 0 - entity, offset = self.getEntity(ctx, offset) - while entity: - if (not onlyEntities or - isinstance(entity, Entity) or - type(entity) is Junk): - yield entity - entity, offset = self.getEntity(ctx, offset) - if len(contents) > offset: - yield Junk(ctx, (offset, len(contents))) - def getEntity(self, ctx, offset): + next_offset = 0 + while next_offset < len(contents): + entity = self.getNext(ctx, next_offset) + + if isinstance(entity, (Entity, Junk)): + yield entity + elif not only_localizable: + yield entity + + next_offset = entity.span[1] + + def getNext(self, ctx, offset): + m = self.reWhitespace.match(ctx.contents, offset) + if m: + return Whitespace(ctx, m.span()) m = self.reKey.match(ctx.contents, offset) if m: - offset = m.end() - entity = self.createEntity(ctx, m) - return (entity, offset) + return self.createEntity(ctx, m) m = self.reComment.match(ctx.contents, offset) if m: - offset = m.end() - self.last_comment = Comment(ctx, *[m.span(i) for i in xrange(4)]) - return (self.last_comment, offset) - return self.getTrailing(ctx, offset, self.reKey, self.reComment) + self.last_comment = Comment(ctx, m.span()) + return self.last_comment + return self.getJunk(ctx, offset, self.reKey, self.reComment) - def getTrailing(self, ctx, offset, *expressions): + def getJunk(self, ctx, offset, *expressions): junkend = None for exp in expressions: m = exp.search(ctx.contents, offset) if m: junkend = min(junkend, m.start()) if junkend else m.start() - if junkend is None: - if self.tail.match(ctx.contents, offset): - white_end = len(ctx.contents) - return (Whitespace(ctx, (offset, white_end)), white_end) - else: - return (None, offset) - return (Junk(ctx, (offset, junkend)), junkend) + return Junk(ctx, (offset, junkend or len(ctx.contents))) def createEntity(self, ctx, m): pre_comment = self.last_comment self.last_comment = None - return Entity(ctx, pre_comment, - *[m.span(i) for i in xrange(6)]) + return Entity(ctx, pre_comment, m.span(), m.span('key'), m.span('val')) @classmethod def findDuplicates(cls, entities): @@ -326,21 +300,22 @@ def getParser(path): raise UserWarning("Cannot find Parser") -# Subgroups of the match will: -# 1: pre white space -# 2: pre comments -# 3: entity definition -# 4: entity key (name) -# 5: entity value -# 6: post comment (and white space) in the same line (dtd only) -# <--[1] -# <--[2] -# -# -# <-------[3]---------><------[6]------> - - class DTDEntity(Entity): + @property + def val(self): + '''Unescape HTML entities into corresponding Unicode characters. + + Named (&), decimal (&), and hex (& and &) formats + are supported. Unknown entities are left intact. + + As of Python 2.7 and Python 3.6 the following 252 named entities are + recognized and unescaped: + + https://github.com/python/cpython/blob/2.7/Lib/htmlentitydefs.py + https://github.com/python/cpython/blob/3.6/Lib/html/entities.py + ''' + return html_unescape(self.raw_val) + def value_position(self, offset=0): # DTDChecker already returns tuples of (line, col) positions if isinstance(offset, tuple): @@ -374,22 +349,21 @@ class DTDParser(Parser): # [#x0300-#x036F] | [#x203F-#x2040] NameChar = NameStartChar + ur'\-\.0-9' + u'\xB7\u0300-\u036F\u203F-\u2040' Name = '[' + NameStartChar + '][' + NameChar + ']*' - reKey = re.compile('(?:(?P
\s*)(?P' + Name +
-                       ')\s+(?P\"[^\"]*\"|\'[^\']*\'?)\s*>)'
-                       '(?P\s+)?)',
+    reKey = re.compile('' + Name + ')\s+'
+                       '(?P\"[^\"]*\"|\'[^\']*\'?)\s*>',
                        re.DOTALL | re.M)
     # add BOM to DTDs, details in bug 435002
     reHeader = re.compile(u'^\ufeff')
-    reComment = re.compile('(\s*)()(\s*)' % CharMinusDash,
+    reComment = re.compile('' % CharMinusDash,
                            re.S)
-    rePE = re.compile(u'(?:(\s*)'
-                      u'(\s*%' + Name +
-                      u';)([ \t]*(?:' + XmlComment + u'\s*)*\n?)?)')
+    rePE = re.compile(u'' + Name + ')\s+'
+                      u'SYSTEM\s+(?P\"[^\"]*\"|\'[^\']*\')\s*>\s*'
+                      u'%' + Name + ';'
+                      u'(?:[ \t]*(?:' + XmlComment + u'\s*)*\n?)?')
 
-    def getEntity(self, ctx, offset):
+    def getNext(self, ctx, offset):
         '''
-        Overload Parser.getEntity to special-case ParsedEntities.
+        Overload Parser.getNext to special-case ParsedEntities.
         Just check for a parsed entity if that method claims junk.
 
         
@@ -397,14 +371,14 @@ class DTDParser(Parser):
         '''
         if offset is 0 and self.reHeader.match(ctx.contents):
             offset += 1
-        entity, inneroffset = Parser.getEntity(self, ctx, offset)
+        entity = Parser.getNext(self, ctx, offset)
         if (entity and isinstance(entity, Junk)) or entity is None:
             m = self.rePE.match(ctx.contents, offset)
             if m:
-                inneroffset = m.end()
                 self.last_comment = None
-                entity = DTDEntity(ctx, '', *[m.span(i) for i in xrange(6)])
-        return (entity, inneroffset)
+                entity = DTDEntity(
+                    ctx, '', m.span(), m.span('key'), m.span('val'))
+        return entity
 
     def createEntity(self, ctx, m):
         valspan = m.span('val')
@@ -412,10 +386,7 @@ class DTDParser(Parser):
         pre_comment = self.last_comment
         self.last_comment = None
         return DTDEntity(ctx, pre_comment,
-                         m.span(),
-                         m.span('pre'),
-                         m.span('entity'), m.span('key'), valspan,
-                         m.span('post'))
+                         m.span(), m.span('key'), valspan)
 
 
 class PropertiesEntity(Entity):
@@ -438,28 +409,26 @@ class PropertiesEntity(Entity):
 
 class PropertiesParser(Parser):
     def __init__(self):
-        self.reKey = re.compile('^(\s*)'
-                                '([^#!\s\n][^=:\n]*?)\s*[:=][ \t]*', re.M)
-        self.reComment = re.compile('(\s*)(((?:[#!][^\n]*\n?)+))', re.M)
+        self.reKey = re.compile(
+            '(?P[^#!\s\n][^=:\n]*?)\s*[:=][ \t]*', re.M)
+        self.reComment = re.compile('(?:[#!][^\n]*\n)*(?:[#!][^\n]*)', re.M)
         self._escapedEnd = re.compile(r'\\+$')
         self._trailingWS = re.compile(r'\s*(?:\n|\Z)', re.M)
         Parser.__init__(self)
 
-    def getEntity(self, ctx, offset):
+    def getNext(self, ctx, offset):
         # overwritten to parse values line by line
         contents = ctx.contents
+
+        m = self.reWhitespace.match(contents, offset)
+        if m:
+            return Whitespace(ctx, m.span())
+
         m = self.reComment.match(contents, offset)
         if m:
-            spans = [m.span(i) for i in xrange(3)]
-            start_trailing = offset = m.end()
-            while offset < len(contents):
-                m = self._trailingWS.match(contents, offset)
-                if not m:
-                    break
-                offset = m.end()
-            spans.append((start_trailing, offset))
-            self.last_comment = Comment(ctx, *spans)
-            return (self.last_comment, offset)
+            self.last_comment = Comment(ctx, m.span())
+            return self.last_comment
+
         m = self.reKey.match(contents, offset)
         if m:
             startline = offset = m.end()
@@ -477,35 +446,31 @@ class PropertiesParser(Parser):
                 if len(_e.group()) % 2 == 0:
                     break
                 startline = offset
+
             # strip trailing whitespace
             ws = self._trailingWS.search(contents, startline)
             if ws:
                 endval = ws.start()
-                offset = ws.end()
+
             pre_comment = self.last_comment
             self.last_comment = None
             entity = PropertiesEntity(
                 ctx, pre_comment,
-                (m.start(), offset),   # full span
-                m.span(1),  # leading whitespan
-                (m.start(2), offset),   # entity def span
-                m.span(2),   # key span
-                (m.end(), endval),   # value span
-                (offset, offset))  # post comment span, empty
-            return (entity, offset)
-        return self.getTrailing(ctx, offset, self.reKey, self.reComment)
+                (m.start(), endval),   # full span
+                m.span('key'),
+                (m.end(), endval))   # value span
+            return entity
+
+        return self.getJunk(ctx, offset, self.reKey, self.reComment)
 
 
 class DefinesInstruction(EntityBase):
     '''Entity-like object representing processing instructions in inc files
     '''
-    def __init__(self, ctx, span, pre_ws_span, def_span, val_span, post_span):
+    def __init__(self, ctx, span, val_span):
         self.ctx = ctx
         self.span = span
-        self.pre_ws_span = pre_ws_span
-        self.def_span = def_span
         self.key_span = self.val_span = val_span
-        self.post_span = post_span
 
     def __repr__(self):
         return self.raw_val
@@ -514,53 +479,61 @@ class DefinesInstruction(EntityBase):
 class DefinesParser(Parser):
     # can't merge, #unfilter needs to be the last item, which we don't support
     capabilities = CAN_COPY
-    tail = re.compile(r'(?!)')  # never match
+    reWhitespace = re.compile('\n+', re.M)
+
+    EMPTY_LINES = 1 << 0
+    PAST_FIRST_LINE = 1 << 1
 
     def __init__(self):
-        self.reComment = re.compile(
-            '((?:[ \t]*\n)*)'
-            '((?:^# .*?(?:\n|\Z))+)'
-            '((?:[ \t]*(?:\n|\Z))*)', re.M)
-        self.reKey = re.compile('((?:[ \t]*\n)*)'
-                                '(#define[ \t]+(\w+)(?:[ \t](.*?))?(?:\n|\Z))'
-                                '((?:[ \t]*(?:\n|\Z))*)',
-                                re.M)
-        self.rePI = re.compile('((?:[ \t]*\n)*)'
-                               '(#(\w+)[ \t]+(.*?)(?:\n|\Z))'
-                               '((?:[ \t]*(?:\n|\Z))*)',
-                               re.M)
+        self.reComment = re.compile('(?:^# .*?\n)*(?:^# [^\n]*)', re.M)
+        # corresponds to
+        # https://hg.mozilla.org/mozilla-central/file/72ee4800d4156931c89b58bd807af4a3083702bb/python/mozbuild/mozbuild/preprocessor.py#l561  # noqa
+        self.reKey = re.compile(
+            '#define[ \t]+(?P\w+)(?:[ \t](?P[^\n]*))?', re.M)
+        self.rePI = re.compile('#(?P\w+[ \t]+[^\n]+)', re.M)
         Parser.__init__(self)
 
-    def getEntity(self, ctx, offset):
+    def getNext(self, ctx, offset):
         contents = ctx.contents
+
+        m = self.reWhitespace.match(contents, offset)
+        if m:
+            if ctx.state & self.EMPTY_LINES:
+                return Whitespace(ctx, m.span())
+            if ctx.state & self.PAST_FIRST_LINE and len(m.group()) == 1:
+                return Whitespace(ctx, m.span())
+            else:
+                return Junk(ctx, m.span())
+
+        # We're not in the first line anymore.
+        ctx.state |= self.PAST_FIRST_LINE
+
         m = self.reComment.match(contents, offset)
         if m:
-            offset = m.end()
-            self.last_comment = Comment(ctx, *[m.span(i) for i in xrange(4)])
-            return (self.last_comment, offset)
+            self.last_comment = Comment(ctx, m.span())
+            return self.last_comment
         m = self.reKey.match(contents, offset)
         if m:
-            offset = m.end()
-            return (self.createEntity(ctx, m), offset)
+            return self.createEntity(ctx, m)
         m = self.rePI.match(contents, offset)
         if m:
-            offset = m.end()
-            return (DefinesInstruction(ctx, *[m.span(i) for i in xrange(5)]),
-                    offset)
-        return self.getTrailing(ctx, offset,
-                                self.reComment, self.reKey, self.rePI)
+            instr = DefinesInstruction(ctx, m.span(), m.span('val'))
+            if instr.val == 'filter emptyLines':
+                ctx.state |= self.EMPTY_LINES
+            if instr.val == 'unfilter emptyLines':
+                ctx.state &= ~ self.EMPTY_LINES
+            return instr
+        return self.getJunk(
+            ctx, offset, self.reComment, self.reKey, self.rePI)
 
 
 class IniSection(EntityBase):
     '''Entity-like object representing sections in ini files
     '''
-    def __init__(self, ctx, span, pre_ws_span, def_span, val_span, post_span):
+    def __init__(self, ctx, span, val_span):
         self.ctx = ctx
         self.span = span
-        self.pre_ws_span = pre_ws_span
-        self.def_span = def_span
         self.key_span = self.val_span = val_span
-        self.post_span = post_span
 
     def __repr__(self):
         return self.raw_val
@@ -577,37 +550,28 @@ class IniParser(Parser):
     ...
     '''
     def __init__(self):
-        self.reComment = re.compile(
-            '((?:[ \t]*\n)*)'
-            '((?:^[;#].*?(?:\n|\Z))+)'
-            '((?:[ \t]*(?:\n|\Z))*)', re.M)
-        self.reSection = re.compile(
-            '((?:[ \t]*\n)*)'
-            '(\[(.*?)\])'
-            '((?:[ \t]*(?:\n|\Z))*)', re.M)
-        self.reKey = re.compile(
-            '((?:[ \t]*\n)*)'
-            '((.+?)=(.*))'
-            '((?:[ \t]*(?:\n|\Z))*)', re.M)
+        self.reComment = re.compile('(?:^[;#][^\n]*\n)*(?:^[;#][^\n]*)', re.M)
+        self.reSection = re.compile('\[(?P.*?)\]', re.M)
+        self.reKey = re.compile('(?P.+?)=(?P.*)', re.M)
         Parser.__init__(self)
 
-    def getEntity(self, ctx, offset):
+    def getNext(self, ctx, offset):
         contents = ctx.contents
+        m = self.reWhitespace.match(contents, offset)
+        if m:
+            return Whitespace(ctx, m.span())
         m = self.reComment.match(contents, offset)
         if m:
-            offset = m.end()
-            self.last_comment = Comment(ctx, *[m.span(i) for i in xrange(4)])
-            return (self.last_comment, offset)
+            self.last_comment = Comment(ctx, m.span())
+            return self.last_comment
         m = self.reSection.match(contents, offset)
         if m:
-            offset = m.end()
-            return (IniSection(ctx, *[m.span(i) for i in xrange(5)]), offset)
+            return IniSection(ctx, m.span(), m.span('val'))
         m = self.reKey.match(contents, offset)
         if m:
-            offset = m.end()
-            return (self.createEntity(ctx, m), offset)
-        return self.getTrailing(ctx, offset,
-                                self.reComment, self.reSection, self.reKey)
+            return self.createEntity(ctx, m)
+        return self.getJunk(
+            ctx, offset, self.reComment, self.reSection, self.reKey)
 
 
 class FluentAttribute(EntityBase):
@@ -642,10 +606,16 @@ class FluentEntity(Entity):
         if entry.value is not None:
             self.val_span = (entry.value.span.start, entry.value.span.end)
         else:
-            self.val_span = (0, 0)
+            self.val_span = None
 
         self.entry = entry
 
+        # EntityBase instances are expected to have pre_comment. It's used by
+        # other formats to associate a Comment with an Entity. FluentEntities
+        # don't need it because message comments are part of the entry AST and
+        # are not separate Comment instances.
+        self.pre_comment = None
+
     _word_count = None
 
     def count_words(self):
@@ -665,18 +635,10 @@ class FluentEntity(Entity):
         return self.entry.equals(
             other.entry, ignored_fields=self.ignored_fields)
 
-    # Positions yielded by FluentChecker.check are absolute offsets from the
-    # beginning of the file.  This is different from the base Checker behavior
-    # which yields offsets from the beginning of the current entity's value.
-    def position(self, pos=None):
-        if pos is None:
-            pos = self.entry.span.start
-        return self.ctx.lines(pos)[0]
-
-    # FluentEntities don't differentiate between entity and value positions
-    # because all positions are absolute from the beginning of the file.
-    def value_position(self, pos=None):
-        return self.position(pos)
+    # In Fluent we treat entries as a whole.  FluentChecker reports errors at
+    # offsets calculated from the beginning of the entry.
+    def value_position(self, offset=0):
+        return self.position(offset)
 
     @property
     def attributes(self):
@@ -684,6 +646,16 @@ class FluentEntity(Entity):
             yield FluentAttribute(self, attr_node)
 
 
+class FluentSection(EntityBase):
+    def __init__(self, ctx, entry):
+        self.entry = entry
+        self.ctx = ctx
+
+        self.span = (entry.span.start, entry.span.end)
+        self.key_span = self.val_span = (
+            entry.name.span.start, entry.name.span.end)
+
+
 class FluentParser(Parser):
     capabilities = CAN_SKIP
 
@@ -691,12 +663,32 @@ class FluentParser(Parser):
         super(FluentParser, self).__init__()
         self.ftl_parser = FTLParser()
 
-    def walk(self, onlyEntities=False):
+    def walk(self, only_localizable=False):
         if not self.ctx:
             # loading file failed, or we just didn't load anything
             return
+
         resource = self.ftl_parser.parse(self.ctx.contents)
+
+        if resource.comment:
+            last_span_end = resource.comment.span.end
+
+            if not only_localizable:
+                if 0 < resource.comment.span.start:
+                    yield Whitespace(
+                        self.ctx, (0, resource.comment.span.start))
+                yield Comment(
+                    self.ctx,
+                    (resource.comment.span.start, resource.comment.span.end))
+        else:
+            last_span_end = 0
+
         for entry in resource.body:
+            if not only_localizable:
+                if entry.span.start > last_span_end:
+                    yield Whitespace(
+                        self.ctx, (last_span_end, entry.span.start))
+
             if isinstance(entry, ftl.Message):
                 yield FluentEntity(self.ctx, entry)
             elif isinstance(entry, ftl.Junk):
@@ -708,6 +700,19 @@ class FluentParser(Parser):
                 ws, we = re.search('\s*$', entry.content).span()
                 end -= we - ws
                 yield Junk(self.ctx, (start, end))
+            elif isinstance(entry, ftl.Comment) and not only_localizable:
+                span = (entry.span.start, entry.span.end)
+                yield Comment(self.ctx, span)
+            elif isinstance(entry, ftl.Section) and not only_localizable:
+                yield FluentSection(self.ctx, entry)
+
+            last_span_end = entry.span.end
+
+        # Yield Whitespace at the EOF.
+        if not only_localizable:
+            eof_offset = len(self.ctx.contents)
+            if eof_offset > last_span_end:
+                yield Whitespace(self.ctx, (last_span_end, eof_offset))
 
 
 __constructors = [('\\.dtd$', DTDParser()),
diff --git a/third_party/python/compare-locales/compare_locales/paths.py b/third_party/python/compare-locales/compare_locales/paths.py
index 4153a00b44d2..1e8b63e663cc 100644
--- a/third_party/python/compare-locales/compare_locales/paths.py
+++ b/third_party/python/compare-locales/compare_locales/paths.py
@@ -135,7 +135,7 @@ class ProjectConfig(object):
         def filter_(module, path, entity=None):
             try:
                 rv = filter(module, path, entity=entity)
-            except:
+            except BaseException:  # we really want to handle EVERYTHING here
                 return 'error'
             rv = {
                 True: 'error',
@@ -434,7 +434,7 @@ class TOMLParser(object):
         try:
             with open(self.path, 'rb') as fin:
                 self.data = toml.load(fin)
-        except:
+        except (toml.TomlError, IOError):
             raise ConfigNotFound(self.path)
 
     def processEnv(self):
@@ -542,7 +542,7 @@ class L10nConfigParser(object):
         '''
         try:
             depth = cp.get('general', 'depth')
-        except:
+        except (NoSectionError, NoOptionError):
             depth = '.'
         return depth
 
@@ -554,13 +554,13 @@ class L10nConfigParser(object):
         '''
         filter_path = mozpath.join(mozpath.dirname(self.inipath), 'filter.py')
         try:
-            l = {}
-            execfile(filter_path, {}, l)
-            if 'test' in l and callable(l['test']):
-                filters = [l['test']]
+            local = {}
+            execfile(filter_path, {}, local)
+            if 'test' in local and callable(local['test']):
+                filters = [local['test']]
             else:
                 filters = []
-        except:
+        except BaseException:  # we really want to handle EVERYTHING here
             filters = []
 
         for c in self.children:
diff --git a/third_party/python/compare-locales/compare_locales/plurals.py b/third_party/python/compare-locales/compare_locales/plurals.py
new file mode 100644
index 000000000000..3506f00e72f5
--- /dev/null
+++ b/third_party/python/compare-locales/compare_locales/plurals.py
@@ -0,0 +1,160 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+'Mapping of locales to CLDR plural categories as implemented by PluralForm.jsm'
+
+CATEGORIES_BY_INDEX = (
+    # 0 (Chinese)
+    ('other',),
+    # 1 (English)
+    ('one', 'other'),
+    # 2 (French)
+    ('one', 'other'),
+    # 3 (Latvian)
+    ('zero', 'one', 'other'),
+    # 4 (Scottish Gaelic)
+    ('one', 'two', 'few', 'other'),
+    # 5 (Romanian)
+    ('one', 'few', 'other'),
+    # 6 (Lithuanian)
+    # CLDR: one, few, many (fractions), other
+    ('one', 'other', 'few'),
+    # 7 (Russian)
+    # CLDR: one, few, many, other (fractions)
+    ('one', 'few', 'many'),
+    # 8 (Slovak)
+    # CLDR: one, few, many (fractions), other
+    ('one', 'few', 'other'),
+    # 9 (Polish)
+    # CLDR: one, few, many, other (fractions)
+    ('one', 'few', 'many'),
+    # 10 (Slovenian)
+    ('one', 'two', 'few', 'other'),
+    # 11 (Irish Gaelic)
+    ('one', 'two', 'few', 'many', 'other'),
+    # 12 (Arabic)
+    # CLDR: zero, one, two, few, many, other
+    ('one', 'two', 'few', 'many', 'other', 'zero'),
+    # 13 (Maltese)
+    ('one', 'few', 'many', 'other'),
+    # 14 (Macedonian)
+    # CLDR: one, other
+    ('one', 'two', 'other'),
+    # 15 (Icelandic)
+    ('one', 'other'),
+    # 16 (Breton)
+    ('one', 'two', 'few', 'many', 'other'),
+    # 17 (Shuar)
+    # CLDR: (missing)
+    ('zero', 'other')
+)
+
+CATEGORIES_BY_LOCALE = {
+    'ach': CATEGORIES_BY_INDEX[1],
+    'af': CATEGORIES_BY_INDEX[1],
+    'an': CATEGORIES_BY_INDEX[1],
+    'ar': CATEGORIES_BY_INDEX[12],
+    'as': CATEGORIES_BY_INDEX[1],
+    'ast': CATEGORIES_BY_INDEX[1],
+    'az': CATEGORIES_BY_INDEX[0],
+    'be': CATEGORIES_BY_INDEX[7],
+    'bg': CATEGORIES_BY_INDEX[1],
+    'bn-BD': CATEGORIES_BY_INDEX[1],
+    'bn-IN': CATEGORIES_BY_INDEX[1],
+    'br': CATEGORIES_BY_INDEX[1],
+    'bs': CATEGORIES_BY_INDEX[1],
+    'ca': CATEGORIES_BY_INDEX[1],
+    'cak': CATEGORIES_BY_INDEX[1],
+    'cs': CATEGORIES_BY_INDEX[8],
+    'cy': CATEGORIES_BY_INDEX[1],
+    'da': CATEGORIES_BY_INDEX[1],
+    'de': CATEGORIES_BY_INDEX[1],
+    'dsb': CATEGORIES_BY_INDEX[10],
+    'el': CATEGORIES_BY_INDEX[1],
+    'en-GB': CATEGORIES_BY_INDEX[1],
+    'en-US': CATEGORIES_BY_INDEX[1],
+    'en-ZA': CATEGORIES_BY_INDEX[1],
+    'eo': CATEGORIES_BY_INDEX[1],
+    'es-AR': CATEGORIES_BY_INDEX[1],
+    'es-CL': CATEGORIES_BY_INDEX[1],
+    'es-ES': CATEGORIES_BY_INDEX[1],
+    'es-MX': CATEGORIES_BY_INDEX[1],
+    'et': CATEGORIES_BY_INDEX[1],
+    'eu': CATEGORIES_BY_INDEX[1],
+    'fa': CATEGORIES_BY_INDEX[0],
+    'ff': CATEGORIES_BY_INDEX[1],
+    'fi': CATEGORIES_BY_INDEX[1],
+    'fr': CATEGORIES_BY_INDEX[2],
+    'fy-NL': CATEGORIES_BY_INDEX[1],
+    'ga-IE': CATEGORIES_BY_INDEX[11],
+    'gd': CATEGORIES_BY_INDEX[4],
+    'gl': CATEGORIES_BY_INDEX[1],
+    'gn': CATEGORIES_BY_INDEX[1],
+    'gu-IN': CATEGORIES_BY_INDEX[2],
+    'he': CATEGORIES_BY_INDEX[1],
+    'hi-IN': CATEGORIES_BY_INDEX[1],
+    'hr': CATEGORIES_BY_INDEX[7],
+    'hsb': CATEGORIES_BY_INDEX[10],
+    'hu': CATEGORIES_BY_INDEX[1],
+    'hy-AM': CATEGORIES_BY_INDEX[1],
+    'ia': CATEGORIES_BY_INDEX[1],
+    'id': CATEGORIES_BY_INDEX[0],
+    'is': CATEGORIES_BY_INDEX[15],
+    'it': CATEGORIES_BY_INDEX[1],
+    'ja': CATEGORIES_BY_INDEX[0],
+    'ja-JP-mac': CATEGORIES_BY_INDEX[0],
+    'jiv': CATEGORIES_BY_INDEX[17],
+    'ka': CATEGORIES_BY_INDEX[0],
+    'kab': CATEGORIES_BY_INDEX[1],
+    'kk': CATEGORIES_BY_INDEX[1],
+    'km': CATEGORIES_BY_INDEX[1],
+    'kn': CATEGORIES_BY_INDEX[1],
+    'ko': CATEGORIES_BY_INDEX[0],
+    'lij': CATEGORIES_BY_INDEX[1],
+    'lo': CATEGORIES_BY_INDEX[0],
+    'lt': CATEGORIES_BY_INDEX[6],
+    'ltg': CATEGORIES_BY_INDEX[3],
+    'lv': CATEGORIES_BY_INDEX[3],
+    'mai': CATEGORIES_BY_INDEX[1],
+    'mk': CATEGORIES_BY_INDEX[15],
+    'ml': CATEGORIES_BY_INDEX[1],
+    'mr': CATEGORIES_BY_INDEX[1],
+    'ms': CATEGORIES_BY_INDEX[1],
+    'my': CATEGORIES_BY_INDEX[1],
+    'nb-NO': CATEGORIES_BY_INDEX[1],
+    'ne-NP': CATEGORIES_BY_INDEX[1],
+    'nl': CATEGORIES_BY_INDEX[1],
+    'nn-NO': CATEGORIES_BY_INDEX[1],
+    'oc': CATEGORIES_BY_INDEX[1],
+    'or': CATEGORIES_BY_INDEX[1],
+    'pa-IN': CATEGORIES_BY_INDEX[1],
+    'pl': CATEGORIES_BY_INDEX[9],
+    'pt-BR': CATEGORIES_BY_INDEX[1],
+    'pt-PT': CATEGORIES_BY_INDEX[1],
+    'rm': CATEGORIES_BY_INDEX[1],
+    'ro': CATEGORIES_BY_INDEX[1],
+    'ru': CATEGORIES_BY_INDEX[7],
+    'si': CATEGORIES_BY_INDEX[1],
+    'sk': CATEGORIES_BY_INDEX[8],
+    'sl': CATEGORIES_BY_INDEX[10],
+    'son': CATEGORIES_BY_INDEX[1],
+    'sq': CATEGORIES_BY_INDEX[1],
+    'sr': CATEGORIES_BY_INDEX[7],
+    'sv-SE': CATEGORIES_BY_INDEX[1],
+    'ta': CATEGORIES_BY_INDEX[1],
+    'te': CATEGORIES_BY_INDEX[1],
+    'th': CATEGORIES_BY_INDEX[0],
+    'tl': CATEGORIES_BY_INDEX[1],
+    'tr': CATEGORIES_BY_INDEX[0],
+    'trs': CATEGORIES_BY_INDEX[1],
+    'uk': CATEGORIES_BY_INDEX[7],
+    'ur': CATEGORIES_BY_INDEX[1],
+    'uz': CATEGORIES_BY_INDEX[0],
+    'vi': CATEGORIES_BY_INDEX[1],
+    'wo': CATEGORIES_BY_INDEX[0],
+    'xh': CATEGORIES_BY_INDEX[1],
+    'zam': CATEGORIES_BY_INDEX[1],
+    'zh-CN': CATEGORIES_BY_INDEX[1],
+    'zh-TW': CATEGORIES_BY_INDEX[0]
+}
diff --git a/third_party/python/compare-locales/compare_locales/tests/test_defines.py b/third_party/python/compare-locales/compare_locales/tests/test_defines.py
index 43ed4e33e5aa..e699148be283 100644
--- a/third_party/python/compare-locales/compare_locales/tests/test_defines.py
+++ b/third_party/python/compare-locales/compare_locales/tests/test_defines.py
@@ -11,8 +11,7 @@ from compare_locales.tests import ParserTestMixin
 mpl2 = '''\
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
-# You can obtain one at http://mozilla.org/MPL/2.0/.
-'''
+# You can obtain one at http://mozilla.org/MPL/2.0/.'''
 
 
 class TestDefinesParser(ParserTestMixin, unittest.TestCase):
@@ -20,7 +19,8 @@ class TestDefinesParser(ParserTestMixin, unittest.TestCase):
     filename = 'defines.inc'
 
     def testBrowser(self):
-        self._test(mpl2 + '''#filter emptyLines
+        self._test(mpl2 + '''
+#filter emptyLines
 
 #define MOZ_LANGPACK_CREATOR mozilla.org
 
@@ -32,13 +32,19 @@ class TestDefinesParser(ParserTestMixin, unittest.TestCase):
 
 ''', (
             ('Comment', mpl2),
+            ('Whitespace', '\n'),
             ('DefinesInstruction', 'filter emptyLines'),
+            ('Whitespace', '\n\n'),
             ('MOZ_LANGPACK_CREATOR', 'mozilla.org'),
+            ('Whitespace', '\n\n'),
             ('Comment', '#define'),
-            ('DefinesInstruction', 'unfilter emptyLines')))
+            ('Whitespace', '\n\n'),
+            ('DefinesInstruction', 'unfilter emptyLines'),
+            ('Junk', '\n\n')))
 
     def testBrowserWithContributors(self):
-        self._test(mpl2 + '''#filter emptyLines
+        self._test(mpl2 + '''
+#filter emptyLines
 
 #define MOZ_LANGPACK_CREATOR mozilla.org
 
@@ -50,15 +56,22 @@ class TestDefinesParser(ParserTestMixin, unittest.TestCase):
 
 ''', (
             ('Comment', mpl2),
+            ('Whitespace', '\n'),
             ('DefinesInstruction', 'filter emptyLines'),
+            ('Whitespace', '\n\n'),
             ('MOZ_LANGPACK_CREATOR', 'mozilla.org'),
+            ('Whitespace', '\n\n'),
             ('Comment', 'non-English'),
+            ('Whitespace', '\n'),
             ('MOZ_LANGPACK_CONTRIBUTORS',
              'Joe Solon'),
-            ('DefinesInstruction', 'unfilter emptyLines')))
+            ('Whitespace', '\n\n'),
+            ('DefinesInstruction', 'unfilter emptyLines'),
+            ('Junk', '\n\n')))
 
     def testCommentWithNonAsciiCharacters(self):
-        self._test(mpl2 + '''#filter emptyLines
+        self._test(mpl2 + '''
+#filter emptyLines
 
 # e.g. #define seamonkey_l10n 
SeaMonkey v češtině #define seamonkey_l10n_long @@ -67,15 +80,81 @@ class TestDefinesParser(ParserTestMixin, unittest.TestCase): ''', ( ('Comment', mpl2), + ('Whitespace', '\n'), ('DefinesInstruction', 'filter emptyLines'), + ('Whitespace', '\n\n'), ('Comment', u'češtině'), + ('Whitespace', '\n'), ('seamonkey_l10n_long', ''), + ('Whitespace', '\n\n'), + ('DefinesInstruction', 'unfilter emptyLines'), + ('Junk', '\n\n'))) + + def test_no_empty_lines(self): + self._test('''#define MOZ_LANGPACK_CREATOR mozilla.org +#define MOZ_LANGPACK_CREATOR mozilla.org +''', ( + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Whitespace', '\n'), + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Whitespace', '\n'))) + + def test_empty_line_between(self): + self._test('''#define MOZ_LANGPACK_CREATOR mozilla.org + +#define MOZ_LANGPACK_CREATOR mozilla.org +''', ( + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Junk', '\n'), + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Whitespace', '\n'))) + + def test_empty_line_at_the_beginning(self): + self._test(''' +#define MOZ_LANGPACK_CREATOR mozilla.org +#define MOZ_LANGPACK_CREATOR mozilla.org +''', ( + ('Junk', '\n'), + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Whitespace', '\n'), + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Whitespace', '\n'))) + + def test_filter_empty_lines(self): + self._test('''#filter emptyLines + +#define MOZ_LANGPACK_CREATOR mozilla.org +#define MOZ_LANGPACK_CREATOR mozilla.org +#unfilter emptyLines''', ( + ('DefinesInstruction', 'filter emptyLines'), + ('Whitespace', '\n\n'), + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Whitespace', '\n'), + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Whitespace', '\n'), ('DefinesInstruction', 'unfilter emptyLines'))) + def test_unfilter_empty_lines_with_trailing(self): + self._test('''#filter emptyLines + +#define MOZ_LANGPACK_CREATOR mozilla.org +#define MOZ_LANGPACK_CREATOR mozilla.org +#unfilter emptyLines +''', ( + ('DefinesInstruction', 'filter emptyLines'), + ('Whitespace', '\n\n'), + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Whitespace', '\n'), + ('MOZ_LANGPACK_CREATOR', 'mozilla.org'), + ('Whitespace', '\n'), + ('DefinesInstruction', 'unfilter emptyLines'), + ('Whitespace', '\n'))) + def testToolkit(self): self._test('''#define MOZ_LANG_TITLE English (US) ''', ( - ('MOZ_LANG_TITLE', 'English (US)'),)) + ('MOZ_LANG_TITLE', 'English (US)'), + ('Whitespace', '\n'))) def testToolkitEmpty(self): self._test('', tuple()) @@ -90,6 +169,23 @@ class TestDefinesParser(ParserTestMixin, unittest.TestCase): self._test('\n\n', (('Junk', '\n\n'),)) self._test(' \n\n', (('Junk', ' \n\n'),)) + def test_whitespace_value(self): + '''Test that there's only one whitespace between key and value + ''' + # funny formatting of trailing whitespace to make it explicit + # and flake-8 happy + self._test('''\ +#define one \n\ +#define two \n\ +#define tre \n\ +''', ( + ('one', ''), + ('Whitespace', '\n'), + ('two', ' '), + ('Whitespace', '\n'), + ('tre', ' '), + ('Whitespace', '\n'),)) + if __name__ == '__main__': unittest.main() diff --git a/third_party/python/compare-locales/compare_locales/tests/test_dtd.py b/third_party/python/compare-locales/compare_locales/tests/test_dtd.py index 5d098ed9eed7..1e6676b11d23 100644 --- a/third_party/python/compare-locales/compare_locales/tests/test_dtd.py +++ b/third_party/python/compare-locales/compare_locales/tests/test_dtd.py @@ -30,12 +30,17 @@ class TestDTD(ParserTestMixin, unittest.TestCase): ''' quoteRef = ( ('good.one', 'one'), - ('Junk', ''), + ('Whitespace', '\n'), + ('Junk', '\n'), ('good.two', 'two'), - ('Junk', ''), + ('Whitespace', '\n'), + ('Junk', '\n'), ('good.three', 'three'), + ('Whitespace', '\n'), ('good.four', 'good \' quote'), - ('good.five', 'good \'quoted\' word'),) + ('Whitespace', '\n'), + ('good.five', 'good \'quoted\' word'), + ('Whitespace', '\n'),) def test_quotes(self): self._test(self.quoteContent, self.quoteRef) @@ -47,7 +52,7 @@ class TestDTD(ParserTestMixin, unittest.TestCase): return qr.sub(lambda m: m.group(0) == '"' and "'" or '"', s) self._test(quot2apos(self.quoteContent), - map(lambda t: (t[0], quot2apos(t[1])), self.quoteRef)) + ((ref[0], quot2apos(ref[1])) for ref in self.quoteRef)) def test_parsed_ref(self): self._test(''' @@ -62,8 +67,13 @@ class TestDTD(ParserTestMixin, unittest.TestCase): --> ''', - (('first', 'string'), ('second', 'string'), - ('Comment', 'out'))) + ( + ('first', 'string'), + ('Whitespace', '\n'), + ('second', 'string'), + ('Whitespace', '\n'), + ('Comment', 'out'), + ('Whitespace', '\n'))) def test_license_header(self): p = parser.getParser('foo.dtd') @@ -71,11 +81,11 @@ class TestDTD(ParserTestMixin, unittest.TestCase): entities = list(p.walk()) self.assert_(isinstance(entities[0], parser.Comment)) self.assertIn('MPL', entities[0].all) - e = entities[1] + e = entities[2] self.assert_(isinstance(e, parser.Entity)) self.assertEqual(e.key, 'foo') self.assertEqual(e.val, 'value') - self.assertEqual(len(entities), 2) + self.assertEqual(len(entities), 4) p.readContents('''\ ', @@ -118,20 +128,12 @@ escaped value"> one, two = list(self.parser) self.assertEqual(one.position(), (1, 1)) self.assertEqual(one.value_position(), (1, 16)) - self.assertEqual(one.position(-1), (2, 1)) + self.assertEqual(one.position(-1), (1, 23)) self.assertEqual(two.position(), (2, 1)) self.assertEqual(two.value_position(), (2, 16)) self.assertEqual(two.value_position(-1), (3, 14)) self.assertEqual(two.value_position(10), (3, 5)) - def test_post(self): - self.parser.readContents('') - a, b = list(self.parser) - self.assertEqual(a.post, '') - self.parser.readContents(' ') - a, b = list(self.parser) - self.assertEqual(a.post, ' ') - def test_word_count(self): self.parser.readContents('''\ @@ -145,6 +147,36 @@ escaped value"> self.assertEqual(c.count_words(), 1) self.assertEqual(d.count_words(), 3) + def test_html_entities(self): + self.parser.readContents('''\ + + + + + +''') + entities = iter(self.parser) + + entity = next(entities) + self.assertEqual(entity.raw_val, '&') + self.assertEqual(entity.val, '&') + + entity = next(entities) + self.assertEqual(entity.raw_val, '&') + self.assertEqual(entity.val, '&') + + entity = next(entities) + self.assertEqual(entity.raw_val, '&') + self.assertEqual(entity.val, '&') + + entity = next(entities) + self.assertEqual(entity.raw_val, '&') + self.assertEqual(entity.val, '&') + + entity = next(entities) + self.assertEqual(entity.raw_val, '&unknownEntity;') + self.assertEqual(entity.val, '&unknownEntity;') + if __name__ == '__main__': unittest.main() diff --git a/third_party/python/compare-locales/compare_locales/tests/test_ftl.py b/third_party/python/compare-locales/compare_locales/tests/test_ftl.py index ce09e10c0029..6c5fe7759ecb 100644 --- a/third_party/python/compare-locales/compare_locales/tests/test_ftl.py +++ b/third_party/python/compare-locales/compare_locales/tests/test_ftl.py @@ -5,6 +5,7 @@ import unittest +from compare_locales import parser from compare_locales.tests import ParserTestMixin @@ -102,7 +103,7 @@ abc = [abc] = list(self.parser) self.assertEqual(abc.key, 'abc') - self.assertEqual(abc.val, '\n A\n B\n C') + self.assertEqual(abc.val, ' A\n B\n C') self.assertEqual(abc.all, 'abc =\n A\n B\n C') def test_message_with_attribute(self): @@ -124,10 +125,81 @@ abc [abc] = list(self.parser) self.assertEqual(abc.key, 'abc') - self.assertEqual(abc.val, '') + self.assertEqual(abc.val, None) self.assertEqual(abc.all, 'abc\n .attr = Attr') attributes = list(abc.attributes) self.assertEqual(len(attributes), 1) attr = attributes[0] self.assertEqual(attr.key, 'attr') self.assertEqual(attr.val, 'Attr') + + def test_non_localizable(self): + self.parser.readContents('''\ +// Resource Comment + +foo = Foo + +// Section Comment +[[ Section Header ]] + +bar = Bar + +// Standalone Comment + +// Baz Comment +baz = Baz +''') + entities = self.parser.walk() + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.Comment)) + self.assertEqual(entity.all, '// Resource Comment') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.Whitespace)) + self.assertEqual(entity.all, '\n\n') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.FluentEntity)) + self.assertEqual(entity.val, 'Foo') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.Whitespace)) + self.assertEqual(entity.all, '\n\n') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.FluentSection)) + self.assertEqual( + entity.all, '// Section Comment\n[[ Section Header ]]') + self.assertEqual(entity.val, 'Section Header ') + self.assertEqual( + entity.entry.comment.content, 'Section Comment') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.Whitespace)) + self.assertEqual(entity.all, '\n\n') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.FluentEntity)) + self.assertEqual(entity.val, 'Bar') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.Whitespace)) + self.assertEqual(entity.all, '\n\n') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.Comment)) + self.assertEqual(entity.all, '// Standalone Comment') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.Whitespace)) + self.assertEqual(entity.all, '\n\n') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.FluentEntity)) + self.assertEqual(entity.val, 'Baz') + self.assertEqual(entity.entry.comment.content, 'Baz Comment') + + entity = next(entities) + self.assertTrue(isinstance(entity, parser.Whitespace)) + self.assertEqual(entity.all, '\n') diff --git a/third_party/python/compare-locales/compare_locales/tests/test_merge.py b/third_party/python/compare-locales/compare_locales/tests/test_merge.py index b5fa81753c4a..0b3486cb5b3f 100644 --- a/third_party/python/compare-locales/compare_locales/tests/test_merge.py +++ b/third_party/python/compare-locales/compare_locales/tests/test_merge.py @@ -293,9 +293,9 @@ class TestDTD(unittest.TestCase, ContentMixin): 'details': { 'l10n.dtd': [ {'error': u'Unparsed content "" ' + u'\'gimmick\'>\n" ' u'from line 2 column 1 to ' - u'line 2 column 22'}, + u'line 3 column 1'}, {'missingEntity': u'bar'}] } }) diff --git a/third_party/python/compare-locales/compare_locales/tests/test_merge_comments.py b/third_party/python/compare-locales/compare_locales/tests/test_merge_comments.py new file mode 100644 index 000000000000..17b4822c640d --- /dev/null +++ b/third_party/python/compare-locales/compare_locales/tests/test_merge_comments.py @@ -0,0 +1,186 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import unittest +from compare_locales.merge import merge_channels + + +class TestMergeComments(unittest.TestCase): + name = "foo.properties" + + def test_comment_added_in_first(self): + channels = (b""" +foo = Foo 1 +# Bar Comment 1 +bar = Bar 1 +""", b""" +foo = Foo 2 +bar = Bar 2 +""") + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +# Bar Comment 1 +bar = Bar 1 +""") + + def test_comment_still_in_last(self): + channels = (b""" +foo = Foo 1 +bar = Bar 1 +""", b""" +foo = Foo 2 +# Bar Comment 2 +bar = Bar 2 +""") + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +# Bar Comment 2 +bar = Bar 1 +""") + + def test_comment_changed(self): + channels = (b""" +foo = Foo 1 +# Bar Comment 1 +bar = Bar 1 +""", b""" +foo = Foo 2 +# Bar Comment 2 +bar = Bar 2 +""") + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +# Bar Comment 1 +bar = Bar 1 +""") + + +class TestMergeStandaloneComments(unittest.TestCase): + name = "foo.properties" + + def test_comment_added_in_first(self): + channels = (b""" +# Standalone Comment 1 + +# Foo Comment 1 +foo = Foo 1 +""", b""" +# Foo Comment 2 +foo = Foo 2 +""") + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b""" +# Standalone Comment 1 + +# Foo Comment 1 +foo = Foo 1 +""") + + def test_comment_still_in_last(self): + channels = (b""" +# Foo Comment 1 +foo = Foo 1 +""", b""" +# Standalone Comment 2 + +# Foo Comment 2 +foo = Foo 2 +""") + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b""" +# Standalone Comment 2 + +# Foo Comment 1 +foo = Foo 1 +""") + + def test_comments_in_both(self): + channels = (b""" +# Standalone Comment 1 + +# Foo Comment 1 +foo = Foo 1 +""", b""" +# Standalone Comment 2 + +# Foo Comment 2 +foo = Foo 2 +""") + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b""" +# Standalone Comment 2 + +# Standalone Comment 1 + +# Foo Comment 1 +foo = Foo 1 +""") + + def test_identical_comments_in_both(self): + channels = (b""" +# Standalone Comment + +# Foo Comment 1 +foo = Foo 1 +""", b""" +# Standalone Comment + +# Foo Comment 2 +foo = Foo 2 +""") + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b""" +# Standalone Comment + +# Foo Comment 1 +foo = Foo 1 +""") + + def test_standalone_which_is_attached_in_first(self): + channels = (b""" +# Ambiguous Comment +foo = Foo 1 + +# Bar Comment 1 +bar = Bar 1 +""", b""" +# Ambiguous Comment + +# Bar Comment 2 +bar = Bar 2 +""") + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b""" +# Ambiguous Comment + +foo = Foo 1 + +# Bar Comment 1 +bar = Bar 1 +""") + + def test_standalone_which_is_attached_in_second(self): + channels = (b""" +# Ambiguous Comment + +# Bar Comment 1 +bar = Bar 1 +""", b""" +# Ambiguous Comment +foo = Foo 1 + +# Bar Comment 2 +bar = Bar 2 +""") + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b""" +# Ambiguous Comment +foo = Foo 1 + +# Bar Comment 1 +bar = Bar 1 +""") diff --git a/third_party/python/compare-locales/compare_locales/tests/test_merge_dtd.py b/third_party/python/compare-locales/compare_locales/tests/test_merge_dtd.py new file mode 100644 index 000000000000..3893ad128bbe --- /dev/null +++ b/third_party/python/compare-locales/compare_locales/tests/test_merge_dtd.py @@ -0,0 +1,133 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import unittest +from compare_locales.merge import merge_channels + + +class TestMergeDTD(unittest.TestCase): + name = "foo.dtd" + maxDiff = None + + def test_no_changes(self): + channels = (b""" + +""", b""" + +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" + +""") + + def test_trailing_whitespace(self): + channels = (b""" + +""", b""" + \n""") + self.assertEqual( + merge_channels(self.name, *channels), b""" + \n""") + + def test_browser_dtd(self): + channels = (b"""\ + + + + + + + + + + + +""", b"""\ + + + + + + + + + + + +""") + + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b"""\ + + + + + + + + + + + + + + + + +""") + + def test_aboutServiceWorkers_dtd(self): + channels = (b"""\ + + + + + + + + + + +""", b"""\ + + + + + + + + + + +""") + + self.assertMultiLineEqual( + merge_channels(self.name, *channels), b"""\ + + + + + + + + + + +""") diff --git a/third_party/python/compare-locales/compare_locales/tests/test_merge_ftl.py b/third_party/python/compare-locales/compare_locales/tests/test_merge_ftl.py new file mode 100644 index 000000000000..c22721bfd406 --- /dev/null +++ b/third_party/python/compare-locales/compare_locales/tests/test_merge_ftl.py @@ -0,0 +1,316 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import unittest +from compare_locales.merge import merge_channels + + +class TestMergeFluent(unittest.TestCase): + name = "foo.ftl" + + def test_no_changes(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +""") + + def test_attribute_in_first(self): + channels = (b""" +foo = Foo 1 + .attr = Attr 1 +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 + .attr = Attr 1 +""") + + def test_attribute_in_last(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 + .attr = Attr 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +""") + + def test_attribute_changed(self): + channels = (b""" +foo = Foo 1 + .attr = Attr 1 +""", b""" +foo = Foo 2 + .attr = Attr 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 + .attr = Attr 1 +""") + + def test_tag_in_first(self): + channels = (b""" +foo = Foo 1 + #tag +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 + #tag +""") + + def test_tag_in_last(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 + #tag +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +""") + + def test_tag_changed(self): + channels = (b""" +foo = Foo 1 + #tag1 +""", b""" +foo = Foo 2 + #tag2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 + #tag1 +""") + + def test_section_in_first(self): + channels = (b""" +[[ Section 1 ]] +foo = Foo 1 +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +[[ Section 1 ]] +foo = Foo 1 +""") + + def test_section_in_last(self): + channels = (b""" +foo = Foo 1 +""", b""" +[[ Section 2 ]] +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +[[ Section 2 ]] +foo = Foo 1 +""") + + def test_section_changed(self): + channels = (b""" +[[ Section 1 ]] +foo = Foo 1 +""", b""" +[[ Section 2 ]] +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +[[ Section 2 ]] +[[ Section 1 ]] +foo = Foo 1 +""") + + def test_message_comment_in_first(self): + channels = (b""" +// Comment 1 +foo = Foo 1 +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +// Comment 1 +foo = Foo 1 +""") + + def test_message_comment_in_last(self): + channels = (b""" +foo = Foo 1 +""", b""" +// Comment 2 +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +""") + + def test_message_comment_changed(self): + channels = (b""" +// Comment 1 +foo = Foo 1 +""", b""" +// Comment 2 +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +// Comment 1 +foo = Foo 1 +""") + + def test_section_comment_in_first(self): + channels = (b""" +// Comment 1 +[[ Section ]] +""", b""" +[[ Section ]] +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +// Comment 1 +[[ Section ]] +""") + + def test_section_comment_in_last(self): + channels = (b""" +[[ Section ]] +""", b""" +// Comment 2 +[[ Section ]] +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +[[ Section ]] +""") + + def test_section_comment_changed(self): + channels = (b""" +// Comment 1 +[[ Section ]] +""", b""" +// Comment 2 +[[ Section ]] +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +// Comment 1 +[[ Section ]] +""") + + def test_standalone_comment_in_first(self): + channels = (b""" +foo = Foo 1 + +// Comment 1 +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 + +// Comment 1 +""") + + def test_standalone_comment_in_last(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 + +// Comment 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 + +// Comment 2 +""") + + def test_standalone_comment_changed(self): + channels = (b""" +foo = Foo 1 + +// Comment 1 +""", b""" +foo = Foo 2 + +// Comment 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 + +// Comment 2 + +// Comment 1 +""") + + def test_resource_comment_in_first(self): + channels = (b""" +// Resource Comment 1 + +foo = Foo 1 +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +// Resource Comment 1 + +foo = Foo 1 +""") + + def test_resource_comment_in_last(self): + channels = (b""" +foo = Foo 1 +""", b""" +// Resource Comment 1 + +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +// Resource Comment 1 + +foo = Foo 1 +""") + + def test_resource_comment_changed(self): + channels = (b""" +// Resource Comment 1 + +foo = Foo 1 +""", b""" +// Resource Comment 2 + +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +// Resource Comment 2 + +// Resource Comment 1 + +foo = Foo 1 +""") diff --git a/third_party/python/compare-locales/compare_locales/tests/test_merge_messages.py b/third_party/python/compare-locales/compare_locales/tests/test_merge_messages.py new file mode 100644 index 000000000000..cd9a3e203bce --- /dev/null +++ b/third_party/python/compare-locales/compare_locales/tests/test_merge_messages.py @@ -0,0 +1,93 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import unittest +from compare_locales.merge import merge_channels + + +class TestMergeTwo(unittest.TestCase): + name = "foo.properties" + + def test_no_changes(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +""") + + def test_message_added_in_first(self): + channels = (b""" +foo = Foo 1 +bar = Bar 1 +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +bar = Bar 1 +""") + + def test_message_still_in_last(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 +bar = Bar 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +bar = Bar 2 +""") + + def test_message_reordered(self): + channels = (b""" +foo = Foo 1 +bar = Bar 1 +""", b""" +bar = Bar 2 +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +bar = Bar 1 +""") + + +class TestMergeThree(unittest.TestCase): + name = "foo.properties" + + def test_no_changes(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 +""", b""" +foo = Foo 3 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +""") + + def test_message_still_in_last(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 +""", b""" +foo = Foo 3 +bar = Bar 3 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +bar = Bar 3 +""") diff --git a/third_party/python/compare-locales/compare_locales/tests/test_merge_properties.py b/third_party/python/compare-locales/compare_locales/tests/test_merge_properties.py new file mode 100644 index 000000000000..931e4752e019 --- /dev/null +++ b/third_party/python/compare-locales/compare_locales/tests/test_merge_properties.py @@ -0,0 +1,41 @@ +# coding=utf8 + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from codecs import decode, encode +import unittest + +from compare_locales.merge import merge_channels + + +class TestMergeProperties(unittest.TestCase): + name = "foo.properties" + + def test_no_changes(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +""") + + def test_encoding(self): + channels = (encode(u""" +foo = Foo 1… +""", "utf8"), encode(u""" +foo = Foo 2… +""", "utf8")) + output = merge_channels(self.name, *channels) + self.assertEqual(output, encode(u""" +foo = Foo 1… +""", "utf8")) + + u_output = decode(output, "utf8") + self.assertEqual(u_output, u""" +foo = Foo 1… +""") diff --git a/third_party/python/compare-locales/compare_locales/tests/test_merge_unknown.py b/third_party/python/compare-locales/compare_locales/tests/test_merge_unknown.py new file mode 100644 index 000000000000..8ac6951e4255 --- /dev/null +++ b/third_party/python/compare-locales/compare_locales/tests/test_merge_unknown.py @@ -0,0 +1,21 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import unittest + +from compare_locales.merge import merge_channels, MergeNotSupportedError + + +class TestMergeUnknown(unittest.TestCase): + name = "foo.unknown" + + def test_not_supported_error(self): + channels = (b""" +foo = Foo 1 +""", b""" +foo = Foo 2 +""") + pattern = "Unsupported file format \(foo\.unknown\)\." + with self.assertRaisesRegexp(MergeNotSupportedError, pattern): + merge_channels(self.name, *channels) diff --git a/third_party/python/compare-locales/compare_locales/tests/test_merge_whitespace.py b/third_party/python/compare-locales/compare_locales/tests/test_merge_whitespace.py new file mode 100644 index 000000000000..a3b39d7c462b --- /dev/null +++ b/third_party/python/compare-locales/compare_locales/tests/test_merge_whitespace.py @@ -0,0 +1,76 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import unittest +from compare_locales.merge import merge_channels + + +class TestMergeWhitespace(unittest.TestCase): + name = "foo.properties" + + def test_trailing_spaces(self): + channels = (b""" +foo = Foo 1 + """, b""" +foo = Foo 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 + """) + + def test_blank_lines_between_messages(self): + channels = (b""" +foo = Foo 1 + +bar = Bar 1 +""", b""" +foo = Foo 2 +bar = Bar 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 + +bar = Bar 1 +""") + + def test_no_eol(self): + channels = (b""" +foo = Foo 1""", b""" +foo = Foo 2 +bar = Bar 2 +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" +foo = Foo 1 +bar = Bar 2 +""") + + def test_still_in_last_with_blank(self): + channels = (b""" + +foo = Foo 1 + +baz = Baz 1 + +""", b""" + +foo = Foo 2 + +bar = Bar 2 + +baz = Baz 2 + +""") + self.assertEqual( + merge_channels(self.name, *channels), b""" + +foo = Foo 1 + +bar = Bar 2 + +baz = Baz 1 + +""") diff --git a/third_party/python/compare-locales/compare_locales/tests/test_parser.py b/third_party/python/compare-locales/compare_locales/tests/test_parser.py index b8f632382ede..e88ff7189002 100644 --- a/third_party/python/compare-locales/compare_locales/tests/test_parser.py +++ b/third_party/python/compare-locales/compare_locales/tests/test_parser.py @@ -8,27 +8,31 @@ from compare_locales import parser class TestParserContext(unittest.TestCase): - def test_lines(self): - "Test that Parser.Context.lines returns 1-based tuples" + def test_linecol(self): + "Should return 1-based line and column numbers." ctx = parser.Parser.Context('''first line second line third line ''') self.assertEqual( - ctx.lines(0, 1), - [(1, 1), (1, 2)] + ctx.linecol(0), + (1, 1) ) self.assertEqual( - ctx.lines(len('first line')), - [(1, len('first line') + 1)] + ctx.linecol(1), + (1, 2) ) self.assertEqual( - ctx.lines(len('first line') + 1), - [(2, 1)] + ctx.linecol(len('first line')), + (1, len('first line') + 1) ) self.assertEqual( - ctx.lines(len(ctx.contents)), - [(4, 1)] + ctx.linecol(len('first line') + 1), + (2, 1) + ) + self.assertEqual( + ctx.linecol(len(ctx.contents)), + (4, 1) ) def test_empty_parser(self): diff --git a/third_party/python/compare-locales/compare_locales/tests/test_properties.py b/third_party/python/compare-locales/compare_locales/tests/test_properties.py index ebf83a1f033c..86730828c5dc 100644 --- a/third_party/python/compare-locales/compare_locales/tests/test_properties.py +++ b/third_party/python/compare-locales/compare_locales/tests/test_properties.py @@ -22,11 +22,15 @@ two_lines_triple = This line is one of two and ends in \\\ and still has another line coming ''', ( ('one_line', 'This is one line'), + ('Whitespace', '\n'), ('two_line', u'This is the first of two lines'), + ('Whitespace', '\n'), ('one_line_trailing', u'This line ends in \\'), + ('Whitespace', '\n'), ('Junk', 'and has junk\n'), ('two_lines_triple', 'This line is one of two and ends in \\' - 'and still has another line coming'))) + 'and still has another line coming'), + ('Whitespace', '\n'))) def testProperties(self): # port of netwerk/test/PropertiesTest.cpp @@ -63,7 +67,11 @@ and an end''', (('bar', 'one line with a # part that looks like a comment ' # file, You can obtain one at http://mozilla.org/MPL/2.0/. foo=value -''', (('Comment', 'MPL'), ('foo', 'value'))) +''', ( + ('Comment', 'MPL'), + ('Whitespace', '\n\n'), + ('foo', 'value'), + ('Whitespace', '\n'))) def test_escapes(self): self.parser.readContents(r''' @@ -87,15 +95,20 @@ second = string # #commented out -''', (('first', 'string'), ('second', 'string'), - ('Comment', 'commented out'))) +''', ( + ('first', 'string'), + ('Whitespace', '\n'), + ('second', 'string'), + ('Whitespace', '\n\n'), + ('Comment', 'commented out'), + ('Whitespace', '\n'))) def test_trailing_newlines(self): self._test('''\ foo = bar \x20\x20 - ''', (('foo', 'bar'),)) + ''', (('foo', 'bar'), ('Whitespace', '\n\n\x20\x20\n '))) def test_just_comments(self): self._test('''\ @@ -105,7 +118,11 @@ foo = bar # LOCALIZATION NOTE These strings are used inside the Promise debugger # which is available as a panel in the Debugger. -''', (('Comment', 'MPL'), ('Comment', 'LOCALIZATION NOTE'))) +''', ( + ('Comment', 'MPL'), + ('Whitespace', '\n\n'), + ('Comment', 'LOCALIZATION NOTE'), + ('Whitespace', '\n'))) def test_just_comments_without_trailing_newline(self): self._test('''\ @@ -115,7 +132,9 @@ foo = bar # LOCALIZATION NOTE These strings are used inside the Promise debugger # which is available as a panel in the Debugger.''', ( - ('Comment', 'MPL'), ('Comment', 'LOCALIZATION NOTE'))) + ('Comment', 'MPL'), + ('Whitespace', '\n\n'), + ('Comment', 'LOCALIZATION NOTE'))) def test_trailing_comment_and_newlines(self): self._test('''\ @@ -124,13 +143,15 @@ foo = bar -''', (('Comment', 'LOCALIZATION NOTE'),)) +''', ( + ('Comment', 'LOCALIZATION NOTE'), + ('Whitespace', '\n\n\n'))) def test_empty_file(self): self._test('', tuple()) self._test('\n', (('Whitespace', '\n'),)) self._test('\n\n', (('Whitespace', '\n\n'),)) - self._test(' \n\n', (('Whitespace', ' \n\n'),)) + self._test(' \n\n', (('Whitespace', '\n\n'),)) def test_positions(self): self.parser.readContents('''\ @@ -146,6 +167,14 @@ escaped value self.assertEqual(two.value_position(-1), (3, 14)) self.assertEqual(two.value_position(10), (3, 3)) + # Bug 1399059 comment 18 + def test_z(self): + self.parser.readContents('''\ +one = XYZ ABC +''') + one, = list(self.parser) + self.assertEqual(one.val, 'XYZ ABC') + if __name__ == '__main__': unittest.main() diff --git a/third_party/python/fluent/PKG-INFO b/third_party/python/fluent/PKG-INFO deleted file mode 100644 index 4c8d934fd4cd..000000000000 --- a/third_party/python/fluent/PKG-INFO +++ /dev/null @@ -1,16 +0,0 @@ -Metadata-Version: 1.1 -Name: fluent -Version: 0.4.2 -Summary: Localization library for expressive translations. -Home-page: https://github.com/projectfluent/python-fluent -Author: Mozilla -Author-email: l10n-drivers@mozilla.org -License: APL 2 -Description: UNKNOWN -Keywords: fluent,localization,l10n -Platform: UNKNOWN -Classifier: Development Status :: 3 - Alpha -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.5 diff --git a/third_party/python/fluent/fluent/migrate/__init__.py b/third_party/python/fluent/fluent/migrate/__init__.py index d1c1bf761daa..3582468d244a 100644 --- a/third_party/python/fluent/fluent/migrate/__init__.py +++ b/third_party/python/fluent/fluent/migrate/__init__.py @@ -1,10 +1,13 @@ # coding=utf8 from .context import MergeContext # noqa: F401 +from .errors import ( # noqa: F401 + MigrationError, NotSupportedError, UnreadableReferenceError +) from .transforms import ( # noqa: F401 Source, COPY, REPLACE_IN_TEXT, REPLACE, PLURALS, CONCAT ) from .helpers import ( # noqa: F401 - LITERAL, EXTERNAL_ARGUMENT, MESSAGE_REFERENCE + EXTERNAL_ARGUMENT, MESSAGE_REFERENCE ) from .changesets import convert_blame_to_changesets # noqa: F401 diff --git a/third_party/python/fluent/fluent/migrate/context.py b/third_party/python/fluent/fluent/migrate/context.py index 8ee779f32f5c..7cb0e023796b 100644 --- a/third_party/python/fluent/fluent/migrate/context.py +++ b/third_party/python/fluent/fluent/migrate/context.py @@ -5,6 +5,11 @@ import os import codecs import logging +try: + from itertools import zip_longest +except ImportError: + from itertools import izip_longest as zip_longest + import fluent.syntax.ast as FTL from fluent.syntax.parser import FluentParser from fluent.syntax.serializer import FluentSerializer @@ -18,7 +23,7 @@ except ImportError: from .cldr import get_plural_categories from .transforms import Source from .merge import merge_resource -from .util import get_message +from .errors import NotSupportedError, UnreadableReferenceError class MergeContext(object): @@ -41,7 +46,7 @@ class MergeContext(object): - A list of `FTL.Message` objects some of whose nodes are special helper or transform nodes: - helpers: LITERAL, EXTERNAL_ARGUMENT, MESSAGE_REFERENCE + helpers: EXTERNAL_ARGUMENT, MESSAGE_REFERENCE transforms: COPY, REPLACE_IN_TEXT, REPLACE, PLURALS, CONCAT """ @@ -79,6 +84,10 @@ class MergeContext(object): f = codecs.open(path, 'r', 'utf8') try: contents = f.read() + except UnicodeDecodeError as err: + logger = logging.getLogger('migrate') + logger.warn('Unable to read file {}: {}'.format(path, err)) + raise err finally: f.close() @@ -94,7 +103,7 @@ class MergeContext(object): logger = logging.getLogger('migrate') for annot in annots: msg = annot.message - logger.warn(u'Syntax error in {}: {}'.format(path, msg)) + logger.warn('Syntax error in {}: {}'.format(path, msg)) return ast @@ -105,52 +114,37 @@ class MergeContext(object): # Transform the parsed result which is an iterator into a dict. return {entity.key: entity.val for entity in parser} - def add_reference(self, path, realpath=None): - """Add an FTL AST to this context's reference resources.""" - fullpath = os.path.join(self.reference_dir, realpath or path) - try: - ast = self.read_ftl_resource(fullpath) - except IOError as err: - logger = logging.getLogger('migrate') - logger.error(u'Missing reference file: {}'.format(path)) - raise err - except UnicodeDecodeError as err: - logger = logging.getLogger('migrate') - logger.error(u'Error reading file {}: {}'.format(path, err)) - raise err - else: - self.reference_resources[path] = ast + def maybe_add_localization(self, path): + """Add a localization resource to migrate translations from. - def add_localization(self, path): - """Add an existing localization resource. + Only legacy resources can be added as migration sources. The resource + may be missing on disk. - If it's an FTL resource, add an FTL AST. Otherwise, it's a legacy - resource. Use a compare-locales parser to create a dict of (key, - string value) tuples. + Uses a compare-locales parser to create a dict of (key, string value) + tuples. """ - fullpath = os.path.join(self.localization_dir, path) - if fullpath.endswith('.ftl'): - try: - ast = self.read_ftl_resource(fullpath) - except IOError: - logger = logging.getLogger('migrate') - logger.warn(u'Missing localization file: {}'.format(path)) - except UnicodeDecodeError as err: - logger = logging.getLogger('migrate') - logger.warn(u'Error reading file {}: {}'.format(path, err)) - else: - self.localization_resources[path] = ast - else: - try: - collection = self.read_legacy_resource(fullpath) - except IOError: - logger = logging.getLogger('migrate') - logger.warn(u'Missing localization file: {}'.format(path)) - else: - self.localization_resources[path] = collection + if path.endswith('.ftl'): + error_message = ( + 'Migrating translations from Fluent files is not supported ' + '({})'.format(path)) + logging.getLogger('migrate').error(error_message) + raise NotSupportedError(error_message) - def add_transforms(self, path, transforms): - """Define transforms for path. + try: + fullpath = os.path.join(self.localization_dir, path) + collection = self.read_legacy_resource(fullpath) + except IOError: + logger = logging.getLogger('migrate') + logger.warn('Missing localization file: {}'.format(path)) + else: + self.localization_resources[path] = collection + + def add_transforms(self, target, reference, transforms): + """Define transforms for target using reference as template. + + `target` is a path of the destination FTL file relative to the + localization directory. `reference` is a path to the template FTL + file relative to the reference directory. Each transform is an extended FTL node with `Transform` nodes as some values. Transforms are stored in their lazy AST form until @@ -165,27 +159,83 @@ class MergeContext(object): acc.add((cur.path, cur.key)) return acc + refpath = os.path.join(self.reference_dir, reference) + try: + ast = self.read_ftl_resource(refpath) + except IOError as err: + error_message = 'Missing reference file: {}'.format(refpath) + logging.getLogger('migrate').error(error_message) + raise UnreadableReferenceError(error_message) + except UnicodeDecodeError as err: + error_message = 'Error reading file {}: {}'.format(refpath, err) + logging.getLogger('migrate').error(error_message) + raise UnreadableReferenceError(error_message) + else: + # The reference file will be used by the merge function as + # a template for serializing the merge results. + self.reference_resources[target] = ast + for node in transforms: # Scan `node` for `Source` nodes and collect the information they # store into a set of dependencies. dependencies = fold(get_sources, node, set()) # Set these sources as dependencies for the current transform. - self.dependencies[(path, node.id.name)] = dependencies + self.dependencies[(target, node.id.name)] = dependencies - path_transforms = self.transforms.setdefault(path, []) + # Read all legacy translation files defined in Source transforms. + for path in set(path for path, _ in dependencies): + self.maybe_add_localization(path) + + path_transforms = self.transforms.setdefault(target, []) path_transforms += transforms + if target not in self.localization_resources: + fullpath = os.path.join(self.localization_dir, target) + try: + ast = self.read_ftl_resource(fullpath) + except IOError: + logger = logging.getLogger('migrate') + logger.info( + 'Localization file {} does not exist and ' + 'it will be created'.format(target)) + except UnicodeDecodeError: + logger = logging.getLogger('migrate') + logger.warn( + 'Localization file {} will be re-created and some ' + 'translations might be lost'.format(target)) + else: + self.localization_resources[target] = ast + def get_source(self, path, key): - """Get an entity value from the localized source. + """Get an entity value from a localized legacy source. Used by the `Source` transform. """ - if path.endswith('.ftl'): - resource = self.localization_resources[path] - return get_message(resource.body, key) - else: - resource = self.localization_resources[path] - return resource.get(key, None) + resource = self.localization_resources[path] + return resource.get(key, None) + + def messages_equal(self, res1, res2): + """Compare messages of two FTL resources. + + Uses FTL.BaseNode.equals to compare all messages in two FTL resources. + If the order or number of messages differ, the result is also False. + """ + def message_id(message): + "Return the message's identifer name for sorting purposes." + return message.id.name + + messages1 = sorted( + (entry for entry in res1.body if isinstance(entry, FTL.Message)), + key=message_id) + messages2 = sorted( + (entry for entry in res2.body if isinstance(entry, FTL.Message)), + key=message_id) + for msg1, msg2 in zip_longest(messages1, messages2): + if msg1 is None or msg2 is None: + return False + if not msg1.equals(msg2): + return False + return True def merge_changeset(self, changeset=None): """Return a generator of FTL ASTs for the changeset. @@ -200,10 +250,11 @@ class MergeContext(object): """ if changeset is None: - # Merge all known legacy translations. + # Merge all known legacy translations. Used in tests. changeset = { (path, key) for path, strings in self.localization_resources.iteritems() + if not path.endswith('.ftl') for key in strings.iterkeys() } @@ -240,11 +291,14 @@ class MergeContext(object): self, reference, current, transforms, in_changeset ) - # If none of the transforms is in the given changeset, the merged - # snapshot is identical to the current translation. We compare - # JSON trees rather then use filtering by `in_changeset` to account - # for translations removed from `reference`. - if snapshot.to_json() == current.to_json(): + # Skip this path if the messages in the merged snapshot are + # identical to those in the current state of the localization file. + # This may happen when: + # + # - none of the transforms is in the changset, or + # - all messages which would be migrated by the context's + # transforms already exist in the current state. + if self.messages_equal(current, snapshot): continue # Store the merged snapshot on the context so that the next merge diff --git a/third_party/python/fluent/fluent/migrate/errors.py b/third_party/python/fluent/fluent/migrate/errors.py new file mode 100644 index 000000000000..e52984f02be2 --- /dev/null +++ b/third_party/python/fluent/fluent/migrate/errors.py @@ -0,0 +1,10 @@ +class MigrationError(ValueError): + pass + + +class NotSupportedError(MigrationError): + pass + + +class UnreadableReferenceError(MigrationError): + pass diff --git a/third_party/python/fluent/fluent/migrate/helpers.py b/third_party/python/fluent/fluent/migrate/helpers.py index 01fa3fb6fc00..f0e9c0886824 100644 --- a/third_party/python/fluent/fluent/migrate/helpers.py +++ b/third_party/python/fluent/fluent/migrate/helpers.py @@ -13,12 +13,6 @@ from __future__ import unicode_literals import fluent.syntax.ast as FTL -def LITERAL(value): - """Create a Pattern with a single TextElement.""" - elements = [FTL.TextElement(value)] - return FTL.Pattern(elements) - - def EXTERNAL_ARGUMENT(name): """Create an ExternalArgument expression.""" diff --git a/third_party/python/fluent/fluent/migrate/merge.py b/third_party/python/fluent/fluent/migrate/merge.py index 3112cb549903..586393e544f5 100644 --- a/third_party/python/fluent/fluent/migrate/merge.py +++ b/third_party/python/fluent/fluent/migrate/merge.py @@ -11,9 +11,10 @@ def merge_resource(ctx, reference, current, transforms, in_changeset): """Transform legacy translations into FTL. Use the `reference` FTL AST as a template. For each en-US string in the - reference, first check if it's in the currently processed changeset with - `in_changeset`; then check for an existing translation in the current FTL - `localization` or for a migration specification in `transforms`. + reference, first check for an existing translation in the current FTL + `localization` and use it if it's present; then if the string has + a transform defined in the migration specification and if it's in the + currently processed changeset, evaluate the transform. """ def merge_body(body): diff --git a/third_party/python/fluent/fluent/migrate/transforms.py b/third_party/python/fluent/fluent/migrate/transforms.py index b1a1ac2412b9..6a8e5e364f80 100644 --- a/third_party/python/fluent/fluent/migrate/transforms.py +++ b/third_party/python/fluent/fluent/migrate/transforms.py @@ -8,36 +8,35 @@ they are evaluated by a MergeContext. All Transforms evaluate to Fluent Patterns. This makes them suitable for defining migrations of values of message, attributes and variants. The special CONCAT Transform is capable of joining multiple Patterns returned by evaluating -other Transforms into a single Pattern. It can also concatenate Fluent -Expressions, like MessageReferences and ExternalArguments. +other Transforms into a single Pattern. It can also concatenate Pattern +elements: TextElements and Placeables. The COPY, REPLACE and PLURALS Transforms inherit from Source which is a special AST Node defining the location (the file path and the id) of the legacy translation. During the migration, the current MergeContext scans the migration spec for Source nodes and extracts the information about all legacy -translations being migrated. Thus, +translations being migrated. For instance, COPY('file.dtd', 'hello') is equivalent to: - LITERAL(Source('file.dtd', 'hello')) + FTL.Pattern([ + FTL.TextElement(Source('file.dtd', 'hello')) + ]) -where LITERAL is a helper defined in the helpers.py module for creating Fluent -Patterns from the text passed as the argument. - -The LITERAL helper and the special REPLACE_IN_TEXT Transforms are useful for -working with text rather than (path, key) source definitions. This is the case -when the migrated translation requires some hardcoded text, e.g. and -when multiple translations become a single one with a DOM overlay. +Sometimes it's useful to work with text rather than (path, key) source +definitions. This is the case when the migrated translation requires some +hardcoded text, e.g. and when multiple translations become a single +one with a DOM overlay. In such cases it's best to use the AST nodes: FTL.Message( id=FTL.Identifier('update-failed'), value=CONCAT( COPY('aboutDialog.dtd', 'update.failed.start'), - LITERAL(''), + FTL.TextElement(''), COPY('aboutDialog.dtd', 'update.failed.linkText'), - LITERAL(''), + FTL.TextElement(''), COPY('aboutDialog.dtd', 'update.failed.end'), ) ) @@ -45,7 +44,8 @@ when multiple translations become a single one with a DOM overlay. The REPLACE_IN_TEXT Transform also takes text as input, making in possible to pass it as the foreach function of the PLURALS Transform. In this case, each slice of the plural string will be run through a REPLACE_IN_TEXT operation. -Those slices are strings, so a REPLACE(path, key, …) isn't suitable for them. +Those slices are strings, so a REPLACE(path, key, …) wouldn't be suitable for +them. FTL.Message( FTL.Identifier('delete-all'), @@ -66,7 +66,13 @@ Those slices are strings, so a REPLACE(path, key, …) isn't suitable for them. from __future__ import unicode_literals import fluent.syntax.ast as FTL -from .helpers import LITERAL +from .errors import NotSupportedError + + +def pattern_from_text(value): + return FTL.Pattern([ + FTL.TextElement(value) + ]) def evaluate(ctx, node): @@ -87,27 +93,25 @@ class Transform(FTL.BaseNode): class Source(Transform): """Declare the source translation to be migrated with other transforms. - When evaluated `Source` returns a simple string value. All \\uXXXX from - the original translations are converted beforehand to the literal - characters they encode. + When evaluated, `Source` returns a simple string value. Escaped characters + are unescaped by the compare-locales parser according to the file format: - HTML entities are left unchanged for now because we can't know if they - should be converted to the characters they represent or not. Consider the - following example in which `&` could be replaced with the literal `&`: + - in properties files: \\uXXXX, + - in DTD files: known named, decimal, and hexadecimal HTML entities. - Privacy & History + Consult the following files for the list of known named HTML entities: - vs. these two examples where the HTML encoding should be preserved: - - Erreur ! - Use /help <command> for more information. + https://github.com/python/cpython/blob/2.7/Lib/htmlentitydefs.py + https://github.com/python/cpython/blob/3.6/Lib/html/entities.py """ - # XXX Perhaps there's a strict subset of HTML entities which must or must - # not be replaced? - def __init__(self, path, key): + if path.endswith('.ftl'): + raise NotSupportedError( + 'Migrating translations from Fluent files is not supported ' + '({})'.format(path)) + self.path = path self.key = key @@ -120,7 +124,7 @@ class COPY(Source): def __call__(self, ctx): source = super(self.__class__, self).__call__(ctx) - return LITERAL(source) + return pattern_from_text(source) class REPLACE_IN_TEXT(Transform): @@ -210,10 +214,12 @@ class PLURALS(Source): Build an `FTL.SelectExpression` with the supplied `selector` and variants extracted from the source. The source needs to be a semicolon-separated list of variants. Each variant will be run through the `foreach` function, - which should return an `FTL.Node` or a `Transform`. + which should return an `FTL.Node` or a `Transform`. By default, the + `foreach` function transforms the source text into a Pattern with a single + TextElement. """ - def __init__(self, path, key, selector, foreach=LITERAL): + def __init__(self, path, key, selector, foreach=pattern_from_text): super(self.__class__, self).__init__(path, key) self.selector = selector self.foreach = foreach @@ -223,6 +229,13 @@ class PLURALS(Source): selector = evaluate(ctx, self.selector) variants = value.split(';') keys = ctx.plural_categories + + # A special case for languages with one plural category. We don't need + # to insert a SelectExpression at all for them. + if len(keys) == len(variants) == 1: + variant, = variants + return evaluate(ctx, self.foreach(variant)) + last_index = min(len(variants), len(keys)) - 1 def createVariant(zipped_enum): @@ -264,7 +277,7 @@ class CONCAT(Transform): return acc raise RuntimeError( - 'CONCAT accepts FTL Patterns and Expressions.' + 'CONCAT accepts FTL Patterns, TextElements and Placeables.' ) # Merge adjecent `FTL.TextElement` nodes. diff --git a/third_party/python/fluent/setup.cfg b/third_party/python/fluent/setup.cfg deleted file mode 100644 index 8bfd5a12f85b..000000000000 --- a/third_party/python/fluent/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[egg_info] -tag_build = -tag_date = 0 - diff --git a/third_party/python/fluent/setup.py b/third_party/python/fluent/setup.py deleted file mode 100644 index 22e7048653a8..000000000000 --- a/third_party/python/fluent/setup.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -from setuptools import setup - -setup(name='fluent', - version='0.4.2', - description='Localization library for expressive translations.', - author='Mozilla', - author_email='l10n-drivers@mozilla.org', - license='APL 2', - url='https://github.com/projectfluent/python-fluent', - keywords=['fluent', 'localization', 'l10n'], - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.5', - ], - packages=['fluent', 'fluent.syntax', 'fluent.migrate'], - package_data={ - 'fluent.migrate': ['cldr_data/*'] - } - ) diff --git a/third_party/python/fluent/tools/fluentfmt.py b/third_party/python/fluent/tools/fluentfmt.py new file mode 100755 index 000000000000..d22d7896a895 --- /dev/null +++ b/third_party/python/fluent/tools/fluentfmt.py @@ -0,0 +1,23 @@ +#!/usr/bin/python + +import sys + +sys.path.append('./') +import codecs +from fluent.syntax import parse, serialize + + +def read_file(path): + with codecs.open(path, 'r', encoding='utf-8') as file: + text = file.read() + return text + + +def pretty_print(fileType, data): + ast = parse(data) + print(serialize(ast)) + +if __name__ == "__main__": + file_type = 'ftl' + f = read_file(sys.argv[1]) + pretty_print(file_type, f) diff --git a/third_party/python/fluent/tools/migrate/README.md b/third_party/python/fluent/tools/migrate/README.md new file mode 100644 index 000000000000..b92aa089fe95 --- /dev/null +++ b/third_party/python/fluent/tools/migrate/README.md @@ -0,0 +1,39 @@ +# Migration Tools + +`migrate-l10n.py` is a CLI script which uses the `fluent.migrate` module under +the hood to run migrations on existing translations. + +## Examples + +The `examples/` directory contains a number of sample migrations. To run them +you'll need at least one clone of a localization repository, e.g. from +https://hg.mozilla.org/l10n-central/. + +Amend your `PYTHONPATH` to make sure that all `fluent.*` modules can be found: + + $ export PYTHONPATH=$(pwd)/../..:$PYTHONPATH + +Then run migrations passing the `examples` directory as the reference: + + $ ./migrate-l10n.py --lang it --reference-dir examples --localization-dir ~/moz/l10n-central/it examples.about_dialog examples.about_downloads examples.bug_1291693 + +Here's what the output should look like: + + Annotating /home/stas/moz/l10n-central/it + Running migration examples.bug_1291693 + Writing to /home/stas/moz/l10n-central/it/browser/branding/official/brand.ftl + Committing changeset: Bug 1291693 - Migrate the menubar to FTL, part 1 + Writing to /home/stas/moz/l10n-central/it/browser/menubar.ftl + Writing to /home/stas/moz/l10n-central/it/browser/toolbar.ftl + Writing to /home/stas/moz/l10n-central/it/browser/branding/official/brand.ftl + Committing changeset: Bug 1291693 - Migrate the menubar to FTL, part 2 + Running migration examples.about_dialog + Writing to /home/stas/moz/l10n-central/it/browser/about_dialog.ftl + Committing changeset: Migrate about:dialog, part 1 + Running migration examples.about_downloads + Writing to /home/stas/moz/l10n-central/it/mobile/about_downloads.ftl + Committing changeset: Migrate about:download in Firefox for Android, part 1 + Writing to /home/stas/moz/l10n-central/it/mobile/about_downloads.ftl + Committing changeset: Migrate about:download in Firefox for Android, part 2 + Writing to /home/stas/moz/l10n-central/it/mobile/about_downloads.ftl + Committing changeset: Migrate about:download in Firefox for Android, part 3 diff --git a/third_party/python/fluent/tools/migrate/blame.py b/third_party/python/fluent/tools/migrate/blame.py new file mode 100644 index 000000000000..076c7f80b317 --- /dev/null +++ b/third_party/python/fluent/tools/migrate/blame.py @@ -0,0 +1,60 @@ +import argparse +import json +import hglib +from hglib.util import b, cmdbuilder +from compare_locales.parser import getParser, Junk + + +class Blame(object): + def __init__(self, repopath): + self.client = hglib.open(repopath) + self.users = [] + self.blame = {} + + def main(self): + args = cmdbuilder( + b('annotate'), self.client.root(), d=True, u=True, T='json') + blame_json = ''.join(self.client.rawcommand(args)) + file_blames = json.loads(blame_json) + + for file_blame in file_blames: + self.handleFile(file_blame) + + return {'authors': self.users, + 'blame': self.blame} + + def handleFile(self, file_blame): + abspath = file_blame['abspath'] + + try: + parser = getParser(abspath) + except UserWarning: + return + + self.blame[abspath] = {} + + parser.readFile(file_blame['path']) + entities, emap = parser.parse() + for e in entities: + if isinstance(e, Junk): + continue + entity_lines = file_blame['lines'][ + (e.value_position()[0] - 1):e.value_position(-1)[0] + ] + # ignore timezone + entity_lines.sort(key=lambda blame: -blame['date'][0]) + line_blame = entity_lines[0] + user = line_blame['user'] + timestamp = line_blame['date'][0] # ignore timezone + if user not in self.users: + self.users.append(user) + userid = self.users.index(user) + self.blame[abspath][e.key] = [userid, timestamp] + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("repopath") + args = parser.parse_args() + blame = Blame(args.repopath) + blimey = blame.main() + print(json.dumps(blimey, indent=4, separators=(',', ': '))) diff --git a/third_party/python/fluent/tools/migrate/examples/__init__.py b/third_party/python/fluent/tools/migrate/examples/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/third_party/python/fluent/tools/migrate/examples/about_dialog.ftl b/third_party/python/fluent/tools/migrate/examples/about_dialog.ftl new file mode 100644 index 000000000000..21901565d404 --- /dev/null +++ b/third_party/python/fluent/tools/migrate/examples/about_dialog.ftl @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +update-failed = + Update failed. +channel-description = + You are currently on the update channel. +community = + { brand-short-name } is designed by , + a working together to keep the Web + open, public and accessible to all. diff --git a/third_party/python/fluent/tools/migrate/examples/about_dialog.py b/third_party/python/fluent/tools/migrate/examples/about_dialog.py new file mode 100644 index 000000000000..e45def1873f7 --- /dev/null +++ b/third_party/python/fluent/tools/migrate/examples/about_dialog.py @@ -0,0 +1,85 @@ +# coding=utf8 + +import fluent.syntax.ast as FTL +from fluent.migrate import ( + CONCAT, EXTERNAL_ARGUMENT, MESSAGE_REFERENCE, COPY, REPLACE +) + + +def migrate(ctx): + """Migrate about:dialog, part {index}""" + + ctx.add_transforms('browser/about_dialog.ftl', 'about_dialog.ftl', [ + FTL.Message( + id=FTL.Identifier('update-failed'), + value=CONCAT( + COPY( + 'browser/chrome/browser/aboutDialog.dtd', + 'update.failed.start' + ), + FTL.TextElement(''), + COPY( + 'browser/chrome/browser/aboutDialog.dtd', + 'update.failed.linkText' + ), + FTL.TextElement(''), + COPY( + 'browser/chrome/browser/aboutDialog.dtd', + 'update.failed.end' + ) + ) + ), + FTL.Message( + id=FTL.Identifier('channel-description'), + value=CONCAT( + COPY( + 'browser/chrome/browser/aboutDialog.dtd', + 'channel.description.start' + ), + FTL.Placeable(EXTERNAL_ARGUMENT('channelname')), + COPY( + 'browser/chrome/browser/aboutDialog.dtd', + 'channel.description.end' + ) + ) + ), + FTL.Message( + id=FTL.Identifier('community'), + value=CONCAT( + REPLACE( + 'browser/chrome/browser/aboutDialog.dtd', + 'community.start2', + { + '&brandShortName;': MESSAGE_REFERENCE( + 'brand-short-name' + ) + } + ), + FTL.TextElement(''), + REPLACE( + 'browser/chrome/browser/aboutDialog.dtd', + 'community.mozillaLink', + { + '&vendorShortName;': MESSAGE_REFERENCE( + 'vendor-short-name' + ) + } + ), + FTL.TextElement(''), + COPY( + 'browser/chrome/browser/aboutDialog.dtd', + 'community.middle2' + ), + FTL.TextElement(''), + COPY( + 'browser/chrome/browser/aboutDialog.dtd', + 'community.creditsLink' + ), + FTL.TextElement(''), + COPY( + 'browser/chrome/browser/aboutDialog.dtd', + 'community.end3' + ) + ) + ), + ]) diff --git a/third_party/python/fluent/tools/migrate/examples/about_downloads.ftl b/third_party/python/fluent/tools/migrate/examples/about_downloads.ftl new file mode 100644 index 000000000000..e591eee86cf8 --- /dev/null +++ b/third_party/python/fluent/tools/migrate/examples/about_downloads.ftl @@ -0,0 +1,38 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +title = Downloads +header = Your Downloads +empty = No Downloads + +open-menuitem + .label = Open +retry-menuitem + .label = Retry +remove-menuitem + .label = Delete +pause-menuitem + .label = Pause +resume-menuitem + .label = Resume +cancel-menuitem + .label = Cancel +remove-all-menuitem + .label = Delete All + +delete-all-title = Delete All +delete-all-message = + { $num -> + [1] Delete this download? + *[other] Delete { $num } downloads? + } + +download-state-downloading = Downloading… +download-state-canceled = Canceled +download-state-failed = Failed +download-state-paused = Paused +download-state-starting = Starting… +download-state-unknown = Unknown + +download-size-unknown = Unknown size diff --git a/third_party/python/fluent/tools/migrate/examples/about_downloads.py b/third_party/python/fluent/tools/migrate/examples/about_downloads.py new file mode 100644 index 000000000000..edfc8234366d --- /dev/null +++ b/third_party/python/fluent/tools/migrate/examples/about_downloads.py @@ -0,0 +1,179 @@ +# coding=utf8 + +import fluent.syntax.ast as FTL +from fluent.migrate import EXTERNAL_ARGUMENT, COPY, PLURALS, REPLACE_IN_TEXT + + +def migrate(ctx): + """Migrate about:download in Firefox for Android, part {index}""" + + ctx.add_transforms('mobile/about_downloads.ftl', 'about_downloads.ftl', [ + FTL.Message( + id=FTL.Identifier('title'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.title' + ) + ), + FTL.Message( + id=FTL.Identifier('header'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.header' + ) + ), + FTL.Message( + id=FTL.Identifier('empty'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.empty' + ) + ), + FTL.Message( + id=FTL.Identifier('open-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.open' + ) + ) + ] + ), + FTL.Message( + id=FTL.Identifier('retry-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.retry' + ) + ) + ] + ), + FTL.Message( + id=FTL.Identifier('remove-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.remove' + ) + ) + ] + ), + FTL.Message( + id=FTL.Identifier('pause-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.pause' + ) + ) + ] + ), + FTL.Message( + id=FTL.Identifier('resume-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.resume' + ) + ) + ] + ), + FTL.Message( + id=FTL.Identifier('cancel-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.cancel' + ) + ) + ] + ), + FTL.Message( + id=FTL.Identifier('remove-all-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'mobile/android/chrome/aboutDownloads.dtd', + 'aboutDownloads.removeAll' + ) + ) + ] + ), + FTL.Message( + id=FTL.Identifier('delete-all-title'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.properties', + 'downloadAction.deleteAll' + ) + ), + FTL.Message( + id=FTL.Identifier('delete-all-message'), + value=PLURALS( + 'mobile/android/chrome/aboutDownloads.properties', + 'downloadMessage.deleteAll', + EXTERNAL_ARGUMENT('num'), + lambda text: REPLACE_IN_TEXT( + text, + { + '#1': EXTERNAL_ARGUMENT('num') + } + ) + ) + ), + FTL.Message( + id=FTL.Identifier('download-state-downloading'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.properties', + 'downloadState.downloading' + ) + ), + FTL.Message( + id=FTL.Identifier('download-state-canceled'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.properties', + 'downloadState.canceled' + ) + ), + FTL.Message( + id=FTL.Identifier('download-state-failed'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.properties', + 'downloadState.failed' + ) + ), + FTL.Message( + id=FTL.Identifier('download-state-paused'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.properties', + 'downloadState.paused' + ) + ), + FTL.Message( + id=FTL.Identifier('download-state-starting'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.properties', + 'downloadState.starting' + ) + ), + FTL.Message( + id=FTL.Identifier('download-size-unknown'), + value=COPY( + 'mobile/android/chrome/aboutDownloads.properties', + 'downloadState.unknownSize' + ) + ), + ]) diff --git a/third_party/python/fluent/tools/migrate/examples/brand.ftl b/third_party/python/fluent/tools/migrate/examples/brand.ftl new file mode 100644 index 000000000000..f9aae63e09f1 --- /dev/null +++ b/third_party/python/fluent/tools/migrate/examples/brand.ftl @@ -0,0 +1,13 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +brand-shorter-name = Firefox +brand-short-name = Firefox +brand-full-name = Mozilla Firefox +vendor-short-name = Mozilla + +trademark-info = + Firefox and the Firefox logos are trademarks of the Mozilla Foundation. + +sync-brand-short-name = Sync diff --git a/third_party/python/fluent/tools/migrate/examples/bug_1291693.py b/third_party/python/fluent/tools/migrate/examples/bug_1291693.py new file mode 100644 index 000000000000..b05327bff8b6 --- /dev/null +++ b/third_party/python/fluent/tools/migrate/examples/bug_1291693.py @@ -0,0 +1,1917 @@ +# coding=utf8 + +import fluent.syntax.ast as FTL +from fluent.migrate import MESSAGE_REFERENCE, COPY, REPLACE + + +def migrate(ctx): + """Bug 1291693 - Migrate the menubar to FTL, part {index}""" + + ctx.add_transforms('browser/menubar.ftl', 'menubar.ftl', [ + FTL.Message( + id=FTL.Identifier('file-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fileMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fileMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('tab-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'tabCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'tabCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('tab-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'tabCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('new-user-context-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'newUserContext.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'newUserContext.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('new-navigator-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'newNavigatorCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'newNavigatorCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('new-navigator-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'newNavigatorCmd.key', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('new-private-window-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'newPrivateWindow.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'newPrivateWindow.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('new-non-remote-window-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'newNonRemoteWindow.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('open-location-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'openLocationCmd.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('open-file-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'openFileCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'openFileCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('open-file-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'openFileCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('close-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'closeCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'closeCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('close-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'closeCmd.key', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('close-window-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'closeWindow.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'closeWindow.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('save-page-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'savePageCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'savePageCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('save-page-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'savePageCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('email-page-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'emailPageCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'emailPageCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('print-setup-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'printSetupCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'printSetupCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('print-preview-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'printPreviewCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'printPreviewCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('print-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'printCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'printCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('print-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'printCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('go-offline-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'goOfflineCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'goOfflineCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('quit-application-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'quitApplicationCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'quitApplicationCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('quit-application-menuitem-win'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'quitApplicationCmdWin2.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'quitApplicationCmdWin2.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('quit-application-menuitem-mac'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'quitApplicationCmdMac2.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('quit-application-command-unix'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'quitApplicationCmdUnix.key', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('edit-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'editMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'editMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('undo-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'undoCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'undoCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('undo-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'undoCmd.key', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('redo-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'redoCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'redoCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('redo-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'redoCmd.key', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('cut-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'cutCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'cutCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('cut-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'cutCmd.key', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('copy-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'copyCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'copyCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('copy-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'copyCmd.key', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('paste-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pasteCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pasteCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('paste-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pasteCmd.key', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('delete-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'deleteCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'deleteCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('select-all-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'selectAllCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'selectAllCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('select-all-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'selectAllCmd.key', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('find-on-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'findOnCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'findOnCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('find-on-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'findOnCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('find-again-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'findAgainCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'findAgainCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('find-again-command1'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'findAgainCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('find-again-command2'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'findAgainCmd.commandkey2', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('find-selection-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'findSelectionCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('bidi-switch-text-direction-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bidiSwitchTextDirectionItem.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bidiSwitchTextDirectionItem.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('bidi-switch-text-direction-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bidiSwitchTextDirectionItem.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('preferences-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'preferencesCmd2.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'preferencesCmd2.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('preferences-menuitem-unix'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'preferencesCmdUnix.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'preferencesCmdUnix.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'viewMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'viewMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-toolbar-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'viewToolbarsMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'viewToolbarsMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-sidebar-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'viewSidebarMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'viewSidebarMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-customize-toolbar-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'viewCustomizeToolbar.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'viewCustomizeToolbar.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoom.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoom.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-enlarge-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomEnlargeCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomEnlargeCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-enlarge-command1'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomEnlargeCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-enlarge-command2'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomEnlargeCmd.commandkey2', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-enlarge-command3'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomEnlargeCmd.commandkey3', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-reduce-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomReduceCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomReduceCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-reduce-command1'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomReduceCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-reduce-command2'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomReduceCmd.commandkey2', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-reset-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomResetCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomResetCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-reset-command1'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomResetCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-reset-command2'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomResetCmd.commandkey2', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-zoom-toggle-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomToggleCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullZoomToggleCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('page-style-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageStyleMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageStyleMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('page-style-no-style-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageStyleNoStyle.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageStyleNoStyle.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('page-style-persistent-only-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageStylePersistentOnly.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageStylePersistentOnly.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('show-all-tabs-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'showAllTabsCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'showAllTabsCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('bidi-switch-page-direction-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bidiSwitchPageDirectionItem.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bidiSwitchPageDirectionItem.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('enter-full-screen-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'enterFullScreenCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'enterFullScreenCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('exit-full-screen-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'exitFullScreenCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'exitFullScreenCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-screen-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullScreenCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullScreenCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('full-screen-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'fullScreenCmd.macCommandKey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('history-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'historyMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'historyMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('show-all-history-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'showAllHistoryCmd2.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('show-all-history-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'showAllHistoryCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('clear-recent-history-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'clearRecentHistory.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('history-synced-tabs-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'syncTabsMenu3.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('history-restore-last-session-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'historyRestoreLastSession.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('history-undo-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'historyUndoMenu.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('history-undo-window-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'historyUndoWindowMenu.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('bookmarks-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bookmarksMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bookmarksMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('show-all-bookmarks-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'showAllBookmarks2.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('show-all-bookmarks-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bookmarksCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('show-all-bookmarks-command-gtk'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bookmarksGtkCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('bookmark-this-page-broadcaster'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bookmarkThisPageCmd.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('edit-this-page-broadcaster'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'editThisBookmarkCmd.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('bookmark-this-page-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bookmarkThisPageCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('subscribe-to-page-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'subscribeToPageMenupopup.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('subscribe-to-page-menupopup'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'subscribeToPageMenupopup.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('add-cur-pages-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'addCurPagesCmd.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('recent-bookmarks-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'recentBookmarks.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('other-bookmarks-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'otherBookmarksCmd.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('personalbar-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'personalbarCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'personalbarCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('tools-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'toolsMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'toolsMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('downloads-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'downloads.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'downloads.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('downloads-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'downloads.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('downloads-command-unix'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'downloadsUnix.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('addons-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'addons.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'addons.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('addons-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'addons.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('sync-sign-in-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + REPLACE( + 'browser/chrome/browser/browser.dtd', + 'syncSignIn.label', + { + '&syncBrand.shortName.label;': MESSAGE_REFERENCE( + 'sync-brand-short-name' + ) + } + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'syncSignIn.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('sync-sync-now-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'syncSyncNowItem.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'syncSyncNowItem.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('sync-re-auth-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + REPLACE( + 'browser/chrome/browser/browser.dtd', + 'syncReAuthItem.label', + { + '&syncBrand.shortName.label;': MESSAGE_REFERENCE( + 'sync-brand-short-name' + ) + } + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'syncReAuthItem.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('sync-toolbar-button'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'syncToolbarButton.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('web-developer-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'webDeveloperMenu.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'webDeveloperMenu.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('page-source-broadcaster'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageSourceCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageSourceCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('page-source-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageSourceCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('page-info-menuitem'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageInfoCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageInfoCmd.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('page-info-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'pageInfoCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('mirror-tab-menu'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'mirrorTabCmd.label', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'mirrorTabCmd.accesskey', + ) + ), + ] + ), + ]) + + ctx.add_transforms('browser/toolbar.ftl', 'toolbar.ftl', [ + FTL.Message( + id=FTL.Identifier('urlbar-textbox'), + attributes=[ + FTL.Attribute( + FTL.Identifier('placeholder'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'urlbar.placeholder2', + ) + ), + FTL.Attribute( + FTL.Identifier('accesskey'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'urlbar.accesskey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-bookmarks-broadcaster'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bookmarksButton.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-bookmarks-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bookmarksCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-bookmarks-command-win'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'bookmarksWinCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-history-broadcaster'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'historyButton.label', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-history-command'), + attributes=[ + FTL.Attribute( + FTL.Identifier('key'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'historySidebarCmd.commandkey', + ) + ), + ] + ), + FTL.Message( + id=FTL.Identifier('view-tabs-broadcaster'), + attributes=[ + FTL.Attribute( + FTL.Identifier('label'), + COPY( + 'browser/chrome/browser/browser.dtd', + 'syncedTabs.sidebar.label', + ) + ), + ] + ), + ]) + + ctx.add_transforms('browser/branding/official/brand.ftl', 'brand.ftl', [ + FTL.Message( + id=FTL.Identifier('brand-shorter-name'), + value=COPY( + 'browser/branding/official/brand.dtd', + 'brandShorterName' + ) + ), + FTL.Message( + id=FTL.Identifier('brand-short-name'), + value=COPY( + 'browser/branding/official/brand.dtd', + 'brandShortName' + ) + ), + FTL.Message( + id=FTL.Identifier('brand-full-name'), + value=COPY( + 'browser/branding/official/brand.dtd', + 'brandFullName' + ) + ), + FTL.Message( + id=FTL.Identifier('vendor-short-name'), + value=COPY( + 'browser/branding/official/brand.dtd', + 'vendorShortName' + ) + ), + FTL.Message( + id=FTL.Identifier('trademark-info'), + value=COPY( + 'browser/branding/official/brand.dtd', + 'trademarkInfo.part1' + ) + ), + FTL.Message( + id=FTL.Identifier('sync-brand-short-name'), + value=COPY( + 'browser/branding/official/brand.properties', + 'syncBrandShortName' + ) + ), + ]) diff --git a/third_party/python/fluent/tools/migrate/examples/menubar.ftl b/third_party/python/fluent/tools/migrate/examples/menubar.ftl new file mode 100644 index 000000000000..c4a2ab59e1a0 --- /dev/null +++ b/third_party/python/fluent/tools/migrate/examples/menubar.ftl @@ -0,0 +1,336 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[[ File menu ]] + +file-menu + .label = File + .accesskey = F +tab-menuitem + .label = New Tab + .accesskey = T +tab-command + .key = t +new-user-context-menu + .label = New Container Tab + .accesskey = C +new-navigator-menuitem + .label = New Window + .accesskey = N +new-navigator-command + .key = N +new-private-window-menuitem + .label = New Private Window + .accesskey = W +new-non-remote-window-menuitem + .label = New Non-e10s Window + +// Only displayed on OS X, and only on windows that aren't main browser windows, +// or when there are no windows but Firefox is still running. +open-location-menuitem + .label = Open Location… +open-file-menuitem + .label = Open File… + .accesskey = O +open-file-command + .key = o + +close-menuitem + .label = Close + .accesskey = C +close-command + .key = W +close-window-menuitem + .label = Close Window + .accesskey = d + +// .accesskey2 is for content area context menu +save-page-menuitem + .label = Save Page As… + .accesskey = A + .accesskey2 = P +save-page-command + .key = s + +email-page-menuitem + .label = Email Link… + .accesskey = E + +print-setup-menuitem + .label = Page Setup… + .accesskey = u +print-preview-menuitem + .label = Print Preview… + .accesskey = v +print-menuitem + .label = Print… + .accesskey = P +print-command + .key = p + +go-offline-menuitem + .label = Work Offline + .accesskey = k + +quit-application-menuitem + .label = Quit + .accesskey = Q +quit-application-menuitem-win + .label = Exit + .accesskey = x +quit-application-menuitem-mac + .label = Quit { brand-shorter-name } +// Used by both Linux and OSX builds +quit-application-command-unix + .key = Q + +[[ Edit menu ]] + +edit-menu + .label = Edit + .accesskey = E +undo-menuitem + .label = Undo + .accesskey = U +undo-command + .key = Z +redo-menuitem + .label = Redo + .accesskey = R +redo-command + .key = Y +cut-menuitem + .label = Cut + .accesskey = t +cut-command + .key = X +copy-menuitem + .label = Copy + .accesskey = C +copy-command + .key = C +paste-menuitem + .label = Paste + .accesskey = P +paste-command + .key = V +delete-menuitem + .label = Delete + .accesskey = D +select-all-menuitem + .label = Select All + .accesskey = A +select-all-command + .key = A + +find-on-menuitem + .label = Find in This Page… + .accesskey = F +find-on-command + .key = f +find-again-menuitem + .label = Find Again + .accesskey = g +find-again-command1 + .key = g +find-again-command2 + .keycode = VK_F3 +find-selection-command + .key = e + +bidi-switch-text-direction-menuitem + .label = Switch Text Direction + .accesskey = w +bidi-switch-text-direction-command + .key = X + +preferences-menuitem + .label = Options + .accesskey = O +preferences-menuitem-unix + .label = Preferences + .accesskey = n + + +[[ View menu ]] + +view-menu + .label = View + .accesskey = V +view-toolbars-menu + .label = Toolbars + .accesskey = T +view-sidebar-menu + .label = Sidebar + .accesskey = e +view-customize-toolbar-menuitem + .label = Customize… + .accesskey = C + +full-zoom-menu + .label = Zoom + .accesskey = Z +full-zoom-enlarge-menuitem + .label = Zoom In + .accesskey = I +full-zoom-enlarge-command1 + .key = + +full-zoom-enlarge-command2 + .key = +full-zoom-enlarge-command3 + .key = "" +full-zoom-reduce-menuitem + .label = Zoom Out + .accesskey = O +full-zoom-reduce-command1 + .key = - +full-zoom-reduce-command2 + .key = "" +full-zoom-reset-menuitem + .label = Reset + .accesskey = R +full-zoom-reset-command1 + .key = 0 +full-zoom-reset-command2 + .key = "" +full-zoom-toggle-menuitem + .label = Zoom Text Only + .accesskey = T + +page-style-menu + .label = Page Style + .accesskey = y +page-style-no-style-menuitem + .label = No Style + .accesskey = n +page-style-persistent-only-menuitem + .label = Basic Page Style + .accesskey = b + +show-all-tabs-menuitem + .label = Show All Tabs + .accesskey = A +bidi-switch-page-direction-menuitem + .label = Switch Page Direction + .accesskey = D + +// Match what Safari and other Apple applications use on OS X Lion. +[[ Full Screen controls ]] + +enter-full-screen-menuitem + .label = Enter Full Screen + .accesskey = F +exit-full-screen-menuitem + .label = Exit Full Screen + .accesskey = F +full-screen-menuitem + .label = Full Screen + .accesskey = F +full-screen-command + .key = f + + +[[ History menu ]] + +history-menu + .label = History + .accesskey = s +show-all-history-menuitem + .label = Show All History +show-all-history-command + .key = H +clear-recent-history-menuitem + .label = Clean Recent History… +history-synced-tabs-menuitem + .label = Synced Tabs +history-restore-last-session-menuitem + .label = Restore Previous Session +history-undo-menu + .label = Recently Closed Tabs +history-undo-window-menu + .label = Recently Closed Windows + + +[[ Bookmarks menu ]] + +bookmarks-menu + .label = Bookmarks + .accesskey = B +show-all-bookmarks-menuitem + .label = Show All Bookmarks +show-all-bookmarks-command + .key = b +// .key should not contain the letters A-F since the are reserved shortcut +// keys on Linux. +show-all-bookmarks-command-gtk + .key = o +bookmark-this-page-broadcaster + .label = Bookmark This Page +edit-this-page-broadcaster + .label = Edit This Page +bookmark-this-page-command + .key = d +subscribe-to-page-menuitem + .label = Subscribe to This Page… +subscribe-to-page-menupopup + .label = Subscribe to This Page… +add-cur-pages-menuitem + .label = Bookmark All Tabs… +recent-bookmarks-menuitem + .label = Recently Bookmarked + +other-bookmarks-menu + .label = Other Bookmarks +personalbar-menu + .label = Bookmarks Toolbar + .accesskey = B + + +[[ Tools menu ]] + +tools-menu + .label = Tools + .accesskey = T +downloads-menuitem + .label = Downloads + .accesskey = D +downloads-command + .key = j +downloads-command-unix + .key = y +addons-menuitem + .label = Add-ons + .accesskey = A +addons-command + .key = A + +sync-sign-in-menuitem + .label = Sign In To { sync-brand-short-name }… + .accesskey = Y +sync-sync-now-menuitem + .label = Sync Now + .accesskey = S +sync-re-auth-menuitem + .label = Reconnect to { sync-brand-short-name }… + .accesskey = R +sync-toolbar-button + .label = Sync + +web-developer-menu + .label = Web Developer + .accesskey = W + +page-source-broadcaster + .label = Page Source + .accesskey = o +page-source-command + .key = u +page-info-menuitem + .label = Page Info + .accesskey = I +page-info-command + .key = i +mirror-tab-menu + .label = Mirror Tab + .accesskey = m diff --git a/third_party/python/fluent/tools/migrate/examples/toolbar.ftl b/third_party/python/fluent/tools/migrate/examples/toolbar.ftl new file mode 100644 index 000000000000..1d056553d19f --- /dev/null +++ b/third_party/python/fluent/tools/migrate/examples/toolbar.ftl @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +urlbar-textbox + .placeholder = Search or enter address + .accesskey = d + + +[[ Toolbar items ]] + +view-bookmarks-broadcaster + .label = Bookmarks +view-bookmarks-command + .key = b +view-bookmarks-command-win + .key = i + +view-history-broadcaster + .label = History +view-history-command + .key = h +view-tabs-broadcaster + .label = Synced Tabs diff --git a/third_party/python/fluent/tools/migrate/migrate-l10n.py b/third_party/python/fluent/tools/migrate/migrate-l10n.py new file mode 100755 index 000000000000..d8cf5cb1ebee --- /dev/null +++ b/third_party/python/fluent/tools/migrate/migrate-l10n.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# coding=utf8 + +import os +import sys +import json +import logging +import argparse +import importlib + +import hglib +from hglib.util import b + +from fluent.migrate import ( + MergeContext, MigrationError, convert_blame_to_changesets +) +from blame import Blame + + +def main(lang, reference_dir, localization_dir, blame, migrations, dry_run): + """Run migrations and commit files with the result.""" + changesets = convert_blame_to_changesets(blame) + client = hglib.open(localization_dir) + + for migration in migrations: + + print('Running migration {}'.format(migration.__name__)) + + # For each migration create a new context. + ctx = MergeContext(lang, reference_dir, localization_dir) + + try: + # Add the migration spec. + migration.migrate(ctx) + except MigrationError as err: + sys.exit(err.message) + + # Keep track of how many changesets we're committing. + index = 0 + + for changeset in changesets: + # Run the migration for the changeset. + snapshot = ctx.serialize_changeset(changeset['changes']) + + # Did it change any files? + if not snapshot: + continue + + # Write serialized FTL files to disk. + for path, content in snapshot.iteritems(): + fullpath = os.path.join(localization_dir, path) + print(' Writing to {}'.format(fullpath)) + if not dry_run: + fulldir = os.path.dirname(fullpath) + if not os.path.isdir(fulldir): + os.makedirs(fulldir) + with open(fullpath, 'w') as f: + f.write(content.encode('utf8')) + f.close() + + index += 1 + author = changeset['author'].encode('utf8') + message = migration.migrate.__doc__.format( + index=index, + author=author + ) + + print(' Committing changeset: {}'.format(message)) + if not dry_run: + client.commit( + b(message), user=b(author), addremove=True + ) + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Migrate translations to FTL.' + ) + parser.add_argument( + 'migrations', metavar='MIGRATION', type=str, nargs='+', + help='migrations to run (Python modules)' + ) + parser.add_argument( + '--lang', type=str, + help='target language code' + ) + parser.add_argument( + '--reference-dir', type=str, + help='directory with reference FTL files' + ) + parser.add_argument( + '--localization-dir', type=str, + help='directory for localization files' + ) + parser.add_argument( + '--blame', type=argparse.FileType(), default=None, + help='path to a JSON with blame information' + ) + parser.add_argument( + '--dry-run', action='store_true', + help='do not write to disk nor commit any changes' + ) + parser.set_defaults(dry_run=False) + + logger = logging.getLogger('migrate') + logger.setLevel(logging.INFO) + + args = parser.parse_args() + + if args.blame: + # Load pre-computed blame from a JSON file. + blame = json.load(args.blame) + else: + # Compute blame right now. + print('Annotating {}'.format(args.localization_dir)) + blame = Blame(args.localization_dir).main() + + main( + lang=args.lang, + reference_dir=args.reference_dir, + localization_dir=args.localization_dir, + blame=blame, + migrations=map(importlib.import_module, args.migrations), + dry_run=args.dry_run + ) diff --git a/third_party/python/fluent/tools/parse.py b/third_party/python/fluent/tools/parse.py new file mode 100755 index 000000000000..8e026c2b6f2c --- /dev/null +++ b/third_party/python/fluent/tools/parse.py @@ -0,0 +1,25 @@ +#!/usr/bin/python + +import sys + +sys.path.append('./') +import codecs +from fluent.syntax import parse +import json + + +def read_file(path): + with codecs.open(path, 'r', encoding='utf-8') as file: + text = file.read() + return text + + +def print_ast(fileType, data): + ast = parse(data) + print(json.dumps(ast.to_json(), indent=2, ensure_ascii=False)) + + +if __name__ == "__main__": + file_type = 'ftl' + f = read_file(sys.argv[1]) + print_ast(file_type, f) diff --git a/third_party/python/fluent/tools/serialize.py b/third_party/python/fluent/tools/serialize.py new file mode 100755 index 000000000000..69b75575fb69 --- /dev/null +++ b/third_party/python/fluent/tools/serialize.py @@ -0,0 +1,23 @@ +#!/usr/bin/python + +import sys +import json + +sys.path.append('./') +import codecs +from fluent.syntax import ast, serialize + + +def read_json(path): + with codecs.open(path, 'r', encoding='utf-8') as file: + return json.load(file) + + +def pretty_print(fileType, data): + resource = ast.from_json(data) + print(serialize(resource)) + +if __name__ == "__main__": + file_type = 'ftl' + f = read_json(sys.argv[1]) + pretty_print(file_type, f) From 476410d40e7cc80b0b245efd45e94d7f36a68d4f Mon Sep 17 00:00:00 2001 From: Michael Kaply Date: Thu, 30 Nov 2017 16:35:34 -0600 Subject: [PATCH 011/219] Bug 1417502 - Update Yandex search params per Yandex. r=flod MozReview-Commit-ID: ERkD09pt0cn --HG-- extra : rebase_source : ea7abe1f7c1e04fef162e1092cbb92058de6a83f --- browser/locales/searchplugins/yandex-az.xml | 2 +- browser/locales/searchplugins/yandex-by.xml | 2 +- browser/locales/searchplugins/yandex-kk.xml | 2 +- browser/locales/searchplugins/yandex-ru.xml | 2 +- browser/locales/searchplugins/yandex-tr.xml | 2 +- browser/locales/searchplugins/yandex-uk.xml | 2 +- mobile/locales/searchplugins/yandex-ru.xml | 4 ++-- mobile/locales/searchplugins/yandex-tr.xml | 4 ++-- mobile/locales/searchplugins/yandex.by.xml | 4 ++-- mobile/locales/searchplugins/yandex.xml | 4 ++-- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/browser/locales/searchplugins/yandex-az.xml b/browser/locales/searchplugins/yandex-az.xml index 8b7d4d301f9a..dd752acbc355 100644 --- a/browser/locales/searchplugins/yandex-az.xml +++ b/browser/locales/searchplugins/yandex-az.xml @@ -10,7 +10,7 @@ - + diff --git a/browser/locales/searchplugins/yandex-by.xml b/browser/locales/searchplugins/yandex-by.xml index ce9c9bfa5786..87ae7f0ad70b 100644 --- a/browser/locales/searchplugins/yandex-by.xml +++ b/browser/locales/searchplugins/yandex-by.xml @@ -10,7 +10,7 @@ - + diff --git a/browser/locales/searchplugins/yandex-kk.xml b/browser/locales/searchplugins/yandex-kk.xml index 78ce3f3d7332..6f1045ddbdee 100644 --- a/browser/locales/searchplugins/yandex-kk.xml +++ b/browser/locales/searchplugins/yandex-kk.xml @@ -10,7 +10,7 @@ - + diff --git a/browser/locales/searchplugins/yandex-ru.xml b/browser/locales/searchplugins/yandex-ru.xml index 8342ab9cfdac..b00c5b9afb22 100644 --- a/browser/locales/searchplugins/yandex-ru.xml +++ b/browser/locales/searchplugins/yandex-ru.xml @@ -10,7 +10,7 @@ - + diff --git a/browser/locales/searchplugins/yandex-tr.xml b/browser/locales/searchplugins/yandex-tr.xml index 8afca0b58935..2a3bc69a8bd4 100644 --- a/browser/locales/searchplugins/yandex-tr.xml +++ b/browser/locales/searchplugins/yandex-tr.xml @@ -10,7 +10,7 @@ - + diff --git a/browser/locales/searchplugins/yandex-uk.xml b/browser/locales/searchplugins/yandex-uk.xml index 18e7661c8cbb..f66cb31d0e7a 100644 --- a/browser/locales/searchplugins/yandex-uk.xml +++ b/browser/locales/searchplugins/yandex-uk.xml @@ -10,7 +10,7 @@ - + diff --git a/mobile/locales/searchplugins/yandex-ru.xml b/mobile/locales/searchplugins/yandex-ru.xml index 85828e2b1d09..68d5f6bbf82d 100644 --- a/mobile/locales/searchplugins/yandex-ru.xml +++ b/mobile/locales/searchplugins/yandex-ru.xml @@ -9,11 +9,11 @@ - + - + diff --git a/mobile/locales/searchplugins/yandex-tr.xml b/mobile/locales/searchplugins/yandex-tr.xml index 17b07f9c4cb0..5d798c746cdb 100644 --- a/mobile/locales/searchplugins/yandex-tr.xml +++ b/mobile/locales/searchplugins/yandex-tr.xml @@ -11,11 +11,11 @@ - + - + diff --git a/mobile/locales/searchplugins/yandex.by.xml b/mobile/locales/searchplugins/yandex.by.xml index a7febdcd545c..a4c6ea7a55c7 100644 --- a/mobile/locales/searchplugins/yandex.by.xml +++ b/mobile/locales/searchplugins/yandex.by.xml @@ -10,11 +10,11 @@ - + - + diff --git a/mobile/locales/searchplugins/yandex.xml b/mobile/locales/searchplugins/yandex.xml index 4daae4695836..801ff5cbefc1 100644 --- a/mobile/locales/searchplugins/yandex.xml +++ b/mobile/locales/searchplugins/yandex.xml @@ -9,11 +9,11 @@ - + - + From f6da4e9275b5339d68079155fa93fd69b3f885bc Mon Sep 17 00:00:00 2001 From: Kilik Kuo Date: Thu, 30 Nov 2017 17:46:06 +0800 Subject: [PATCH 012/219] Bug 1365505 - [P1][Fennec] Enable HLS support by default for all brandings. r=jolin MozReview-Commit-ID: AVGfgKR0zRX --HG-- extra : rebase_source : b1d54aea01ccdf5a4b58e98744efc1b36fb0e941 --- mobile/android/app/mobile.js | 2 -- mobile/android/moz.configure | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index b0761877656f..7acf43fc1a4a 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -609,9 +609,7 @@ pref("media.mediadrm-widevinecdm.visible", true); pref("media.eme.enabled", true); #endif -#ifdef NIGHTLY_BUILD pref("media.hls.enabled", true); -#endif // Whether to suspend decoding of videos in background tabs. pref("media.suspend-bkgnd-video.enabled", true); diff --git a/mobile/android/moz.configure b/mobile/android/moz.configure index dc89b4c50ccb..e31882deedcf 100644 --- a/mobile/android/moz.configure +++ b/mobile/android/moz.configure @@ -80,7 +80,7 @@ project_flag('MOZ_SWITCHBOARD', project_flag('MOZ_ANDROID_HLS_SUPPORT', help='Enable HLS (HTTP Live Streaming) support (currently using the ExoPlayer library)', - default=milestone.is_nightly) + default=True) option(env='MOZ_ANDROID_ACTIVITY_STREAM', help='Enable Activity Stream on Android (replacing the default HomePager)', From b5261ea9427072457e5eab6297cbff8fe49c76b7 Mon Sep 17 00:00:00 2001 From: Kilik Kuo Date: Thu, 30 Nov 2017 17:53:00 +0800 Subject: [PATCH 013/219] Bug 1365505 - [P2][Fennec] Only include exoplayer & HLSDecoder related components when building --with-gradle. r=maliu MozReview-Commit-ID: 7Y0bZynN66i --HG-- extra : rebase_source : 6730f4e09c559074387b3fcf9dd2fb038628d448 --- dom/html/moz.build | 2 +- dom/media/moz.build | 4 ++-- mobile/android/base/Makefile.in | 4 ---- mobile/android/base/moz.build | 4 ++-- mobile/android/geckoview/build.gradle | 3 ++- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/dom/html/moz.build b/dom/html/moz.build index 4ea37e8389f1..e015bf48667b 100644 --- a/dom/html/moz.build +++ b/dom/html/moz.build @@ -248,7 +248,7 @@ LOCAL_INCLUDES += [ FINAL_LIBRARY = 'xul' -if CONFIG['MOZ_ANDROID_HLS_SUPPORT']: +if CONFIG['MOZ_ANDROID_HLS_SUPPORT'] and CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']: DEFINES['MOZ_ANDROID_HLS_SUPPORT'] = True if CONFIG['GNU_CXX']: diff --git a/dom/media/moz.build b/dom/media/moz.build index bb6598481fa0..a34b66ed30b7 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -55,7 +55,7 @@ DIRS += [ 'webvtt', ] -if CONFIG['MOZ_ANDROID_HLS_SUPPORT']: +if CONFIG['MOZ_ANDROID_HLS_SUPPORT'] and CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']: DIRS += ['hls'] if CONFIG['MOZ_FMP4']: @@ -324,7 +324,7 @@ if CONFIG['MOZ_WEBRTC']: DEFINES['MOZILLA_INTERNAL_API'] = True -if CONFIG['MOZ_ANDROID_HLS_SUPPORT']: +if CONFIG['MOZ_ANDROID_HLS_SUPPORT'] and CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']: DEFINES['MOZ_ANDROID_HLS_SUPPORT'] = True include('/ipc/chromium/chromium-config.mozbuild') diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 295e09349349..39832508182a 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -153,10 +153,6 @@ GECKOVIEW_JARS = \ sync-thirdparty.jar \ $(NULL) -ifdef MOZ_ANDROID_HLS_SUPPORT -GECKOVIEW_JARS += exoplayer2.jar -endif - ifdef MOZ_WEBRTC GECKOVIEW_JARS += webrtc.jar endif diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 91528cf873d9..2e94fce755d2 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -462,7 +462,7 @@ gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x 'WakeLockDelegate.java', ]] -if CONFIG['MOZ_ANDROID_HLS_SUPPORT']: +if CONFIG['MOZ_ANDROID_HLS_SUPPORT'] and CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']: gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x for x in [ 'media/GeckoHlsAudioRenderer.java', 'media/GeckoHlsPlayer.java', @@ -1430,7 +1430,7 @@ gvjar.sources += ['generated/org/mozilla/gecko/' + x for x in [ 'process/IProcessManager.java', ]] -if CONFIG['MOZ_ANDROID_HLS_SUPPORT']: +if CONFIG['MOZ_ANDROID_HLS_SUPPORT'] and CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']: gvjar.extra_jars += [ 'exoplayer2.jar', ] diff --git a/mobile/android/geckoview/build.gradle b/mobile/android/geckoview/build.gradle index 0333ece02a46..6825d0a725fe 100644 --- a/mobile/android/geckoview/build.gradle +++ b/mobile/android/geckoview/build.gradle @@ -130,7 +130,8 @@ android { java { srcDir "${topsrcdir}/mobile/android/geckoview/src/thirdparty/java" - if (!mozconfig.substs.MOZ_ANDROID_HLS_SUPPORT) { + if (!mozconfig.substs.MOZ_ANDROID_HLS_SUPPORT || + !mozconfig.substs.get('MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE')) { exclude 'com/google/android/exoplayer2/**' exclude 'org/mozilla/gecko/media/GeckoHlsAudioRenderer.java' exclude 'org/mozilla/gecko/media/GeckoHlsPlayer.java' From 545f2e85ea25cfcb8890e6860d4262fc50ee0408 Mon Sep 17 00:00:00 2001 From: Michael Kaply Date: Thu, 30 Nov 2017 16:21:21 -0600 Subject: [PATCH 014/219] Bug 1416063 - Switch duckduckgo to ddg for mobile. r=flod MozReview-Commit-ID: DID75cJDNeI --HG-- extra : rebase_source : 8f4c42123dd8f1d55b56813b73fb0ea3439c2a9c --- mobile/locales/search/list.json | 73 +++++++++++++++------------- mobile/locales/searchplugins/ddg.xml | 23 +++++++++ 2 files changed, 62 insertions(+), 34 deletions(-) create mode 100644 mobile/locales/searchplugins/ddg.xml diff --git a/mobile/locales/search/list.json b/mobile/locales/search/list.json index 01e94559428e..80db7e49f3fa 100644 --- a/mobile/locales/search/list.json +++ b/mobile/locales/search/list.json @@ -1,7 +1,7 @@ { "default": { "visibleDefaultEngines": [ - "google", "yahoo", "bing", "amazondotcom", "duckduckgo", "twitter", "wikipedia" + "google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia" ] }, "regionOverrides": { @@ -75,14 +75,14 @@ "az": { "default": { "visibleDefaultEngines": [ - "google", "bing", "amazondotcom", "azerdict", "duckduckgo", "wikipedia-az" + "google", "bing", "amazondotcom", "azerdict", "ddg", "wikipedia-az" ] } }, "be": { "default": { "visibleDefaultEngines": [ - "google", "bing", "yahoo", "duckduckgo", "wikipedia-be", "yandex.by" + "google", "bing", "yahoo", "ddg", "wikipedia-be", "yandex.by" ] } }, @@ -96,7 +96,7 @@ "bn-BD": { "default": { "visibleDefaultEngines": [ - "google", "yahoo", "bing", "duckduckgo", "wikipedia-bn" + "google", "yahoo", "bing", "ddg", "wikipedia-bn" ] } }, @@ -131,21 +131,21 @@ "cak": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-espanol", "amazondotcom", "duckduckgo", "wikipedia-es" + "google", "yahoo-espanol", "amazondotcom", "ddg", "wikipedia-es" ] } }, "cs": { "default": { "visibleDefaultEngines": [ - "google", "bing", "duckduckgo", "heureka-cz", "mapy-cz", "seznam-cz", "wikipedia-cz" + "google", "bing", "ddg", "heureka-cz", "mapy-cz", "seznam-cz", "wikipedia-cz" ] } }, "cy": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-en-GB", "bing", "amazon-co-uk", "duckduckgo", "wikipedia-cy" + "google", "yahoo-en-GB", "bing", "amazon-co-uk", "ddg", "wikipedia-cy" ] } }, @@ -159,7 +159,7 @@ "de": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-de", "bing", "amazon-de", "duckduckgo", "qwant", "twitter", "wikipedia-de" + "google", "yahoo-de", "bing", "amazon-de", "ddg", "qwant", "twitter", "wikipedia-de" ] } }, @@ -180,29 +180,29 @@ "en-GB": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-en-GB", "bing", "amazon-co-uk", "duckduckgo", "qwant", "twitter", "wikipedia" + "google", "yahoo-en-GB", "bing", "amazon-co-uk", "ddg", "qwant", "twitter", "wikipedia" ] } }, "en-US": { "default": { "visibleDefaultEngines": [ - "google", "yahoo", "bing", "amazondotcom", "duckduckgo", "twitter", "wikipedia" + "google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia" ] }, "US": { "visibleDefaultEngines": [ - "yahoo", "google-nocodes", "bing", "amazondotcom", "duckduckgo", "twitter", "wikipedia" + "yahoo", "google-nocodes", "bing", "amazondotcom", "ddg", "twitter", "wikipedia" ] }, "CA": { "visibleDefaultEngines": [ - "google-nocodes", "yahoo", "bing", "amazondotcom", "duckduckgo", "twitter", "wikipedia" + "google-nocodes", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia" ] }, "experimental-hidden": { "visibleDefaultEngines": [ - "amazon-ca", "amazon-au", "google-2018" + "amazon-ca", "amazon-au", "google-2018", "duckduckgo" ] } }, @@ -216,7 +216,7 @@ "eo": { "default": { "visibleDefaultEngines": [ - "google", "yahoo", "bing", "duckduckgo", "reta-vortaro", "twitter", "wikipedia-eo" + "google", "yahoo", "bing", "ddg", "reta-vortaro", "twitter", "wikipedia-eo" ] } }, @@ -237,14 +237,14 @@ "es-ES": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-es", "bing", "duckduckgo", "twitter", "wikipedia-es" + "google", "yahoo-es", "bing", "ddg", "twitter", "wikipedia-es" ] } }, "es-MX": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-mx", "bing", "amazondotcom", "duckduckgo", "mercadolibre-mx", "twitter", "wikipedia-es" + "google", "yahoo-mx", "bing", "amazondotcom", "ddg", "mercadolibre-mx", "twitter", "wikipedia-es" ] }, "experimental-hidden": { @@ -291,7 +291,7 @@ "fr": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-france", "bing", "duckduckgo", "qwant", "twitter", "wikipedia-fr" + "google", "yahoo-france", "bing", "ddg", "qwant", "twitter", "wikipedia-fr" ] } }, @@ -317,7 +317,7 @@ "gd": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-en-GB", "bing", "duckduckgo", "faclair-beag", "wikipedia-gd" + "google", "yahoo-en-GB", "bing", "ddg", "faclair-beag", "wikipedia-gd" ] } }, @@ -359,7 +359,7 @@ "hr": { "default": { "visibleDefaultEngines": [ - "google", "yahoo", "bing", "amazon-co-uk", "duckduckgo", "twitter", "wikipedia-hr" + "google", "yahoo", "bing", "amazon-co-uk", "ddg", "twitter", "wikipedia-hr" ] } }, @@ -387,7 +387,7 @@ "ia": { "default": { "visibleDefaultEngines": [ - "google", "yahoo", "bing", "amazondotcom", "duckduckgo", "twitter", "wikipedia-ia" + "google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-ia" ] } }, @@ -408,7 +408,7 @@ "it": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-it", "bing", "duckduckgo", "twitter", "wikipedia-it" + "google", "yahoo-it", "bing", "ddg", "twitter", "wikipedia-it" ] } }, @@ -422,7 +422,7 @@ "ka": { "default": { "visibleDefaultEngines": [ - "google", "yahoo", "bing", "amazondotcom", "duckduckgo", "wikipedia-ka" + "google", "yahoo", "bing", "amazondotcom", "ddg", "wikipedia-ka" ] } }, @@ -506,7 +506,7 @@ "ml": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-in", "bing", "duckduckgo", "twitter", "wikipedia-ml" + "google", "yahoo-in", "bing", "ddg", "twitter", "wikipedia-ml" ] } }, @@ -534,7 +534,7 @@ "nb-NO": { "default": { "visibleDefaultEngines": [ - "google", "duckduckgo", "gulesider-mobile-NO", "twitter", "wikipedia-NO" + "google", "ddg", "gulesider-mobile-NO", "twitter", "wikipedia-NO" ] } }, @@ -548,7 +548,7 @@ "nl": { "default": { "visibleDefaultEngines": [ - "google", "yahoo", "bing", "bolcom-nl", "duckduckgo", "twitter", "wikipedia-nl" + "google", "yahoo", "bing", "bolcom-nl", "ddg", "twitter", "wikipedia-nl" ] }, "experimental-hidden": { @@ -560,7 +560,7 @@ "nn-NO": { "default": { "visibleDefaultEngines": [ - "google", "duckduckgo", "gulesider-mobile-NO", "twitter", "wikipedia-NN" + "google", "ddg", "gulesider-mobile-NO", "twitter", "wikipedia-NN" ] } }, @@ -588,7 +588,7 @@ "pl": { "default": { "visibleDefaultEngines": [ - "google", "bing", "duckduckgo", "twitter", "wikipedia-pl" + "google", "bing", "ddg", "twitter", "wikipedia-pl" ] } }, @@ -614,7 +614,7 @@ "rm": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-ch", "bing", "duckduckgo", "leo_ende_de", "pledarigrond", "wikipedia-rm" + "google", "yahoo-ch", "bing", "ddg", "leo_ende_de", "pledarigrond", "wikipedia-rm" ] } }, @@ -663,7 +663,7 @@ "sr": { "default": { "visibleDefaultEngines": [ - "google", "yahoo", "bing", "duckduckgo", "twitter", "wikipedia-sr" + "google", "yahoo", "bing", "ddg", "twitter", "wikipedia-sr" ] } }, @@ -677,7 +677,7 @@ "ta": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-in", "bing", "amazon-in", "duckduckgo", "wikipedia-ta", "wiktionary-ta" + "google", "yahoo-in", "bing", "amazon-in", "ddg", "wikipedia-ta", "wiktionary-ta" ] } }, @@ -720,7 +720,7 @@ "ur": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-in", "bing", "amazon-in", "duckduckgo", "twitter", "wikipedia-ur" + "google", "yahoo-in", "bing", "amazon-in", "ddg", "twitter", "wikipedia-ur" ] } }, @@ -734,14 +734,14 @@ "vi": { "default": { "visibleDefaultEngines": [ - "google", "coccoc", "duckduckgo", "twitter", "wikipedia-vi" + "google", "coccoc", "ddg", "twitter", "wikipedia-vi" ] } }, "wo": { "default": { "visibleDefaultEngines": [ - "google", "yahoo-france", "bing", "duckduckgo", "wikipedia-wo" + "google", "yahoo-france", "bing", "ddg", "wikipedia-wo" ] } }, @@ -769,7 +769,12 @@ "zh-TW": { "default": { "visibleDefaultEngines": [ - "google", "bing", "duckduckgo", "wikipedia-zh-TW" + "google", "bing", "ddg", "wikipedia-zh-TW" + ] + }, + "experimental-hidden": { + "visibleDefaultEngines": [ + "duckduckgo" ] } } diff --git a/mobile/locales/searchplugins/ddg.xml b/mobile/locales/searchplugins/ddg.xml new file mode 100644 index 000000000000..2e5d1d2802c4 --- /dev/null +++ b/mobile/locales/searchplugins/ddg.xml @@ -0,0 +1,23 @@ + + + +DuckDuckGo +UTF-8 +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAADAFBMVEUAAADeWDPeWTTgWTPfWDPfYDXfWTPeWDP////90QplvEa1tbX+/Pv+/f31zcLibk/66OPeWTX88u8tT47jcVL++vnto4/pknrniG3zxrn77ur439jrmoTgZ0XgYT777OfuqZbsoo7eWzfuq5nqln/9+fj65uDwtqfniW/kd1nfXTn99/b21Mr0x7vxuqvlfF/keFrgZEL89fP88/H3187zwrTsoIvmgWbhaUf0y7/tppPLhHHfXzz88Ozry8LojXPnhmvia0tLqUTZZETdXTrNZjbeWjLzqBj7xQ78ywzevrW6qaTDloroinDNf2nSdFrneyf0rRb3uhL5wg/55N/429PlxbzTt687U4tsZ4rQeGBDokS7dTjaWzTfXjHjbCzlcCvmdijriyLvnRz3txL59PXw+e7p3+P139ja79XUydTu08v0z8X0yr/gwbi8trW1tLTOubPJtrG+47DvsaDvr52ZiZ1lYITFkoPIjHzpkHjke17TclfXb1NuwFJctkVUr0XZYDvCbzjgYS/tjyD1shX6+fv2+/X//fPz8fPq6u/s9+ru4+Xh19//+t7j893g8tra0dn02tPy2dLk0M//9sz20cfDr8HHtsDJ6L22pLu5trbZvLTEuLS2srC54auXg5a/npX/7JGW0oH+53qQz3n+5XDlf2P+4Vjjc1TWa03Wak1mvUdrt0VWsUV2rkN7qUKCpUL+3D2jiDzhZC/iZy7pgCXumB7xpBr4vxD9zwv+0gr99vT65uL54Nrv2tbV6tXFvM/Sz87S7Mnsz8fexMe0q8W+tcKoosH/9MDnxr15g7vG5rqom7h2frbKtbWWjLPovrL/8bGIhLG7rLBib7CMhK+t163xvK1wc63Zs6jwt6e0mqSnk6PtsqLSrKKikKG8pZ+9pJ7opZJjapJFW5DEm4+h1o5wbI6OeI0yT4uY0Iqc1IjrnIZEUYOJzHF7wW7be2Deel9arlvXakz700R9qUOakT6mhjyohTy0ejrGbDbtkyPulB7+1hzwnxsGHbstAAAAB3RSTlMA87tkxxjIlnWKoAAAB3BJREFUaN7NmmV4EzEYx/ELvR5tqQJjXaFFtwIbY8Od4e7u7u7u7u7u7u7u7u7ubkmPcpfkpAz6PPy+FLo0/96rkcaAxI4Tk/EDMePEjoGIG4vxG7HiQgH/zY8UoH0YvxI7RjzGr8SJEZPxKzFjMH7Gd4FkJps1wBHkCLDaTMn+tUBqR7MUWiCgTdHMkfpfCRhSJUoIpEiYKJXh7wVMKbHZSY2Upr8TSB/CeedKHOzOZC0W0SWimDWTOzix920uJH30BXQJAE/2cGduBie3Mzw74Emgi55AmuRmgHAlshmknWNL5AIIc/I00RBIp+etHBTIyBMYxHtIn+6PBSyeqAy1ZGOUyWYJ88St5c8EAnnrhydl1Ekaznsi8A8EkiYBEHsE4xsRdgBJktRnAZ3e85UyMEze16mcWTJ1jlB5kgyeB9brfBTQoSDnsqA4KXG/PB9KZd02xazNwqFU0fkkkFSPnOZkeLqd8qaa3qEUT1YtGpLUB4FAZP+0Nv4/r5pyQCBxTgWFXGmRHwLVBZA5tb/mz1uhcOHC5XM0NHslEmVTUNAix6kKWJD9vfbJW7lE1xKVi3cqeiNHKK+QUUHBip7WoiKQDn2NIHJQ7pzXRwOecs4M8p5GD59OUSANcnBTIgaDygIxXDmnEFC0efVplASSI1diXzHfnbSAolykTD6gjEuuIKBD3swlfudtUyBFChk7RaDaqsMFyEcMEb+xOAeQJqNM2w/HIokUSI8yIFJsn8tAjuQylQ/V1vRyAiFkBHW0ywpoZRYVFsEIlICJgw1ASEWUZ0CaYQf3D8oo0x9gB+JM0gIp4Ufbigd3OgwgQycfawwwdh0/c/5S41zSCkFwQEpJAQPU1mLVqkhDOHjyuZMTJozCHwBlQ4NyMl3UBfusQUogFRkAeVlYt/cdGQpk6CKtkAj+KRUuIPyhs3hkCRbm2MQGQI5waQEbKopSAmHQxViaF2dh3tUDsqSVXqwY4HopTEIgNfxIZmxkVlZ+1Xi2PXS8TT7ZUtMCDvh2JkJAD3gGDwY4j4wVbx4AbmkBJxzgoAUyI11CYAxA9K9kND7sD0S4KhrvnegAkkgL5Ea2oAVSQKPiAyuzfCVqbzRe29Eeq9jPjUZjJWCWKdsw+FJQAsm0gPxGi9nCAAEfoOLdSkDM1cfGijA3ZNbuwTChkpECJjy4EPnYVgAx0wiZCTAaTBxEVLUCwj/dQKQdQxS97RicIi0Aol4Ho7GDZLguYHjW1i4dP37BUusZnkwARhgpYCWCCFGUtfMmHzuWA1IsQqNqfqsb/xe1hLmspEAAfDOAEMjKNgFKcMlq9i1VKP5vyvThP1cMzUUKWIhCgejKNgdyDBx+aPY7OLnA5h99RI3TQgqgKluMEOiFvEwzbv7q71viExSqs47x0oVvXPQTWBnayxTDF8anKV27OtH6HTI+oL1MMHsLNXvBOjUZATkfWHm7qXl5LmWajSVFGaAQRTa+X6p4+bQ3XkrVLYheiNmV8sDEVyg6lzHm8/P380zbB5+cymS6FpVlKC9P4WgLoYSioWsRXU21BtrL2/AIJQSUqyndD6jtdHF2L8DgY7RudXxY9Vq1+9bA+kEzyY5Gbwy6sYWJBF7NR2bttd4hNdbXKY3eiqI7Gt2Tgykvt2xNLhm/FvwV/f1KRUX127TZG6416Z5MryrM1B7uNmvGBewMUzKqEJVspdZhq4qEMusiOpc7sg1xgSSe5lISmkVg04Ya5LpIbmWXkfYysUPY/tuxJTdElS5Tpm6U4F5qZUevTTlyr76EnUrsbRgVMmBrU2p1fZFcpk2fhgu41ASSY6tran9gJrd3s9hQXCFS5fwoTH5/wIQQ4ohOcPWFwZtXca8corRH0y4iV1/lcYF2ivNHhirt0ZgEdLLlZbF9FB5ovatJbQ4SqOyTnQzGdKJtcr+d8HmF5gN5IILCQKey0w/VEV5GbXPki6eTBsBXYfWUf5VGo1lBhGhitMFVPatIkoxum23gbN0fjPDYqFr+/NWYj8s0CNxG4/mzCtXTFje+kUJtc4bGw7xGA550h68rV2l48pPPr02nel5ElO1ebGtBQNOjpwajNyMQACAWn068QADWNrXIRDx5CIGqwricnBBBqmd2XIC4bZYVCSyXE8hpxs7s1E4dQRbBy6igTlITeMZhp47q56bA7Y2lbqig7vEKSPvA0A6Q56bqJ79JdKK2OdIrkKcHcvWaTyvFURQZjJ38+np2Hfpr+Xer5VZQn59v6csqkDcXFsBEW/Neo1nGmz8hfnbt++l7sIlvm6MB6O5JgxFgyM5G9b0FqVfVL8I9SYLA6NwfaFNGwraJCuoczdI5jcBvcv1+2LYupfsD9RsQs1u3BBXUIUfrE70fkfuKS+0GRP0Oh8u4exqgyAmblzOYw+5won8LZbYn5ggBfZbxLuwW6q/v0bQJ7Xp79rTatAkT2+3ZtaJ7tP/6JvDXXWYYkCIM3mX+u9vYzMRtbGZHar/cJ1uCLH94n/wf3YhHE7//aMDvP3vw+w83/P3TE///eAYRO54ff/7zEz3OGZzH90bqAAAAAElFTkSuQmCC + + + + + + + + + + + + + +https://duckduckgo.com + From 250a624c2aecd1134e75ec15aa8c4a9bb7338f43 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 28 Nov 2017 23:21:54 +0100 Subject: [PATCH 015/219] Bug 1407285 - Support spaces in MOZ_MACBUNDLE_NAME and in various Makefile and tools r=glandium MozReview-Commit-ID: Eda1qiPCTJ0 --HG-- extra : rebase_source : c85c5e8bfb5a0f0dc673ea0b6fce1ac1162d9497 --- browser/app/Makefile.in | 32 +++++++++++----------- browser/installer/Makefile.in | 6 ++-- browser/locales/Makefile.in | 4 +-- old-configure.in | 6 ++-- toolkit/mozapps/installer/packager.mk | 2 +- tools/update-packaging/common.sh | 28 +++++++++---------- tools/update-packaging/make_full_update.sh | 8 +++--- 7 files changed, 43 insertions(+), 43 deletions(-) diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in index 320cc1e71ec7..f7e49a9ae214 100644 --- a/browser/app/Makefile.in +++ b/browser/app/Makefile.in @@ -78,24 +78,24 @@ MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_A .PHONY: repackage tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) features rm -rf $(dist_dest) - $(MKDIR) -p $(dist_dest)/Contents/MacOS - $(MKDIR) -p $(dist_dest)/$(LPROJ) - rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents $(dist_dest) --exclude English.lproj - rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(dist_dest)/$(LPROJ) - sed -e 's/%APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' -e 's/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/' -e 's|%MOZ_DEVELOPER_REPO_PATH%|$(topsrcdir)|' -e 's|%MOZ_DEVELOPER_OBJ_PATH%|$(topobjdir)|' $(srcdir)/macbuild/Contents/Info.plist.in > $(dist_dest)/Contents/Info.plist - sed -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(dist_dest)/$(LPROJ)/InfoPlist.strings - rsync -a --exclude-from='$(srcdir)/macbuild/Contents/MacOS-files.in' $(DIST)/bin/ $(dist_dest)/Contents/Resources - rsync -a --include-from='$(srcdir)/macbuild/Contents/MacOS-files.in' --exclude '*' $(DIST)/bin/ $(dist_dest)/Contents/MacOS - $(RM) $(dist_dest)/Contents/MacOS/$(MOZ_APP_NAME) - rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) $(dist_dest)/Contents/MacOS - cp -RL $(DIST)/branding/firefox.icns $(dist_dest)/Contents/Resources/firefox.icns - cp -RL $(DIST)/branding/document.icns $(dist_dest)/Contents/Resources/document.icns - $(MKDIR) -p $(dist_dest)/Contents/Library/LaunchServices + $(MKDIR) -p '$(dist_dest)/Contents/MacOS' + $(MKDIR) -p '$(dist_dest)/$(LPROJ)' + rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents '$(dist_dest)' --exclude English.lproj + rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ '$(dist_dest)/$(LPROJ)' + sed -e 's/%APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' -e 's/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/' -e 's|%MOZ_DEVELOPER_REPO_PATH%|$(topsrcdir)|' -e 's|%MOZ_DEVELOPER_OBJ_PATH%|$(topobjdir)|' $(srcdir)/macbuild/Contents/Info.plist.in > '$(dist_dest)/Contents/Info.plist' + sed -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > '$(dist_dest)/$(LPROJ)/InfoPlist.strings' + rsync -a --exclude-from='$(srcdir)/macbuild/Contents/MacOS-files.in' $(DIST)/bin/ '$(dist_dest)/Contents/Resources' + rsync -a --include-from='$(srcdir)/macbuild/Contents/MacOS-files.in' --exclude '*' $(DIST)/bin/ '$(dist_dest)/Contents/MacOS' + $(RM) '$(dist_dest)/Contents/MacOS/$(MOZ_APP_NAME)' + rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) '$(dist_dest)/Contents/MacOS' + cp -RL $(DIST)/branding/firefox.icns '$(dist_dest)/Contents/Resources/firefox.icns' + cp -RL $(DIST)/branding/document.icns '$(dist_dest)/Contents/Resources/document.icns' + $(MKDIR) -p '$(dist_dest)/Contents/Library/LaunchServices' ifdef MOZ_UPDATER - mv -f $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater $(dist_dest)/Contents/Library/LaunchServices - ln -s ../../../../Library/LaunchServices/org.mozilla.updater $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater + mv -f '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater' '$(dist_dest)/Contents/Library/LaunchServices' + ln -s ../../../../Library/LaunchServices/org.mozilla.updater '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater' endif - printf APPLMOZB > $(dist_dest)/Contents/PkgInfo + printf APPLMOZB > '$(dist_dest)/Contents/PkgInfo' endif .PHONY: features diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in index cf0aaab67e1f..e3bc79992f27 100644 --- a/browser/installer/Makefile.in +++ b/browser/installer/Makefile.in @@ -110,19 +110,19 @@ include $(topsrcdir)/toolkit/mozapps/installer/packager.mk ifeq (bundle, $(MOZ_FS_LAYOUT)) BINPATH = $(_BINPATH) -DEFINES += -DAPPNAME=$(_APPNAME) +DEFINES += -DAPPNAME='$(_APPNAME)' else # Every other platform just winds up in dist/bin BINPATH = bin endif -DEFINES += -DBINPATH=$(BINPATH) +DEFINES += -DBINPATH='$(BINPATH)' ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) RESPATH = $(_APPNAME)/Contents/Resources else RESPATH = $(BINPATH) endif -DEFINES += -DRESPATH=$(RESPATH) +DEFINES += -DRESPATH="$(RESPATH)" LPROJ_ROOT = $(firstword $(subst -, ,$(AB_CD))) ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) diff --git a/browser/locales/Makefile.in b/browser/locales/Makefile.in index f2661af39675..b0371b1acb93 100644 --- a/browser/locales/Makefile.in +++ b/browser/locales/Makefile.in @@ -171,10 +171,10 @@ endif ident: @printf 'fx_revision ' @$(PYTHON) $(topsrcdir)/config/printconfigsetting.py \ - $(STAGEDIST)/application.ini App SourceStamp + '$(STAGEDIST)'/application.ini App SourceStamp @printf 'buildid ' @$(PYTHON) $(topsrcdir)/config/printconfigsetting.py \ - $(STAGEDIST)/application.ini App BuildID + '$(STAGEDIST)'/application.ini App BuildID # test target, depends on make package # try to repack x-test, with just toolkit/defines.inc being there diff --git a/old-configure.in b/old-configure.in index f99ceb3a16cd..21db2fb4e2d8 100644 --- a/old-configure.in +++ b/old-configure.in @@ -4537,13 +4537,13 @@ MOZ_ARG_WITH_STRING(macbundlename-prefix, Prefix for MOZ_MACBUNDLE_NAME], [ MOZ_MACBUNDLE_NAME_PREFIX="$withval"]) -MOZ_MACBUNDLE_NAME=`echo $MOZ_APP_DISPLAYNAME | tr -d ' '` +MOZ_MACBUNDLE_NAME=$MOZ_APP_DISPLAYNAME if test "$MOZ_MACBUNDLE_NAME_PREFIX"; then - MOZ_MACBUNDLE_NAME="${MOZ_MACBUNDLE_NAME_PREFIX}${MOZ_MACBUNDLE_NAME}" + MOZ_MACBUNDLE_NAME="${MOZ_MACBUNDLE_NAME_PREFIX} ${MOZ_MACBUNDLE_NAME}" fi if test "$MOZ_DEBUG"; then - MOZ_MACBUNDLE_NAME=${MOZ_MACBUNDLE_NAME}Debug.app + MOZ_MACBUNDLE_NAME="${MOZ_MACBUNDLE_NAME} Debug.app" else MOZ_MACBUNDLE_NAME=${MOZ_MACBUNDLE_NAME}.app fi diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk index 32bbe1f66d3e..f610e6597627 100644 --- a/toolkit/mozapps/installer/packager.mk +++ b/toolkit/mozapps/installer/packager.mk @@ -46,7 +46,7 @@ stage-package: multilocale.json locale-manifest.in $(MOZ_PKG_MANIFEST) $(MOZ_PKG $(if $(JARLOG_DIR),$(addprefix --jarlog ,$(wildcard $(JARLOG_FILE_AB_CD)))) \ $(if $(OPTIMIZEJARS),--optimizejars) \ $(addprefix --compress ,$(JAR_COMPRESSION)) \ - $(MOZ_PKG_MANIFEST) $(DIST) $(DIST)/$(MOZ_PKG_DIR)$(if $(MOZ_PKG_MANIFEST),,$(_BINPATH)) \ + $(MOZ_PKG_MANIFEST) '$(DIST)' '$(DIST)'/$(MOZ_PKG_DIR)$(if $(MOZ_PKG_MANIFEST),,$(_BINPATH)) \ $(if $(filter omni,$(MOZ_PACKAGER_FORMAT)),$(if $(NON_OMNIJAR_FILES),--non-resource $(NON_OMNIJAR_FILES))) $(PYTHON) $(MOZILLA_DIR)/toolkit/mozapps/installer/find-dupes.py $(DEFINES) $(ACDEFINES) $(MOZ_PKG_DUPEFLAGS) $(DIST)/$(MOZ_PKG_DIR) ifndef MOZ_IS_COMM_TOPDIR diff --git a/tools/update-packaging/common.sh b/tools/update-packaging/common.sh index 1fbf8f0c1b97..d3f6533d0569 100755 --- a/tools/update-packaging/common.sh +++ b/tools/update-packaging/common.sh @@ -83,15 +83,15 @@ make_add_instruction() { # before performing this add instruction. testdir=$(echo "$f" | sed 's/\(.*distribution\/extensions\/[^\/]*\)\/.*/\1/') notice " add-if \"$testdir\" \"$f\"" - echo "add-if \"$testdir\" \"$f\"" >> $filev2 + echo "add-if \"$testdir\" \"$f\"" >> "$filev2" if [ ! $filev3 = "" ]; then - echo "add-if \"$testdir\" \"$f\"" >> $filev3 + echo "add-if \"$testdir\" \"$f\"" >> "$filev3" fi else notice " add \"$f\"$forced" - echo "add \"$f\"" >> $filev2 + echo "add \"$f\"" >> "$filev2" if [ ! $filev3 = "" ]; then - echo "add \"$f\"" >> $filev3 + echo "add \"$f\"" >> "$filev3" fi fi } @@ -138,12 +138,12 @@ make_patch_instruction() { # before performing this add instruction. testdir=$(echo "$f" | sed 's/\(.*distribution\/extensions\/[^\/]*\)\/.*/\1/') notice " patch-if \"$testdir\" \"$f.patch\" \"$f\"" - echo "patch-if \"$testdir\" \"$f.patch\" \"$f\"" >> $filev2 - echo "patch-if \"$testdir\" \"$f.patch\" \"$f\"" >> $filev3 + echo "patch-if \"$testdir\" \"$f.patch\" \"$f\"" >> "$filev2" + echo "patch-if \"$testdir\" \"$f.patch\" \"$f\"" >> "$filev3" else notice " patch \"$f.patch\" \"$f\"" - echo "patch \"$f.patch\" \"$f\"" >> $filev2 - echo "patch \"$f.patch\" \"$f\"" >> $filev3 + echo "patch \"$f.patch\" \"$f\"" >> "$filev2" + echo "patch \"$f.patch\" \"$f\"" >> "$filev3" fi } @@ -172,18 +172,18 @@ append_remove_instructions() { if [ ! $(echo "$f" | grep -c '^#') = 1 ]; then if [ $(echo "$f" | grep -c '\/$') = 1 ]; then notice " rmdir \"$f\"" - echo "rmdir \"$f\"" >> $filev2 - echo "rmdir \"$f\"" >> $filev3 + echo "rmdir \"$f\"" >> "$filev2" + echo "rmdir \"$f\"" >> "$filev3" elif [ $(echo "$f" | grep -c '\/\*$') = 1 ]; then # Remove the * f=$(echo "$f" | sed -e 's:\*$::') notice " rmrfdir \"$f\"" - echo "rmrfdir \"$f\"" >> $filev2 - echo "rmrfdir \"$f\"" >> $filev3 + echo "rmrfdir \"$f\"" >> "$filev2" + echo "rmrfdir \"$f\"" >> "$filev3" else notice " remove \"$f\"" - echo "remove \"$f\"" >> $filev2 - echo "remove \"$f\"" >> $filev3 + echo "remove \"$f\"" >> "$filev2" + echo "remove \"$f\"" >> "$filev3" fi fi fi diff --git a/tools/update-packaging/make_full_update.sh b/tools/update-packaging/make_full_update.sh index a25cbc1ebd9e..5ff098e01b33 100755 --- a/tools/update-packaging/make_full_update.sh +++ b/tools/update-packaging/make_full_update.sh @@ -67,13 +67,13 @@ list_files files popd # Add the type of update to the beginning of the update manifests. -> $updatemanifestv2 -> $updatemanifestv3 +> "$updatemanifestv2" +> "$updatemanifestv3" notice "" notice "Adding type instruction to update manifests" notice " type complete" -echo "type \"complete\"" >> $updatemanifestv2 -echo "type \"complete\"" >> $updatemanifestv3 +echo "type \"complete\"" >> "$updatemanifestv2" +echo "type \"complete\"" >> "$updatemanifestv3" notice "" notice "Adding file add instructions to update manifests" From 211750c567ee8f8a0044985653c69645e020b952 Mon Sep 17 00:00:00 2001 From: yulia Date: Thu, 30 Nov 2017 11:21:00 +0100 Subject: [PATCH 016/219] Bug 1419801 - Update Debugger Frontend (11-22). jdescottes, jlast r=jdescottes MozReview-Commit-ID: Kihg60TpspW --HG-- extra : rebase_source : 64ea5bdcdc353b228ea39486fbf645a40591f9a3 --- devtools/client/debugger/new/README.mozilla | 2 +- devtools/client/debugger/new/debugger.css | 5 + devtools/client/debugger/new/debugger.js | 147 ++++++++++++--- devtools/client/debugger/new/parser-worker.js | 178 ++++++++++++------ .../mochitest/browser_dbg-debugger-buttons.js | 2 +- .../mochitest/browser_dbg-editor-select.js | 5 +- .../browser_dbg-expressions-error.js | 5 +- .../mochitest/browser_dbg-pretty-print.js | 4 +- .../test/mochitest/browser_dbg-sourcemaps2.js | 14 +- .../mochitest/browser_dbg-wasm-sourcemaps.js | 1 + .../debugger/new/test/mochitest/head.js | 34 ++-- .../client/locales/en-US/debugger.properties | 8 + 12 files changed, 296 insertions(+), 109 deletions(-) diff --git a/devtools/client/debugger/new/README.mozilla b/devtools/client/debugger/new/README.mozilla index 937e9c289496..c62f99f7c6f1 100644 --- a/devtools/client/debugger/new/README.mozilla +++ b/devtools/client/debugger/new/README.mozilla @@ -1,7 +1,7 @@ This is the debugger.html project output. See https://github.com/devtools-html/debugger.html -Taken from upstream commit: d2e91e574acbe3d5b546508d028bd278eaabd286 +Taken from upstream commit: efa4ca367dadb1bb54525f9fe305dd38c0ec6323 Packages: - babel-plugin-transform-es2015-modules-commonjs @6.26.0 diff --git a/devtools/client/debugger/new/debugger.css b/devtools/client/debugger/new/debugger.css index e0bc4eec9adc..f1df5b5285f5 100644 --- a/devtools/client/debugger/new/debugger.css +++ b/devtools/client/debugger/new/debugger.css @@ -1758,6 +1758,11 @@ html .toggle-button-end.vertical svg { .source-footer .blackbox-summary { color: var(--theme-body-color); } + +.source-footer .mapped-source { + color: var(--theme-body-color); + padding: 2.5px; +} /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at . */ diff --git a/devtools/client/debugger/new/debugger.js b/devtools/client/debugger/new/debugger.js index dd3e19419fbe..a9d4b132c893 100644 --- a/devtools/client/debugger/new/debugger.js +++ b/devtools/client/debugger/new/debugger.js @@ -14266,7 +14266,7 @@ module.exports = " { + this.props.setExpandedState(expandedState); + }; + + const onCollapse = (item, expandedState) => { + this.props.setExpandedState(expandedState); + }; + const isEmpty = sourceTree.contents.length === 0; const treeProps = { key: isEmpty ? "empty" : "full", @@ -35563,8 +35587,8 @@ class SourcesTree extends _react.Component { listItems, highlightItems, expanded, - onExpand: (item, expandedState) => setExpandedState(expandedState), - onCollapse: (item, expandedState) => setExpandedState(expandedState), + onExpand, + onCollapse, renderItem: this.renderItem }; @@ -35607,7 +35631,9 @@ class SourcesTree extends _react.Component { * file, You can obtain one at . */ // React -exports.default = (0, _reactRedux.connect)(state => { + + +const mapStateToProps = state => { return { shownSource: (0, _selectors.getShownSource)(state), selectedSource: (0, _selectors.getSelectedSource)(state), @@ -35616,7 +35642,14 @@ exports.default = (0, _reactRedux.connect)(state => { projectRoot: (0, _selectors.getProjectDirectoryRoot)(state), sources: (0, _selectors.getSources)(state) }; -}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SourcesTree); +}; + +const actionCreators = { + setExpandedState: _sourceTree.setExpandedState, + selectSource: _sources.selectSource +}; + +exports.default = (0, _reactRedux.connect)(mapStateToProps, actionCreators)(SourcesTree); /***/ }), /* 1554 */ @@ -36247,6 +36280,8 @@ var _devtoolsConfig = __webpack_require__(1355); var _source = __webpack_require__(1356); +var _sources = __webpack_require__(1369); + var _editor = __webpack_require__(1358); var _PaneToggle = __webpack_require__(1407); @@ -36257,6 +36292,10 @@ __webpack_require__(1322); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at . */ + class SourceFooter extends _react.PureComponent { prettyPrintButton() { const { selectedSource, togglePrettyPrint } = this.props; @@ -36371,6 +36410,35 @@ class SourceFooter extends _react.PureComponent { ); } + renderSourceSummary() { + const { mappedSource, jumpToMappedLocation, selectedSource } = this.props; + if (mappedSource) { + const bundleSource = mappedSource.toJS(); + const filename = (0, _source.getFilename)(bundleSource); + const tooltip = L10N.getFormatStr("sourceFooter.mappedSourceTooltip", filename); + const title = L10N.getFormatStr("sourceFooter.mappedSource", filename); + const mappedSourceLocation = { + sourceId: selectedSource.get("id"), + line: 1, + column: 1 + }; + return _react2.default.createElement( + "button", + { + className: "mapped-source", + onClick: () => jumpToMappedLocation(mappedSourceLocation), + title: tooltip + }, + _react2.default.createElement( + "span", + null, + title + ) + ); + } + return null; + } + render() { const { selectedSource, horizontal } = this.props; @@ -36382,18 +36450,19 @@ class SourceFooter extends _react.PureComponent { "div", { className: "source-footer" }, this.renderCommands(), + this.renderSourceSummary(), this.renderToggleButton() ); } -} /* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at . */ +} exports.default = (0, _reactRedux.connect)(state => { const selectedSource = (0, _selectors.getSelectedSource)(state); const selectedId = selectedSource && selectedSource.get("id"); + const source = selectedSource.toJS(); return { selectedSource, + mappedSource: (0, _sources.getGeneratedSource)(state, source), prettySource: (0, _selectors.getPrettySource)(state, selectedId), endPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "end") }; @@ -39846,7 +39915,7 @@ class Breakpoints extends _react.Component { } render() { - const { breakpoints, selectedSource, editor, sourceMetaData } = this.props; + const { breakpoints, selectedSource, editor } = this.props; if (!selectedSource || !breakpoints || selectedSource.get("isBlackBoxed")) { return null; @@ -39860,7 +39929,6 @@ class Breakpoints extends _react.Component { key: (0, _breakpoint.makeLocationId)(bp.location), breakpoint: bp, selectedSource: selectedSource, - sourceMetaData: sourceMetaData, editor: editor }); }) @@ -39872,8 +39940,7 @@ class Breakpoints extends _react.Component { exports.default = (0, _reactRedux.connect)(state => ({ breakpoints: (0, _visibleBreakpoints2.default)(state), - selectedSource: (0, _selectors.getSelectedSource)(state), - sourceMetaData: (0, _selectors.getSourceMetaData)(state, (0, _selectors.getSelectedSource)(state).id) + selectedSource: (0, _selectors.getSelectedSource)(state) }), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Breakpoints); /***/ }), @@ -39934,7 +40001,7 @@ class Breakpoint extends _react.Component { } addBreakpoint() { - const { breakpoint, editor, selectedSource, sourceMetaData } = this.props; + const { breakpoint, editor, selectedSource } = this.props; // Hidden Breakpoints are never rendered on the client if (breakpoint.hidden) { @@ -39950,8 +40017,6 @@ class Breakpoint extends _react.Component { const sourceId = selectedSource.get("id"); const line = (0, _editor.toEditorLine)(sourceId, breakpoint.location.line); - (0, _editor.showSourceText)(editor, selectedSource.toJS(), sourceMetaData); - editor.codeMirror.setGutterMarker(line, "breakpoints", makeMarker(breakpoint.disabled)); editor.codeMirror.addLineClass(line, "line", "new-breakpoint"); @@ -41324,8 +41389,6 @@ class SecondaryPanes extends _react.Component { getStartItems() { const scopesContent = this.props.horizontal ? this.getScopeItem() : null; - const isPaused = () => !!this.props.pauseData; - const items = [{ header: L10N.getStr("breakpoints.header"), className: "breakpoints-pane", @@ -41339,8 +41402,7 @@ class SecondaryPanes extends _react.Component { opened: _prefs.prefs.callStackVisible, onToggle: opened => { _prefs.prefs.callStackVisible = opened; - }, - shouldOpen: isPaused + } }, scopesContent]; if ((0, _devtoolsConfig.isEnabled)("eventListeners")) { @@ -46022,6 +46084,39 @@ exports.default = Badge; // removed by extract-text-webpack-plugin +/***/ }), +/* 1706 */, +/* 1707 */, +/* 1708 */, +/* 1709 */, +/* 1710 */, +/* 1711 */, +/* 1712 */, +/* 1713 */, +/* 1714 */, +/* 1715 */, +/* 1716 */, +/* 1717 */, +/* 1718 */, +/* 1719 */, +/* 1720 */, +/* 1721 */, +/* 1722 */, +/* 1723 */, +/* 1724 */, +/* 1725 */, +/* 1726 */, +/* 1727 */, +/* 1728 */, +/* 1729 */, +/* 1730 */, +/* 1731 */, +/* 1732 */, +/* 1733 */ +/***/ (function(module, exports) { + +module.exports = "" + /***/ }) /******/ ]); }); \ No newline at end of file diff --git a/devtools/client/debugger/new/parser-worker.js b/devtools/client/debugger/new/parser-worker.js index 93d7ba4e916f..60a607282b68 100644 --- a/devtools/client/debugger/new/parser-worker.js +++ b/devtools/client/debugger/new/parser-worker.js @@ -27467,66 +27467,8 @@ module.exports = function(module) { /***/ }), /* 794 */, -/* 795 */ -/***/ (function(module, exports, __webpack_require__) { - -var createToPairs = __webpack_require__(812), - keys = __webpack_require__(205); - -/** - * Creates an array of own enumerable string keyed-value pairs for `object` - * which can be consumed by `_.fromPairs`. If `object` is a map or set, its - * entries are returned. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @alias entries - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the key-value pairs. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.toPairs(new Foo); - * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) - */ -var toPairs = createToPairs(keys); - -module.exports = toPairs; - - -/***/ }), -/* 796 */ -/***/ (function(module, exports, __webpack_require__) { - -var arrayMap = __webpack_require__(110); - -/** - * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array - * of key-value pairs for `object` corresponding to the property names of `props`. - * - * @private - * @param {Object} object The object to query. - * @param {Array} props The property names to get values for. - * @returns {Object} Returns the key-value pairs. - */ -function baseToPairs(object, props) { - return arrayMap(props, function(key) { - return [key, object[key]]; - }); -} - -module.exports = baseToPairs; - - -/***/ }), +/* 795 */, +/* 796 */, /* 797 */, /* 798 */, /* 799 */ @@ -27711,7 +27653,7 @@ module.exports = matchesStrictComparable; /* 812 */ /***/ (function(module, exports, __webpack_require__) { -var baseToPairs = __webpack_require__(796), +var baseToPairs = __webpack_require__(1757), getTag = __webpack_require__(198), mapToArray = __webpack_require__(203), setToPairs = __webpack_require__(813); @@ -36616,7 +36558,7 @@ exports.getVariablesInLocalScope = getVariablesInLocalScope; exports.getVariablesInScope = getVariablesInScope; exports.isExpressionInScope = isExpressionInScope; -var _toPairs = __webpack_require__(795); +var _toPairs = __webpack_require__(1756); var _toPairs2 = _interopRequireDefault(_toPairs); @@ -41822,6 +41764,118 @@ function extendsComponent(classes) { return result; } +/***/ }), +/* 1704 */, +/* 1705 */, +/* 1706 */, +/* 1707 */, +/* 1708 */, +/* 1709 */, +/* 1710 */, +/* 1711 */, +/* 1712 */, +/* 1713 */, +/* 1714 */, +/* 1715 */, +/* 1716 */, +/* 1717 */, +/* 1718 */, +/* 1719 */, +/* 1720 */, +/* 1721 */, +/* 1722 */, +/* 1723 */, +/* 1724 */, +/* 1725 */, +/* 1726 */, +/* 1727 */, +/* 1728 */, +/* 1729 */, +/* 1730 */, +/* 1731 */, +/* 1732 */, +/* 1733 */, +/* 1734 */, +/* 1735 */, +/* 1736 */, +/* 1737 */, +/* 1738 */, +/* 1739 */, +/* 1740 */, +/* 1741 */, +/* 1742 */, +/* 1743 */, +/* 1744 */, +/* 1745 */, +/* 1746 */, +/* 1747 */, +/* 1748 */, +/* 1749 */, +/* 1750 */, +/* 1751 */, +/* 1752 */, +/* 1753 */, +/* 1754 */, +/* 1755 */, +/* 1756 */ +/***/ (function(module, exports, __webpack_require__) { + +var createToPairs = __webpack_require__(812), + keys = __webpack_require__(205); + +/** + * Creates an array of own enumerable string keyed-value pairs for `object` + * which can be consumed by `_.fromPairs`. If `object` is a map or set, its + * entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entries + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairs(new Foo); + * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) + */ +var toPairs = createToPairs(keys); + +module.exports = toPairs; + + +/***/ }), +/* 1757 */ +/***/ (function(module, exports, __webpack_require__) { + +var arrayMap = __webpack_require__(110); + +/** + * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array + * of key-value pairs for `object` corresponding to the property names of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the key-value pairs. + */ +function baseToPairs(object, props) { + return arrayMap(props, function(key) { + return [key, object[key]]; + }); +} + +module.exports = baseToPairs; + + /***/ }) /******/ ]); }); \ No newline at end of file diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-debugger-buttons.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-debugger-buttons.js index c39d53e959ef..205d3ffc6d96 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-debugger-buttons.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-debugger-buttons.js @@ -43,7 +43,7 @@ add_task(async function() { assertPausedLocation(dbg); // resume - await clickResume(dbg) + await clickResume(dbg); await waitForPaused(dbg); assertPausedLocation(dbg); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-select.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-select.js index c99685d5426e..6e2d1963ef30 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-select.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-select.js @@ -47,5 +47,8 @@ add_task(async function() { await waitForLoadedSource(dbg, "long.js"); assertPausedLocation(dbg); - ok(isVisibleInEditor(dbg, findElement(dbg, "breakpoint")), "Breakpoint is visible"); + ok( + isVisibleInEditor(dbg, findElement(dbg, "breakpoint")), + "Breakpoint is visible" + ); }); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions-error.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions-error.js index c5dd7c013823..078cd40d37b4 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions-error.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions-error.js @@ -47,7 +47,7 @@ async function editExpression(dbg, input) { info("updating the expression"); dblClickElement(dbg, "expressionNode", 1); // Position cursor reliably at the end of the text. - const evaluation = waitForDispatch(dbg, "EVALUATE_EXPRESSION") + const evaluation = waitForDispatch(dbg, "EVALUATE_EXPRESSION"); pressKey(dbg, "End"); type(dbg, input); pressKey(dbg, "Enter"); @@ -59,7 +59,7 @@ async function editExpression(dbg, input) { * resume, and wait for the expression to finish being evaluated. */ async function addBadExpression(dbg, input) { - const evaluation = waitForDispatch(dbg, "EVALUATE_EXPRESSION") + const evaluation = waitForDispatch(dbg, "EVALUATE_EXPRESSION"); findElementWithSelector(dbg, expressionSelectors.input).focus(); type(dbg, input); @@ -70,7 +70,6 @@ async function addBadExpression(dbg, input) { ok(dbg.selectors.isEvaluatingExpression(dbg.getState())); await resume(dbg); await evaluation; - } add_task(async function() { diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print.js index 846aab4870fb..38576cb52be4 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print.js @@ -32,8 +32,8 @@ add_task(async function() { // The pretty-print button should go away in the pretty-printed // source. - ok(!findElement(dbg, "editorFooter"), "Footer is hidden"); + ok(!findElement(dbg, "prettyPrintButton"), "Pretty Print Button is hidden"); await selectSource(dbg, "math.min.js"); - ok(findElement(dbg, "editorFooter"), "Footer is hidden"); + ok(findElement(dbg, "prettyPrintButton"), "Pretty Print Button is visible"); }); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js index a85204e1cfeb..74096615937d 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js @@ -4,7 +4,11 @@ function assertBpInGutter(dbg, lineNumber) { const el = findElement(dbg, "breakpoint"); const bpLineNumber = +el.querySelector(".CodeMirror-linenumber").innerText; - is(bpLineNumber, lineNumber, "Breakpoint is on the correct line in the gutter"); + is( + bpLineNumber, + lineNumber, + "Breakpoint is on the correct line in the gutter" + ); } // Tests loading sourcemapped sources, setting breakpoints, and @@ -38,4 +42,12 @@ add_task(async function() { await waitForPaused(dbg); assertPausedLocation(dbg); + + // Tests the existence of the sourcemap link in the original source. + ok(findElement(dbg, "sourceMapLink"), "Sourcemap link in original source"); + await selectSource(dbg, "main.min.js"); + ok( + !findElement(dbg, "sourceMapLink"), + "No Sourcemap link exists in generated source" + ); }); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-wasm-sourcemaps.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-wasm-sourcemaps.js index aa6da211c3e8..dab3864d560f 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-wasm-sourcemaps.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-wasm-sourcemaps.js @@ -26,6 +26,7 @@ add_task(async function() { await waitForPaused(dbg); await waitForLoadedSource(dbg, "average.c"); assertPausedLocation(dbg); + toggleCallStack(dbg); const frames = findAllElements(dbg, "frames"); const firstFrameTitle = frames[0].querySelector(".title").textContent; diff --git a/devtools/client/debugger/new/test/mochitest/head.js b/devtools/client/debugger/new/test/mochitest/head.js index e4f269cbd5a8..d224a1fabb91 100644 --- a/devtools/client/debugger/new/test/mochitest/head.js +++ b/devtools/client/debugger/new/test/mochitest/head.js @@ -295,7 +295,9 @@ function assertDebugLine(dbg, line) { const url = source.get("url"); ok( false, - `Looks like the source ${url} is still loading. Try adding waitForLoadedSource in the test.` + `Looks like the source ${ + url + } is still loading. Try adding waitForLoadedSource in the test.` ); return; } @@ -305,8 +307,9 @@ function assertDebugLine(dbg, line) { "Line is highlighted as paused" ); - const debugLine = findElementWithSelector(dbg, ".new-debug-line") - || findElementWithSelector(dbg, ".new-debug-line-error"); + const debugLine = + findElementWithSelector(dbg, ".new-debug-line") || + findElementWithSelector(dbg, ".new-debug-line-error"); ok(isVisibleInEditor(dbg, debugLine), "debug line is visible"); @@ -553,8 +556,11 @@ function waitForLoadedSources(dbg) { return waitForState( dbg, state => { - const sources = dbg.selectors.getSources(state).valueSeq().toJS() - return !sources.some(source => source.loadedState == "loading") + const sources = dbg.selectors + .getSources(state) + .valueSeq() + .toJS(); + return !sources.some(source => source.loadedState == "loading"); }, "loaded source" ); @@ -835,7 +841,6 @@ function type(dbg, string) { string.split("").forEach(char => EventUtils.synthesizeKey(char, {}, dbg.win)); } - /* * Checks to see if the inner element is visible inside the editor. * @@ -874,12 +879,14 @@ function isVisible(outerEl, innerEl) { const outerRect = outerEl.getBoundingClientRect(); const verticallyVisible = - (innerRect.top >= outerRect.top || innerRect.bottom <= outerRect.bottom) - || (innerRect.top < outerRect.top && innerRect.bottom > outerRect.bottom); + innerRect.top >= outerRect.top || + innerRect.bottom <= outerRect.bottom || + (innerRect.top < outerRect.top && innerRect.bottom > outerRect.bottom); const horizontallyVisible = - (innerRect.left >= outerRect.left || innerRect.right <= outerRect.right) - || (innerRect.left < outerRect.left && innerRect.right > outerRect.right); + innerRect.left >= outerRect.left || + innerRect.right <= outerRect.right || + (innerRect.left < outerRect.left && innerRect.right > outerRect.right); const visible = verticallyVisible && horizontallyVisible; return visible; @@ -891,7 +898,9 @@ const selectors = { expressionNode: i => `.expressions-list .expression-container:nth-child(${i}) .object-label`, expressionValue: i => - `.expressions-list .expression-container:nth-child(${i}) .object-delimiter + *`, + `.expressions-list .expression-container:nth-child(${ + i + }) .object-delimiter + *`, expressionClose: i => `.expressions-list .expression-container:nth-child(${i}) .close`, expressionNodes: ".expressions-list .tree-node", @@ -914,7 +923,8 @@ const selectors = { stepOut: ".stepOut.active", stepIn: ".stepIn.active", toggleBreakpoints: ".breakpoints-toggle", - prettyPrintButton: ".prettyPrint", + prettyPrintButton: ".source-footer .prettyPrint", + sourceMapLink: ".source-footer .mapped-source", sourcesFooter: ".sources-panel .source-footer", editorFooter: ".editor-pane .source-footer", sourceNode: i => `.sources-list .tree-node:nth-child(${i})`, diff --git a/devtools/client/locales/en-US/debugger.properties b/devtools/client/locales/en-US/debugger.properties index 8a8d31a61b47..9323676a563e 100644 --- a/devtools/client/locales/en-US/debugger.properties +++ b/devtools/client/locales/en-US/debugger.properties @@ -470,6 +470,14 @@ sourceFooter.unblackbox.accesskey=b # with a blackboxed source sourceFooter.blackboxed=Blackboxed source +# LOCALIZATION NOTE (sourceFooter.mappedSource): Text associated +# with a mapped source. %S is replaced by the source map origin. +sourceFooter.mappedSource=(From %S) + +# LOCALIZATION NOTE (sourceFooter.mappedSourceTooltip): Tooltip text associated +# with a mapped source. %S is replaced by the source map origin. +sourceFooter.mappedSourceTooltip=(Source mapped from %S) + # LOCALIZATION NOTE (sourceFooter.codeCoverage): Text associated # with a code coverage button sourceFooter.codeCoverage=Code coverage From 38cd380efa3aa7b65174c8edf99856b4a710b53d Mon Sep 17 00:00:00 2001 From: Luke Chang Date: Wed, 29 Nov 2017 17:02:59 +0800 Subject: [PATCH 017/219] Bug 1421551 - Make FormAutofillParent singleton and expose the initialized status. r=MattN MozReview-Commit-ID: IMd25HcNTMa --HG-- extra : rebase_source : 1cbb863199fc78074d7ab8a5547c87cb8b3f8c58 --- .../formautofill/FormAutofillParent.jsm | 26 ++++++++++++++++++- browser/extensions/formautofill/bootstrap.js | 5 ++-- .../test/unit/test_activeStatus.js | 2 +- .../formautofill/test/unit/test_getRecords.js | 2 +- .../test/unit/test_savedFieldNames.js | 2 +- 5 files changed, 30 insertions(+), 7 deletions(-) diff --git a/browser/extensions/formautofill/FormAutofillParent.jsm b/browser/extensions/formautofill/FormAutofillParent.jsm index f112f139b067..5944312f5978 100644 --- a/browser/extensions/formautofill/FormAutofillParent.jsm +++ b/browser/extensions/formautofill/FormAutofillParent.jsm @@ -27,7 +27,9 @@ "use strict"; -this.EXPORTED_SYMBOLS = ["FormAutofillParent"]; +// We expose a singleton from this module. Some tests may import the +// constructor via a backstage pass. +this.EXPORTED_SYMBOLS = ["formAutofillParent"]; const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; @@ -78,10 +80,30 @@ FormAutofillParent.prototype = { */ _active: null, + /** + * The status of Form Autofill's initialization. + */ + _initialized: false, + + /** + * Exposes the status of Form Autofill's initialization. It can be used to + * determine whether Form Autofill is available for current users. + * + * @returns {boolean} Whether FormAutofillParent is initialized. + */ + get initialized() { + return this._initialized; + }, + /** * Initializes ProfileStorage and registers the message handler. */ async init() { + if (this._initialized) { + return; + } + this._initialized = true; + Services.obs.addObserver(this, "sync-pane-loaded"); Services.ppmm.addMessageListener("FormAutofill:InitStorage", this); Services.ppmm.addMessageListener("FormAutofill:GetRecords", this); @@ -552,3 +574,5 @@ FormAutofillParent.prototype = { histogram.add(`${formType}-${fillingType}`, Date.now() - startedFillingMS); }, }; + +this.formAutofillParent = new FormAutofillParent(); diff --git a/browser/extensions/formautofill/bootstrap.js b/browser/extensions/formautofill/bootstrap.js index 3a21191a5b0b..22c3906b809b 100644 --- a/browser/extensions/formautofill/bootstrap.js +++ b/browser/extensions/formautofill/bootstrap.js @@ -16,7 +16,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate", "resource://gre/modules/AddonManager.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillParent", +XPCOMUtils.defineLazyModuleGetter(this, "formAutofillParent", "resource://formautofill/FormAutofillParent.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillUtils", "resource://formautofill/FormAutofillUtils.jsm"); @@ -110,8 +110,7 @@ function startup(data) { // Listen for the autocomplete popup message to lazily append our stylesheet related to the popup. Services.mm.addMessageListener("FormAutoComplete:MaybeOpenPopup", onMaybeOpenPopup); - let parent = new FormAutofillParent(); - parent.init().catch(Cu.reportError); + formAutofillParent.init().catch(Cu.reportError); Services.ppmm.loadProcessScript("data:,new " + function() { Components.utils.import("resource://formautofill/FormAutofillContent.jsm"); }, true); diff --git a/browser/extensions/formautofill/test/unit/test_activeStatus.js b/browser/extensions/formautofill/test/unit/test_activeStatus.js index 6c23177c037c..1bee59801df4 100644 --- a/browser/extensions/formautofill/test/unit/test_activeStatus.js +++ b/browser/extensions/formautofill/test/unit/test_activeStatus.js @@ -4,7 +4,7 @@ "use strict"; -Cu.import("resource://formautofill/FormAutofillParent.jsm"); +let {FormAutofillParent} = Cu.import("resource://formautofill/FormAutofillParent.jsm", {}); Cu.import("resource://formautofill/ProfileStorage.jsm"); add_task(async function test_activeStatus_init() { diff --git a/browser/extensions/formautofill/test/unit/test_getRecords.js b/browser/extensions/formautofill/test/unit/test_getRecords.js index 0a751734eec9..d7f9e32c8bcc 100644 --- a/browser/extensions/formautofill/test/unit/test_getRecords.js +++ b/browser/extensions/formautofill/test/unit/test_getRecords.js @@ -4,7 +4,7 @@ "use strict"; -Cu.import("resource://formautofill/FormAutofillParent.jsm"); +let {FormAutofillParent} = Cu.import("resource://formautofill/FormAutofillParent.jsm", {}); Cu.import("resource://formautofill/MasterPassword.jsm"); Cu.import("resource://formautofill/ProfileStorage.jsm"); diff --git a/browser/extensions/formautofill/test/unit/test_savedFieldNames.js b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js index b278b6b32474..6aaa3435490b 100644 --- a/browser/extensions/formautofill/test/unit/test_savedFieldNames.js +++ b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js @@ -4,7 +4,7 @@ "use strict"; -Cu.import("resource://formautofill/FormAutofillParent.jsm"); +let {FormAutofillParent} = Cu.import("resource://formautofill/FormAutofillParent.jsm", {}); Cu.import("resource://formautofill/ProfileStorage.jsm"); add_task(async function test_profileSavedFieldNames_init() { From 60d7ea01a2a58497e443ed70118e087de7ce8934 Mon Sep 17 00:00:00 2001 From: Ray Lin Date: Tue, 21 Nov 2017 12:26:10 +0800 Subject: [PATCH 018/219] Bug 1415073 - Refactor records structure of form autofill submission to adapt multiple sections. r=seanlee,steveck MozReview-Commit-ID: Fs2hgA7H5GX --HG-- extra : rebase_source : 976d4d1175982554d45de7b7364439bf922a963c --- .../formautofill/FormAutofillContent.jsm | 2 +- .../formautofill/FormAutofillHandler.jsm | 25 +++- .../formautofill/FormAutofillParent.jsm | 124 +++++++++++------- 3 files changed, 97 insertions(+), 54 deletions(-) diff --git a/browser/extensions/formautofill/FormAutofillContent.jsm b/browser/extensions/formautofill/FormAutofillContent.jsm index 627e48564b4c..fdab725fd3e0 100644 --- a/browser/extensions/formautofill/FormAutofillContent.jsm +++ b/browser/extensions/formautofill/FormAutofillContent.jsm @@ -416,7 +416,7 @@ var FormAutofillContent = { } let records = handler.createRecords(); - if (!Object.keys(records).length) { + if (!Object.values(records).some(typeRecords => typeRecords.length)) { return true; } diff --git a/browser/extensions/formautofill/FormAutofillHandler.jsm b/browser/extensions/formautofill/FormAutofillHandler.jsm index 6790e1790565..9787c693475f 100644 --- a/browser/extensions/formautofill/FormAutofillHandler.jsm +++ b/browser/extensions/formautofill/FormAutofillHandler.jsm @@ -986,12 +986,27 @@ class FormAutofillHandler { } } + /** + * Collect the filled sections within submitted form and convert all the valid + * field data into multiple records. + * + * @returns {Object} records + * {Array.} records.address + * {Array.} records.creditCard + */ createRecords() { - // TODO [Bug 1415073] `FormAutofillHandler.createRecords` should traverse - // all sections and aggregate the records into one result. - if (this.sections.length > 0) { - return this.sections[0].createRecords(); + const records = { + address: [], + creditCard: [], + }; + + for (const section of this.sections) { + const secRecords = section.createRecords(); + for (const [type, record] of Object.entries(secRecords)) { + records[type].push(record); + } } - return null; + log.debug("Create records:", records); + return records; } } diff --git a/browser/extensions/formautofill/FormAutofillParent.jsm b/browser/extensions/formautofill/FormAutofillParent.jsm index 5944312f5978..9843ed8a8974 100644 --- a/browser/extensions/formautofill/FormAutofillParent.jsm +++ b/browser/extensions/formautofill/FormAutofillParent.jsm @@ -383,6 +383,7 @@ FormAutofillParent.prototype = { }, _onAddressSubmit(address, target, timeStartedFillingMS) { + let showDoorhanger = null; if (address.guid) { // Avoid updating the fields that users don't modify. let originalAddress = this.profileStorage.addresses.get(address.guid); @@ -395,7 +396,8 @@ FormAutofillParent.prototype = { if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record, true)) { this._recordFormFillingTime("address", "autofill-update", timeStartedFillingMS); - FormAutofillDoorhanger.show(target, "updateAddress").then((state) => { + showDoorhanger = async () => { + const state = await FormAutofillDoorhanger.show(target, "updateAddress"); let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record, true); switch (state) { case "create": @@ -413,15 +415,15 @@ FormAutofillParent.prototype = { break; } changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid)); - }); + }; // Address should be updated Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill_update", 1); - return; + } else { + this._recordFormFillingTime("address", "autofill", timeStartedFillingMS); + this.profileStorage.addresses.notifyUsed(address.guid); + // Address is merged successfully + Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill", 1); } - this._recordFormFillingTime("address", "autofill", timeStartedFillingMS); - this.profileStorage.addresses.notifyUsed(address.guid); - // Address is merged successfully - Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill", 1); } else { let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record); if (!changedGUIDs.length) { @@ -433,22 +435,24 @@ FormAutofillParent.prototype = { // Show first time use doorhanger if (FormAutofillUtils.isAutofillAddressesFirstTimeUse) { Services.prefs.setBoolPref(FormAutofillUtils.ADDRESSES_FIRST_TIME_USE_PREF, false); - FormAutofillDoorhanger.show(target, "firstTimeUse").then((state) => { + showDoorhanger = async () => { + const state = await FormAutofillDoorhanger.show(target, "firstTimeUse"); if (state !== "open-pref") { return; } target.ownerGlobal.openPreferences("panePrivacy", {origin: "autofillDoorhanger"}); - }); + }; } else { // We want to exclude the first time form filling. Services.telemetry.scalarAdd("formautofill.addresses.fill_type_manual", 1); } } + return showDoorhanger; }, - async _onCreditCardSubmit(creditCard, target, timeStartedFillingMS) { + _onCreditCardSubmit(creditCard, target, timeStartedFillingMS) { // Updates the used status for shield/heartbeat to recognize users who have // used Credit Card Autofill. let setUsedStatus = status => { @@ -485,7 +489,7 @@ FormAutofillParent.prototype = { // Add probe to record credit card autofill(without modification). Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_autofill", 1); this._recordFormFillingTime("creditCard", "autofill", timeStartedFillingMS); - return; + return false; } // Add the probe to record credit card autofill with modification. Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_autofill_modified", 1); @@ -505,55 +509,79 @@ FormAutofillParent.prototype = { let dupGuid = this.profileStorage.creditCards.getDuplicateGuid(creditCard.record); if (dupGuid) { this.profileStorage.creditCards.notifyUsed(dupGuid); - return; + return false; } // Indicate that the user has seen the doorhanger. setUsedStatus(2); - let state = await FormAutofillDoorhanger.show(target, creditCard.guid ? "updateCreditCard" : "addCreditCard"); - if (state == "cancel") { - return; - } - - if (state == "disable") { - Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false); - return; - } - - // TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage - // APIs are refactored to be async functions (bug 1399367). - if (!await MasterPassword.ensureLoggedIn()) { - log.warn("User canceled master password entry"); - return; - } - - let changedGUIDs = []; - if (creditCard.guid) { - if (state == "update") { - this.profileStorage.creditCards.update(creditCard.guid, creditCard.record, true); - changedGUIDs.push(creditCard.guid); - } else if ("create") { - changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record)); + return async () => { + // Suppress the pending doorhanger from showing up if user disabled credit card in previous doorhanger. + if (!FormAutofillUtils.isAutofillCreditCardsEnabled) { + return; } - } else { - changedGUIDs.push(...this.profileStorage.creditCards.mergeToStorage(creditCard.record)); - if (!changedGUIDs.length) { - changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record)); + + const state = await FormAutofillDoorhanger.show(target, creditCard.guid ? "updateCreditCard" : "addCreditCard"); + if (state == "cancel") { + return; } - } - changedGUIDs.forEach(guid => this.profileStorage.creditCards.notifyUsed(guid)); + + if (state == "disable") { + Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false); + return; + } + + // TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage + // APIs are refactored to be async functions (bug 1399367). + if (!await MasterPassword.ensureLoggedIn()) { + log.warn("User canceled master password entry"); + return; + } + + let changedGUIDs = []; + if (creditCard.guid) { + if (state == "update") { + this.profileStorage.creditCards.update(creditCard.guid, creditCard.record, true); + changedGUIDs.push(creditCard.guid); + } else if ("create") { + changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record)); + } + } else { + changedGUIDs.push(...this.profileStorage.creditCards.mergeToStorage(creditCard.record)); + if (!changedGUIDs.length) { + changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record)); + } + } + changedGUIDs.forEach(guid => this.profileStorage.creditCards.notifyUsed(guid)); + }; }, - _onFormSubmit(data, target) { + async _onFormSubmit(data, target) { let {profile: {address, creditCard}, timeStartedFillingMS} = data; - if (address) { - this._onAddressSubmit(address, target, timeStartedFillingMS); - } - if (creditCard) { - this._onCreditCardSubmit(creditCard, target, timeStartedFillingMS); + // Don't record filling time if any type of records has more than one section being + // populated. We've been recording the filling time, so the other cases that aren't + // recorded on the same basis should be out of the data samples. E.g. Filling time of + // populating one profile is different from populating two sections, therefore, we + // shouldn't record the later to regress the representation of existing statistics. + if (address.length > 1 || creditCard.length > 1) { + timeStartedFillingMS = null; } + + // Transmit the telemetry immediately in the meantime form submitted, and handle these pending + // doorhangers at a later. + await Promise.all([ + address.map(addrRecord => this._onAddressSubmit(addrRecord, target, timeStartedFillingMS)), + creditCard.map(ccRecord => this._onCreditCardSubmit(ccRecord, target, timeStartedFillingMS)), + ].map(pendingDoorhangers => { + return pendingDoorhangers.filter(pendingDoorhanger => !!pendingDoorhanger && + typeof pendingDoorhanger == "function"); + }).map(pendingDoorhangers => new Promise(async resolve => { + for (const showDoorhanger of pendingDoorhangers) { + await showDoorhanger(); + } + resolve(); + }))); }, /** * Set the probes for the filling time with specific filling type and form type. From 606f156e4aa370ac83a705ea543297df081a3b64 Mon Sep 17 00:00:00 2001 From: Ray Lin Date: Wed, 29 Nov 2017 15:27:59 +0800 Subject: [PATCH 019/219] Bug 1415073 - Update the unit tests accordingly with new submission records. r=seanlee,steveck MozReview-Commit-ID: 8Rcpx7y2rXT --HG-- extra : rebase_source : 1b6ca120d2a75d1a330602bda45ae9e84080e8e6 --- .../test/unit/test_createRecords.js | 142 ++++++++++++++---- .../test/unit/test_onFormSubmitted.js | 109 ++++++++------ 2 files changed, 173 insertions(+), 78 deletions(-) diff --git a/browser/extensions/formautofill/test/unit/test_createRecords.js b/browser/extensions/formautofill/test/unit/test_createRecords.js index 7cf24f2f6a6b..260e579a60bb 100644 --- a/browser/extensions/formautofill/test/unit/test_createRecords.js +++ b/browser/extensions/formautofill/test/unit/test_createRecords.js @@ -26,16 +26,16 @@ const TESTCASES = [ "cc-name": "*".repeat(201), }, expectedRecord: { - address: { + address: [{ "given-name": "John", "organization": "*".repeat(200), "address-level1": "", "country": "US", - }, - creditCard: { + }], + creditCard: [{ "cc-number": "1111222233334444", "cc-name": "", - }, + }], }, }, { @@ -50,7 +50,8 @@ const TESTCASES = [ "organization": "Mozilla", }, expectedRecord: { - address: undefined, + address: [], + creditCard: [], }, }, { @@ -66,11 +67,12 @@ const TESTCASES = [ "country": "United States", }, expectedRecord: { - address: { + address: [{ "given-name": "John", "organization": "Mozilla", "country": "United States", - }, + }], + creditCard: [], }, }, { @@ -86,11 +88,12 @@ const TESTCASES = [ "country": "United States", }, expectedRecord: { - address: { + address: [{ "given-name": "John", "organization": "Mozilla", "country": "US", - }, + }], + creditCard: [], }, }, { @@ -108,11 +111,12 @@ const TESTCASES = [ "tel-national": "1234567890", }, expectedRecord: { - address: { + address: [{ "given-name": "John", "organization": "Mozilla", "tel": "+11234567890", - }, + }], + creditCard: [], }, }, { @@ -130,12 +134,13 @@ const TESTCASES = [ "tel": "1234", }, expectedRecord: { - address: { + address: [{ "given-name": "John", "organization": "Mozilla", "country": "United States", "tel": "", - }, + }], + creditCard: [], }, }, { @@ -153,12 +158,13 @@ const TESTCASES = [ "tel": "1234567890123456", }, expectedRecord: { - address: { + address: [{ "given-name": "John", "organization": "Mozilla", "country": "United States", "tel": "", - }, + }], + creditCard: [], }, }, { @@ -176,12 +182,13 @@ const TESTCASES = [ "tel": "12345###!!!", }, expectedRecord: { - address: { + address: [{ "given-name": "John", "organization": "Mozilla", "country": "United States", "tel": "", - }, + }], + creditCard: [], }, }, { @@ -197,7 +204,8 @@ const TESTCASES = [ "organization": "Mozilla", }, expectedRecord: { - address: undefined, + address: [], + creditCard: [], }, }, { @@ -215,7 +223,8 @@ const TESTCASES = [ "organization": "Mozilla", }, expectedRecord: { - address: undefined, + address: [], + creditCard: [], }, }, { @@ -231,11 +240,12 @@ const TESTCASES = [ "cc-exp": "2022-06", }, expectedRecord: { - creditCard: { + address: [], + creditCard: [{ "cc-number": "4444000022220000", "cc-name": "Foo Bar", "cc-exp": "2022-06", - }, + }], }, }, { @@ -247,9 +257,10 @@ const TESTCASES = [ "cc-number": "4444000022220000", }, expectedRecord: { - creditCard: { + address: [], + creditCard: [{ "cc-number": "4444000022220000", - }, + }], }, }, { @@ -265,7 +276,8 @@ const TESTCASES = [ "cc-exp": "2022-06", }, expectedRecord: { - creditCard: undefined, + address: [], + creditCard: [], }, }, { @@ -279,9 +291,80 @@ const TESTCASES = [ "cc-exp": "2022-06", }, expectedRecord: { - creditCard: undefined, + address: [], + creditCard: [], }, }, + { + description: "A form with multiple sections", + document: `
+
`, + formValue: { + "given-name": "Bar", + "organization": "Foo", + "country": "US", + + "given-name-shipping": "John", + "family-name-shipping": "Doe", + "organization-shipping": "Mozilla", + "country-shipping": "US", + + "given-name-billing": "Foo", + "organization-billing": "Bar", + "country-billing": "US", + + "cc-number-section-one": "4444000022220000", + "cc-name-section-one": "John", + + "cc-number-section-two": "4444000022221111", + "cc-name-section-two": "Foo Bar", + "cc-exp-section-two": "2026-26", + }, + expectedRecord: { + address: [{ + "given-name": "Bar", + "organization": "Foo", + "country": "US", + }, { + "given-name": "John", + "family-name": "Doe", + "organization": "Mozilla", + "country": "US", + }, { + "given-name": "Foo", + "organization": "Bar", + "country": "US", + }], + creditCard: [{ + "cc-number": "4444000022220000", + "cc-name": "John", + }, { + "cc-number": "4444000022221111", + "cc-name": "Foo Bar", + "cc-exp": "2026-26", + }], + }, + }, + ]; for (let testcase of TESTCASES) { @@ -301,12 +384,9 @@ for (let testcase of TESTCASES) { let record = handler.createRecords(); - for (let type in testcase.expectedRecord) { - if (!testcase.expectedRecord[type]) { - do_check_eq(record[type], undefined); - } else { - Assert.deepEqual(record[type].record, testcase.expectedRecord[type]); - } + let expectedRecord = testcase.expectedRecord; + for (let type in record) { + Assert.deepEqual(record[type].map(secRecord => secRecord.record), expectedRecord[type]); } }); } diff --git a/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js b/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js index b1ebae9740fc..15d4608b25e6 100644 --- a/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js +++ b/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js @@ -62,7 +62,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "street-address": "331 E. Evelyn Avenue", @@ -73,7 +73,8 @@ const TESTCASES = [ "tel": "1-650-903-0800", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -88,7 +89,8 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - creditCard: { + address: [], + creditCard: [{ guid: null, record: { "cc-name": "John Doe", @@ -97,7 +99,7 @@ const TESTCASES = [ "cc-exp-year": 2000, }, untouchedFields: [], - }, + }], }, }, }, @@ -115,7 +117,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "street-address": "331 E. Evelyn Avenue", @@ -126,8 +128,8 @@ const TESTCASES = [ "tel": "1-650-903-0800", }, untouchedFields: [], - }, - creditCard: { + }], + creditCard: [{ guid: null, record: { "cc-name": "John Doe", @@ -136,7 +138,7 @@ const TESTCASES = [ "cc-exp-year": 2000, }, untouchedFields: [], - }, + }], }, }, }, @@ -150,7 +152,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "street-address": "331 E. Evelyn Avenue", @@ -161,7 +163,8 @@ const TESTCASES = [ "tel": "1-650-903-0800", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -176,7 +179,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "street-address": "331 E. Evelyn Avenue", @@ -187,7 +190,8 @@ const TESTCASES = [ "tel": "1-650-903-0800", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -201,7 +205,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "address-level1": "CA", @@ -212,7 +216,8 @@ const TESTCASES = [ "tel": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -226,7 +231,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "address-level1": "CA", @@ -237,7 +242,8 @@ const TESTCASES = [ "tel": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -251,7 +257,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "address-level1": "AR", @@ -262,7 +268,8 @@ const TESTCASES = [ "tel": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -276,7 +283,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "address-level1": "CA", @@ -287,7 +294,8 @@ const TESTCASES = [ "tel": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -301,7 +309,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "address-level1": "AZ", @@ -312,7 +320,8 @@ const TESTCASES = [ "tel": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -326,7 +335,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "address-level1": "Arizonac", @@ -337,7 +346,8 @@ const TESTCASES = [ "tel": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -352,7 +362,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "street-address": "331 E. Evelyn Avenue", @@ -363,7 +373,8 @@ const TESTCASES = [ "email": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -378,7 +389,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "street-address": "331 E. Evelyn Avenue", @@ -389,7 +400,8 @@ const TESTCASES = [ "email": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -404,7 +416,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "street-address": "331 E. Evelyn Avenue", @@ -415,7 +427,8 @@ const TESTCASES = [ "email": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -430,7 +443,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "street-address": "331 E. Evelyn Avenue", @@ -441,7 +454,8 @@ const TESTCASES = [ "email": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -456,7 +470,7 @@ const TESTCASES = [ expectedResult: { formSubmission: true, records: { - address: { + address: [{ guid: null, record: { "street-address": "331 E. Evelyn Avenue", @@ -467,7 +481,8 @@ const TESTCASES = [ "email": "", }, untouchedFields: [], - }, + }], + creditCard: [], }, }, }, @@ -478,8 +493,8 @@ add_task(async function handle_earlyformsubmit_event() { let fakeForm = MOCK_DOC.createElement("form"); sinon.spy(FormAutofillContent, "_onFormSubmit"); - do_check_eq(FormAutofillContent.notify(fakeForm), true); - do_check_eq(FormAutofillContent._onFormSubmit.called, false); + Assert.equal(FormAutofillContent.notify(fakeForm), true); + Assert.equal(FormAutofillContent._onFormSubmit.called, false); FormAutofillContent._onFormSubmit.restore(); }); @@ -508,33 +523,33 @@ add_task(async function autofill_disabled() { Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false); Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false); FormAutofillContent.notify(form); - do_check_eq(FormAutofillContent._onFormSubmit.called, false); + Assert.equal(FormAutofillContent._onFormSubmit.called, false); FormAutofillContent._onFormSubmit.reset(); // "_onFormSubmit" should be called as usual. Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled"); Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled"); FormAutofillContent.notify(form); - do_check_eq(FormAutofillContent._onFormSubmit.called, true); - do_check_neq(FormAutofillContent._onFormSubmit.args[0][0].address, undefined); - do_check_neq(FormAutofillContent._onFormSubmit.args[0][0].creditCard, undefined); + Assert.equal(FormAutofillContent._onFormSubmit.called, true); + Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []); + Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []); FormAutofillContent._onFormSubmit.reset(); // "address" should be empty if "addresses" pref is disabled. Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false); FormAutofillContent.notify(form); - do_check_eq(FormAutofillContent._onFormSubmit.called, true); - do_check_eq(FormAutofillContent._onFormSubmit.args[0][0].address, undefined); - do_check_neq(FormAutofillContent._onFormSubmit.args[0][0].creditCard, undefined); + Assert.equal(FormAutofillContent._onFormSubmit.called, true); + Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []); + Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []); FormAutofillContent._onFormSubmit.reset(); Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled"); // "creditCard" should be empty if "creditCards" pref is disabled. Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false); FormAutofillContent.notify(form); - do_check_eq(FormAutofillContent._onFormSubmit.called, true); - do_check_neq(FormAutofillContent._onFormSubmit.args[0][0].address, undefined); - do_check_eq(FormAutofillContent._onFormSubmit.args[0][0].creditCard, undefined); + Assert.deepEqual(FormAutofillContent._onFormSubmit.called, true); + Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []); + Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []); FormAutofillContent._onFormSubmit.reset(); Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled"); @@ -566,7 +581,7 @@ TESTCASES.forEach(testcase => { FormAutofillContent.identifyAutofillFields(element); FormAutofillContent.notify(form); - do_check_eq(FormAutofillContent._onFormSubmit.called, + Assert.equal(FormAutofillContent._onFormSubmit.called, testcase.expectedResult.formSubmission); if (FormAutofillContent._onFormSubmit.called) { Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0], From 8663ce0fcb2eeed4255df4115d268faadbb0e74a Mon Sep 17 00:00:00 2001 From: Evan Tseng Date: Thu, 30 Nov 2017 17:42:40 +0800 Subject: [PATCH 020/219] Bug 1407568 - Add a spotlight indicator to a specific section and UI component. r=jaws,lchang MozReview-Commit-ID: 4AgAFq2r418 --HG-- extra : rebase_source : 055fb2881c1a91aff108fb4a32a7fff842443bc7 --- .../preferences/in-content/preferences.js | 117 ++++++++++++++++-- .../preferences/in-content/privacy.xul | 34 ++--- .../preferences/in-content/tests/browser.ini | 1 + ...g1020245_openPreferences_to_paneContent.js | 6 +- .../in-content/tests/browser_spotlight.js | 37 ++++++ .../uitour/test/browser_openPreferences.js | 5 +- .../formautofill/FormAutofillPreferences.jsm | 2 + .../shared/incontentprefs/preferences.inc.css | 25 +++- 8 files changed, 195 insertions(+), 32 deletions(-) create mode 100644 browser/components/preferences/in-content/tests/browser_spotlight.js diff --git a/browser/components/preferences/in-content/preferences.js b/browser/components/preferences/in-content/preferences.js index 57b1f8ad8862..5e1277951bc7 100644 --- a/browser/components/preferences/in-content/preferences.js +++ b/browser/components/preferences/in-content/preferences.js @@ -27,6 +27,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore", "resource://gre/modules/ExtensionSettingsStore.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "formAutofillParent", + "resource://formautofill/FormAutofillParent.jsm"); var gLastHash = ""; @@ -174,17 +176,19 @@ function gotoPref(aCategory) { categories.clearSelection(); } window.history.replaceState(category, document.title); - search(category, "data-category", subcategory, "data-subcategory"); + search(category, "data-category"); let mainContent = document.querySelector(".main-content"); mainContent.scrollTop = 0; + spotlight(subcategory); + Services.telemetry .getHistogramById("FX_PREFERENCES_CATEGORY_OPENED_V2") .add(telemetryBucketForCategory(friendlyName)); } -function search(aQuery, aAttribute, aSubquery, aSubAttribute) { +function search(aQuery, aAttribute) { let mainPrefPane = document.getElementById("mainPrefPane"); let elements = mainPrefPane.children; for (let element of elements) { @@ -196,14 +200,7 @@ function search(aQuery, aAttribute, aSubquery, aSubAttribute) { element.getAttribute("data-subpanel") == "true") { let attributeValue = element.getAttribute(aAttribute); if (attributeValue == aQuery) { - if (!element.classList.contains("header") && - element.localName !== "preferences" && - aSubquery && aSubAttribute) { - let subAttributeValue = element.getAttribute(aSubAttribute); - element.hidden = subAttributeValue != aSubquery; - } else { - element.hidden = false; - } + element.hidden = false; } else { element.hidden = true; } @@ -221,6 +218,106 @@ function search(aQuery, aAttribute, aSubquery, aSubAttribute) { } } +async function spotlight(subcategory) { + let highlightedElements = document.querySelectorAll(".spotlight"); + if (highlightedElements.length) { + for (let element of highlightedElements) { + element.classList.remove("spotlight"); + } + } + if (subcategory) { + if (!gSearchResultsPane.categoriesInitialized) { + await waitForSystemAddonInjectionsFinished([{ + isGoingToInject: formAutofillParent.initialized, + elementId: "formAutofillGroup", + }]); + } + scrollAndHighlight(subcategory); + } + + /** + * Wait for system addons finished their dom injections. + * @param {Array} addons - The system addon information array. + * For example, the element is looked like + * { isGoingToInject: true, elementId: "formAutofillGroup" }. + * The `isGoingToInject` means the system addon will be visible or not, + * and the `elementId` means the id of the element will be injected into the dom + * if the `isGoingToInject` is true. + * @returns {Promise} Will resolve once all injections are finished. + */ + function waitForSystemAddonInjectionsFinished(addons) { + return new Promise(resolve => { + let elementIdSet = new Set(); + for (let addon of addons) { + if (addon.isGoingToInject) { + elementIdSet.add(addon.elementId); + } + } + if (elementIdSet.size) { + let observer = new MutationObserver(mutations => { + for (let mutation of mutations) { + for (let node of mutation.addedNodes) { + elementIdSet.delete(node.id); + if (elementIdSet.size === 0) { + observer.disconnect(); + resolve(); + } + } + } + }); + let mainContent = document.querySelector(".main-content"); + observer.observe(mainContent, {childList: true, subtree: true}); + // Disconnect the mutation observer once there is any user input. + mainContent.addEventListener("scroll", disconnectMutationObserver); + window.addEventListener("mousedown", disconnectMutationObserver); + window.addEventListener("keydown", disconnectMutationObserver); + function disconnectMutationObserver() { + mainContent.removeEventListener("scroll", disconnectMutationObserver); + window.removeEventListener("mousedown", disconnectMutationObserver); + window.removeEventListener("keydown", disconnectMutationObserver); + observer.disconnect(); + } + } else { + resolve(); + } + }); + } +} + +function scrollAndHighlight(subcategory) { + let element = document.querySelector(`[data-subcategory="${subcategory}"]`); + if (element) { + let header = getClosestDisplayedHeader(element); + scrollContentTo(header); + element.classList.add("spotlight"); + } +} + +/** + * If there is no visible second level header it will return first level header, + * otherwise return second level header. + * @returns {Element} - The closest displayed header. + */ +function getClosestDisplayedHeader(element) { + let header = element.closest("groupbox"); + let searchHeader = header.querySelector("caption.search-header"); + if (searchHeader && searchHeader.hidden && + header.previousSibling.classList.contains("subcategory")) { + header = header.previousSibling; + } + return header; +} + +function scrollContentTo(element) { + const SEARCH_CONTAINER_HEIGHT = document.querySelector(".search-container").clientHeight; + let mainContent = document.querySelector(".main-content"); + let top = element.getBoundingClientRect().top - SEARCH_CONTAINER_HEIGHT; + mainContent.scroll({ + top, + behavior: "smooth", + }); +} + function helpButtonCommand() { let pane = history.state; let categories = document.getElementById("categories"); diff --git a/browser/components/preferences/in-content/privacy.xul b/browser/components/preferences/in-content/privacy.xul index ff9bff668870..0392bf03398f 100644 --- a/browser/components/preferences/in-content/privacy.xul +++ b/browser/components/preferences/in-content/privacy.xul @@ -707,20 +707,19 @@ #ifdef MOZ_DATA_REPORTING -
- - Date: Fri, 1 Dec 2017 10:13:04 +0100 Subject: [PATCH 024/219] Bug 1415906 - Update references to Macedonian in PluralForm.jsm r=Pike Also marked rule #14 as unused. MozReview-Commit-ID: GbfHY5NKgms --HG-- extra : rebase_source : a2bb662155302705e7702e4ef3f6108adf244a2f --- intl/locale/PluralForm.jsm | 4 ++-- intl/locale/tests/unit/test_pluralForm.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/intl/locale/PluralForm.jsm b/intl/locale/PluralForm.jsm index dddaba2789d3..d9e5a0686b3d 100644 --- a/intl/locale/PluralForm.jsm +++ b/intl/locale/PluralForm.jsm @@ -68,9 +68,9 @@ var gFunctions = [ [6, (n) => n==0?5:n==1?0:n==2?1:n%100>=3&&n%100<=10?2:n%100>=11&&n%100<=99?3:4], // 13: Maltese [4, (n) => n==1?0:n==0||n%100>0&&n%100<=10?1:n%100>10&&n%100<20?2:3], - // 14: Macedonian + // 14: Unused [3, (n) => n%10==1?0:n%10==2?1:2], - // 15: Icelandic + // 15: Icelandic, Macedonian [2, (n) => n%10==1&&n%100!=11?0:1], // 16: Breton [5, (n) => n%10==1&&n%100!=11&&n%100!=71&&n%100!=91?0:n%10==2&&n%100!=12&&n%100!=72&&n%100!=92?1:(n%10==3||n%10==4||n%10==9)&&n%100!=13&&n%100!=14&&n%100!=19&&n%100!=73&&n%100!=74&&n%100!=79&&n%100!=93&&n%100!=94&&n%100!=99?2:n%1000000==0&&n!=0?3:4], diff --git a/intl/locale/tests/unit/test_pluralForm.js b/intl/locale/tests/unit/test_pluralForm.js index b270655d463c..0e501dda5513 100644 --- a/intl/locale/tests/unit/test_pluralForm.js +++ b/intl/locale/tests/unit/test_pluralForm.js @@ -488,7 +488,7 @@ function run_test() 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4, ], [ - // 14: Macedonian 0-9, 10-19, ..., 90-99 + // 14: Unused 0-9, 10-19, ..., 90-99 3,1,2,3,3,3,3,3,3,3, 3,1,2,3,3,3,3,3,3,3, 3,1,2,3,3,3,3,3,3,3, @@ -522,7 +522,7 @@ function run_test() 3,1,2,3,3,3,3,3,3,3, 3,1,2,3,3,3,3,3,3,3, ], [ - // 15: Icelandic 0-9, 10-19, ..., 90-99 + // 15: Icelandic, Macedonian 0-9, 10-19, ..., 90-99 2,1,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2, 2,1,2,2,2,2,2,2,2,2, From d093ecbe77848fdfaffdb65c268fd773f53b6efc Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Fri, 1 Dec 2017 13:20:29 +0100 Subject: [PATCH 025/219] Bug 1422277 - Add an observer notification for when the captive portal detection is "done" r=bagder MozReview-Commit-ID: D9Qr0CuUJiQ --HG-- extra : rebase_source : 74ecc449bee263d27af83fa101e6ef59166fecab --- netwerk/base/CaptivePortalService.cpp | 13 +++++++++++++ netwerk/base/CaptivePortalService.h | 1 + netwerk/base/nsICaptivePortalService.idl | 9 +++++++++ 3 files changed, 23 insertions(+) diff --git a/netwerk/base/CaptivePortalService.cpp b/netwerk/base/CaptivePortalService.cpp index 94cfc52d8e77..37ac9b27a627 100644 --- a/netwerk/base/CaptivePortalService.cpp +++ b/netwerk/base/CaptivePortalService.cpp @@ -335,6 +335,17 @@ CaptivePortalService::Observe(nsISupports *aSubject, return NS_OK; } +void +CaptivePortalService::NotifyConnectivityAvailable(bool aCaptive) +{ + nsCOMPtr observerService = services::GetObserverService(); + if (observerService) { + nsCOMPtr cps(this); + observerService->NotifyObservers(cps, NS_CAPTIVE_PORTAL_CONNECTIVITY, + aCaptive ? u"captive" : u"clear"); + } +} + //----------------------------------------------------------------------------- // CaptivePortalService::nsICaptivePortalCallback //----------------------------------------------------------------------------- @@ -364,8 +375,10 @@ CaptivePortalService::Complete(bool success) if (success) { if (mEverBeenCaptive) { mState = UNLOCKED_PORTAL; + NotifyConnectivityAvailable(true); } else { mState = NOT_CAPTIVE; + NotifyConnectivityAvailable(false); } } diff --git a/netwerk/base/CaptivePortalService.h b/netwerk/base/CaptivePortalService.h index af04097e002b..255f13354571 100644 --- a/netwerk/base/CaptivePortalService.h +++ b/netwerk/base/CaptivePortalService.h @@ -45,6 +45,7 @@ private: virtual ~CaptivePortalService(); nsresult PerformCheck(); nsresult RearmTimer(); + void NotifyConnectivityAvailable(bool aCaptive); nsCOMPtr mCaptivePortalDetector; int32_t mState; diff --git a/netwerk/base/nsICaptivePortalService.idl b/netwerk/base/nsICaptivePortalService.idl index 94d9d6e9a8cb..9b6d104fa664 100644 --- a/netwerk/base/nsICaptivePortalService.idl +++ b/netwerk/base/nsICaptivePortalService.idl @@ -56,4 +56,13 @@ interface nsICaptivePortalService : nsISupports */ #define NS_IPC_CAPTIVE_PORTAL_SET_STATE "ipc:network:captive-portal-set-state" +/** + * This notification will be emitted when the captive portal service has + * determined that we can connect to the internet. + * The service will pass either "captive" if there is an unlocked captive portal + * present, or "clear" if no captive portal was detected. + * Note: this notification only gets sent in the parent process. + */ +#define NS_CAPTIVE_PORTAL_CONNECTIVITY "network:captive-portal-connectivity" + %} From 0ec0112df165e252ab44fdf923006ddb677b5ca0 Mon Sep 17 00:00:00 2001 From: "Francesco Lodolo (:flod)" Date: Fri, 1 Dec 2017 09:34:17 +0100 Subject: [PATCH 026/219] Bug 1422249 - Update locales in mobile l10n.toml r=Pike Vietnamese (vi) is missing from the list of languages. List of locales on single-locale builds is out of date. MozReview-Commit-ID: 3zZ3MYAuBWB --HG-- extra : rebase_source : f98d9af6d8c6db1b8195eab211576b303a18dc23 --- mobile/android/locales/l10n.toml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mobile/android/locales/l10n.toml b/mobile/android/locales/l10n.toml index 08a00f4651d9..41b8630040d3 100644 --- a/mobile/android/locales/l10n.toml +++ b/mobile/android/locales/l10n.toml @@ -96,21 +96,22 @@ locales = [ "uk", "ur", "uz", + "vi", "wo", "xh", "zam", "zh-CN", "zh-TW", - ] +] [build] exclude-multi-locale = [ - "be", - "bn-BD", - "ne-NP", + "ach", + "bs", + "ia", + "ltg", + "oc", "trs", - "wo", - "zam", ] [env] From 73e80931eafd3b6a2bddec21295659277d462628 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Fri, 1 Dec 2017 10:33:44 +0800 Subject: [PATCH 027/219] Bug 1421179. P1 - associate data with playback events published by MDSM. r=bechen,gerald This is required for we want to associate playback offset with the PlaybackStarted/PlaybackStopped events. MozReview-Commit-ID: JkRhC2QE7kr --HG-- extra : rebase_source : c5f637f2e5849565acb5b91364537f1724ce7d5f --- dom/media/ChannelMediaDecoder.cpp | 10 +++--- dom/media/ChannelMediaDecoder.h | 2 +- dom/media/MediaDecoder.cpp | 24 +++++++------- dom/media/MediaDecoder.h | 4 +-- dom/media/MediaDecoderStateMachine.cpp | 27 +++++++-------- dom/media/MediaDecoderStateMachine.h | 46 +++++++++++++++++--------- 6 files changed, 64 insertions(+), 49 deletions(-) diff --git a/dom/media/ChannelMediaDecoder.cpp b/dom/media/ChannelMediaDecoder.cpp index 31ac09fbc33c..36015fde81ad 100644 --- a/dom/media/ChannelMediaDecoder.cpp +++ b/dom/media/ChannelMediaDecoder.cpp @@ -351,21 +351,21 @@ ChannelMediaDecoder::IsLiveStream() } void -ChannelMediaDecoder::OnPlaybackEvent(MediaEventType aEvent) +ChannelMediaDecoder::OnPlaybackEvent(MediaPlaybackEvent&& aEvent) { MOZ_ASSERT(NS_IsMainThread()); - MediaDecoder::OnPlaybackEvent(aEvent); - switch (aEvent) { - case MediaEventType::PlaybackStarted: + switch (aEvent.mType) { + case MediaPlaybackEvent::PlaybackStarted: mPlaybackStatistics.Start(); break; - case MediaEventType::PlaybackStopped: + case MediaPlaybackEvent::PlaybackStopped: mPlaybackStatistics.Stop(); ComputePlaybackRate(); break; default: break; } + MediaDecoder::OnPlaybackEvent(Move(aEvent)); } void diff --git a/dom/media/ChannelMediaDecoder.h b/dom/media/ChannelMediaDecoder.h index 2457de6302cf..8bca8284ca0c 100644 --- a/dom/media/ChannelMediaDecoder.h +++ b/dom/media/ChannelMediaDecoder.h @@ -56,7 +56,7 @@ class ChannelMediaDecoder : public MediaDecoder }; protected: - void OnPlaybackEvent(MediaEventType aEvent) override; + void OnPlaybackEvent(MediaPlaybackEvent&& aEvent) override; void DurationChanged() override; void MetadataLoaded(UniquePtr aInfo, UniquePtr aTags, diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index df63b5b0e739..67b07cfa6d62 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -528,38 +528,38 @@ MediaDecoder::~MediaDecoder() } void -MediaDecoder::OnPlaybackEvent(MediaEventType aEvent) +MediaDecoder::OnPlaybackEvent(MediaPlaybackEvent&& aEvent) { - switch (aEvent) { - case MediaEventType::PlaybackEnded: + switch (aEvent.mType) { + case MediaPlaybackEvent::PlaybackEnded: PlaybackEnded(); break; - case MediaEventType::SeekStarted: + case MediaPlaybackEvent::SeekStarted: SeekingStarted(); break; - case MediaEventType::Loop: + case MediaPlaybackEvent::Loop: GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("seeking")); GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("seeked")); break; - case MediaEventType::Invalidate: + case MediaPlaybackEvent::Invalidate: Invalidate(); break; - case MediaEventType::EnterVideoSuspend: + case MediaPlaybackEvent::EnterVideoSuspend: GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozentervideosuspend")); break; - case MediaEventType::ExitVideoSuspend: + case MediaPlaybackEvent::ExitVideoSuspend: GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozexitvideosuspend")); break; - case MediaEventType::StartVideoSuspendTimer: + case MediaPlaybackEvent::StartVideoSuspendTimer: GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozstartvideosuspendtimer")); break; - case MediaEventType::CancelVideoSuspendTimer: + case MediaPlaybackEvent::CancelVideoSuspendTimer: GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozcancelvideosuspendtimer")); break; - case MediaEventType::VideoOnlySeekBegin: + case MediaPlaybackEvent::VideoOnlySeekBegin: GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozvideoonlyseekbegin")); break; - case MediaEventType::VideoOnlySeekCompleted: + case MediaPlaybackEvent::VideoOnlySeekCompleted: GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozvideoonlyseekcompleted")); break; default: diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index beb7a5c92a62..857c99e1d3c3 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -39,8 +39,8 @@ class FrameStatistics; class VideoFrameContainer; class MediaFormatReader; class MediaDecoderStateMachine; +struct MediaPlaybackEvent; -enum class MediaEventType : int8_t; enum class Visibility : uint8_t; // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to @@ -424,7 +424,7 @@ protected: DurationChanged(); } - virtual void OnPlaybackEvent(MediaEventType aEvent); + virtual void OnPlaybackEvent(MediaPlaybackEvent&& aEvent); // Called when the metadata from the media file has been loaded by the // state machine. Call on the main thread only. diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index a02d7896e071..ad39c2058736 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -679,7 +679,7 @@ public: } mMaster->mVideoDecodeSuspended = true; - mMaster->mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend); + mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::EnterVideoSuspend); Reader()->SetVideoBlankDecode(true); } @@ -832,7 +832,7 @@ public: // when seek is done. if (mMaster->mVideoDecodeSuspended) { mMaster->mVideoDecodeSuspended = false; - mMaster->mOnPlaybackEvent.Notify(MediaEventType::ExitVideoSuspend); + mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::ExitVideoSuspend); Reader()->SetVideoBlankDecode(false); } @@ -846,7 +846,7 @@ public: // playback should has been stopped. mMaster->StopPlayback(); mMaster->UpdatePlaybackPositionInternal(mSeekJob.mTarget->GetTime()); - mMaster->mOnPlaybackEvent.Notify(MediaEventType::SeekStarted); + mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::SeekStarted); mMaster->mOnNextFrameStatus.Notify( MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING); } @@ -1642,7 +1642,7 @@ public: // Dispatch a mozvideoonlyseekbegin event to indicate UI for corresponding // changes. - mMaster->mOnPlaybackEvent.Notify(MediaEventType::VideoOnlySeekBegin); + mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::VideoOnlySeekBegin); return p.forget(); } @@ -1652,7 +1652,8 @@ public: // We are completing or discarding this video-only seek operation now, // dispatch an event so that the UI can change in response to the end // of video-only seek. - mMaster->mOnPlaybackEvent.Notify(MediaEventType::VideoOnlySeekCompleted); + mMaster->mOnPlaybackEvent.Notify( + MediaPlaybackEvent::VideoOnlySeekCompleted); AccurateSeekingState::Exit(); } @@ -1858,7 +1859,7 @@ public: } mMaster->mVideoDecodeSuspended = true; - mMaster->mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend); + mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::EnterVideoSuspend); Reader()->SetVideoBlankDecode(true); } @@ -1947,7 +1948,7 @@ public: mMaster->mOnNextFrameStatus.Notify( MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE); - mMaster->mOnPlaybackEvent.Notify(MediaEventType::PlaybackEnded); + mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::PlaybackEnded); mSentPlaybackEndedEvent = true; @@ -2329,7 +2330,7 @@ DecodingState::Step() // when the media is looped back from the end to the beginning. if (before > mMaster->GetMediaTime()) { MOZ_ASSERT(mMaster->mLooping); - mMaster->mOnPlaybackEvent.Notify(MediaEventType::Loop); + mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::Loop); // After looping is cancelled, the time won't be corrected, and therefore we // can check it to see if the end of the media track is reached. Make sure // the media is started before comparing the time, or it's meaningless. @@ -2509,7 +2510,7 @@ SeekingState::SeekCompleted() if (mMaster->VideoQueue().PeekFront()) { mMaster->mMediaSink->Redraw(Info().mVideo); - mMaster->mOnPlaybackEvent.Notify(MediaEventType::Invalidate); + mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::Invalidate); } GoToNextState(); @@ -2905,7 +2906,7 @@ MediaDecoderStateMachine::StopPlayback() MOZ_ASSERT(OnTaskQueue()); LOG("StopPlayback()"); - mOnPlaybackEvent.Notify(MediaEventType::PlaybackStopped); + mOnPlaybackEvent.Notify(MediaPlaybackEvent::PlaybackStopped); if (IsPlaying()) { mMediaSink->SetPlaying(false); @@ -2936,7 +2937,7 @@ void MediaDecoderStateMachine::MaybeStartPlayback() } LOG("MaybeStartPlayback() starting playback"); - mOnPlaybackEvent.Notify(MediaEventType::PlaybackStarted); + mOnPlaybackEvent.Notify(MediaPlaybackEvent::PlaybackStarted); StartMediaSink(); #ifdef XP_WIN @@ -3082,7 +3083,7 @@ void MediaDecoderStateMachine::SetVideoDecodeModeInternal(VideoDecodeMode aMode) mVideoDecodeSuspendTimer.Ensure(target, [=]() { self->OnSuspendTimerResolved(); }, [] () { MOZ_DIAGNOSTIC_ASSERT(false); }); - mOnPlaybackEvent.Notify(MediaEventType::StartVideoSuspendTimer); + mOnPlaybackEvent.Notify(MediaPlaybackEvent::StartVideoSuspendTimer); return; } @@ -3953,7 +3954,7 @@ MediaDecoderStateMachine::CancelSuspendTimer() mVideoDecodeSuspendTimer.IsScheduled() ? 'T' : 'F'); MOZ_ASSERT(OnTaskQueue()); if (mVideoDecodeSuspendTimer.IsScheduled()) { - mOnPlaybackEvent.Notify(MediaEventType::CancelVideoSuspendTimer); + mOnPlaybackEvent.Notify(MediaPlaybackEvent::CancelVideoSuspendTimer); } mVideoDecodeSuspendTimer.Reset(); } diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 2877c2df5e88..f05a9ba5d338 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -114,20 +114,32 @@ class TaskQueue; extern LazyLogModule gMediaDecoderLog; -enum class MediaEventType : int8_t +struct MediaPlaybackEvent { - PlaybackStarted, - PlaybackStopped, - PlaybackEnded, - SeekStarted, - Loop, - Invalidate, - EnterVideoSuspend, - ExitVideoSuspend, - StartVideoSuspendTimer, - CancelVideoSuspendTimer, - VideoOnlySeekBegin, - VideoOnlySeekCompleted, + enum EventType + { + PlaybackStarted, + PlaybackStopped, + PlaybackEnded, + SeekStarted, + Loop, + Invalidate, + EnterVideoSuspend, + ExitVideoSuspend, + StartVideoSuspendTimer, + CancelVideoSuspendTimer, + VideoOnlySeekBegin, + VideoOnlySeekCompleted, + } mType; + + using DataType = Variant; + DataType mData; + + MOZ_IMPLICIT MediaPlaybackEvent(EventType aType) + : mType(aType) + , mData(Nothing{}) + { + } }; enum class VideoDecodeMode : uint8_t @@ -246,8 +258,10 @@ public: MediaDecoderEventVisibility>& FirstFrameLoadedEvent() { return mFirstFrameLoadedEvent; } - MediaEventSource& - OnPlaybackEvent() { return mOnPlaybackEvent; } + MediaEventSource& OnPlaybackEvent() + { + return mOnPlaybackEvent; + } MediaEventSource& OnPlaybackErrorEvent() { return mOnPlaybackErrorEvent; } @@ -659,7 +673,7 @@ private: MediaEventProducerExc, MediaDecoderEventVisibility> mFirstFrameLoadedEvent; - MediaEventProducer mOnPlaybackEvent; + MediaEventProducer mOnPlaybackEvent; MediaEventProducer mOnPlaybackErrorEvent; MediaEventProducer mOnDecoderDoctorEvent; From d6c20167804ff7f642690b04c7234a8e9c5053e3 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Fri, 1 Dec 2017 11:26:03 +0800 Subject: [PATCH 028/219] Bug 1421179. P2 - mPlaybackStatistics should accumulate bytes as playback position progresses. r=bechen,gerald The original code accumulates bytes as the underlying decoder moves on which is wrong. MozReview-Commit-ID: 72hTwOHwKRh --HG-- extra : rebase_source : 68b62543314c7ecc823473a8dbf14e17d2a6eb7b --- dom/media/ChannelMediaDecoder.cpp | 16 +++++++++---- dom/media/ChannelMediaDecoder.h | 6 +++++ dom/media/MediaDecoder.cpp | 3 --- dom/media/MediaDecoder.h | 6 ----- dom/media/MediaDecoderStateMachine.cpp | 32 ++++++++++++++++++-------- dom/media/MediaDecoderStateMachine.h | 18 +++++++++------ 6 files changed, 52 insertions(+), 29 deletions(-) diff --git a/dom/media/ChannelMediaDecoder.cpp b/dom/media/ChannelMediaDecoder.cpp index 36015fde81ad..07f7d12db9d4 100644 --- a/dom/media/ChannelMediaDecoder.cpp +++ b/dom/media/ChannelMediaDecoder.cpp @@ -320,9 +320,6 @@ ChannelMediaDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) } MOZ_ASSERT(GetStateMachine()); - if (aOffset >= mDecoderPosition) { - mPlaybackStatistics.AddBytes(aBytes); - } mDecoderPosition = aOffset + aBytes; } @@ -356,12 +353,23 @@ ChannelMediaDecoder::OnPlaybackEvent(MediaPlaybackEvent&& aEvent) MOZ_ASSERT(NS_IsMainThread()); switch (aEvent.mType) { case MediaPlaybackEvent::PlaybackStarted: + mPlaybackPosition = aEvent.mData.as(); mPlaybackStatistics.Start(); break; - case MediaPlaybackEvent::PlaybackStopped: + case MediaPlaybackEvent::PlaybackProgressed: { + int64_t newPos = aEvent.mData.as(); + mPlaybackStatistics.AddBytes(newPos - mPlaybackPosition); + mPlaybackPosition = newPos; + break; + } + case MediaPlaybackEvent::PlaybackStopped: { + int64_t newPos = aEvent.mData.as(); + mPlaybackStatistics.AddBytes(newPos - mPlaybackPosition); + mPlaybackPosition = newPos; mPlaybackStatistics.Stop(); ComputePlaybackRate(); break; + } default: break; } diff --git a/dom/media/ChannelMediaDecoder.h b/dom/media/ChannelMediaDecoder.h index 8bca8284ca0c..4d43b6051fb0 100644 --- a/dom/media/ChannelMediaDecoder.h +++ b/dom/media/ChannelMediaDecoder.h @@ -158,6 +158,12 @@ private: // True when our media stream has been pinned. We pin the stream // while seeking. bool mPinnedForSeek = false; + + // Current playback position in the stream. This is (approximately) + // where we're up to playing back the stream. This is not adjusted + // during decoder seek operations, but it's updated at the end when we + // start playing back again. + int64_t mPlaybackPosition = 0; }; } // namespace mozilla diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 67b07cfa6d62..131dc47b805d 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -403,7 +403,6 @@ MediaDecoder::MediaDecoder(MediaDecoderInit& aInit) , INIT_MIRROR(mBuffered, TimeIntervals()) , INIT_MIRROR(mCurrentPosition, TimeUnit::Zero()) , INIT_MIRROR(mStateMachineDuration, NullableTimeUnit()) - , INIT_MIRROR(mPlaybackPosition, 0) , INIT_MIRROR(mIsAudioDataAudible, false) , INIT_CANONICAL(mVolume, aInit.mVolume) , INIT_CANONICAL(mPreservesPitch, aInit.mPreservesPitch) @@ -1300,7 +1299,6 @@ MediaDecoder::ConnectMirrors(MediaDecoderStateMachine* aObject) mStateMachineDuration.Connect(aObject->CanonicalDuration()); mBuffered.Connect(aObject->CanonicalBuffered()); mCurrentPosition.Connect(aObject->CanonicalCurrentPosition()); - mPlaybackPosition.Connect(aObject->CanonicalPlaybackOffset()); mIsAudioDataAudible.Connect(aObject->CanonicalIsAudioDataAudible()); } @@ -1311,7 +1309,6 @@ MediaDecoder::DisconnectMirrors() mStateMachineDuration.DisconnectIfConnected(); mBuffered.DisconnectIfConnected(); mCurrentPosition.DisconnectIfConnected(); - mPlaybackPosition.DisconnectIfConnected(); mIsAudioDataAudible.DisconnectIfConnected(); } diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 857c99e1d3c3..2c10aa734120 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -604,12 +604,6 @@ protected: // Duration of the media resource according to the state machine. Mirror mStateMachineDuration; - // Current playback position in the stream. This is (approximately) - // where we're up to playing back the stream. This is not adjusted - // during decoder seek operations, but it's updated at the end when we - // start playing back again. - Mirror mPlaybackPosition; - // Used to distinguish whether the audio is producing sound. Mirror mIsAudioDataAudible; diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index ad39c2058736..93eea3e80a34 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -2634,7 +2634,6 @@ ShutdownState::Enter() master->mDuration.DisconnectAll(); master->mCurrentPosition.DisconnectAll(); - master->mPlaybackOffset.DisconnectAll(); master->mIsAudioDataAudible.DisconnectAll(); // Shut down the watch manager to stop further notifications. @@ -2687,7 +2686,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, INIT_MIRROR(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE), INIT_CANONICAL(mDuration, NullableTimeUnit()), INIT_CANONICAL(mCurrentPosition, TimeUnit::Zero()), - INIT_CANONICAL(mPlaybackOffset, 0), INIT_CANONICAL(mIsAudioDataAudible, false) #ifdef XP_WIN , mShouldUseHiResTimers(Preferences::GetBool("media.hi-res-timers.enabled", true)) @@ -2833,14 +2831,14 @@ void MediaDecoderStateMachine::OnAudioPopped(const RefPtr& aSample) { MOZ_ASSERT(OnTaskQueue()); - mPlaybackOffset = std::max(mPlaybackOffset.Ref(), aSample->mOffset); + mPlaybackOffset = std::max(mPlaybackOffset, aSample->mOffset); } void MediaDecoderStateMachine::OnVideoPopped(const RefPtr& aSample) { MOZ_ASSERT(OnTaskQueue()); - mPlaybackOffset = std::max(mPlaybackOffset.Ref(), aSample->mOffset); + mPlaybackOffset = std::max(mPlaybackOffset, aSample->mOffset); } bool @@ -2906,9 +2904,9 @@ MediaDecoderStateMachine::StopPlayback() MOZ_ASSERT(OnTaskQueue()); LOG("StopPlayback()"); - mOnPlaybackEvent.Notify(MediaPlaybackEvent::PlaybackStopped); - if (IsPlaying()) { + mOnPlaybackEvent.Notify(MediaPlaybackEvent{ + MediaPlaybackEvent::PlaybackStopped, mPlaybackOffset }); mMediaSink->SetPlaying(false); MOZ_ASSERT(!IsPlaying()); #ifdef XP_WIN @@ -2937,7 +2935,6 @@ void MediaDecoderStateMachine::MaybeStartPlayback() } LOG("MaybeStartPlayback() starting playback"); - mOnPlaybackEvent.Notify(MediaPlaybackEvent::PlaybackStarted); StartMediaSink(); #ifdef XP_WIN @@ -2958,6 +2955,9 @@ void MediaDecoderStateMachine::MaybeStartPlayback() mMediaSink->SetPlaying(true); MOZ_ASSERT(IsPlaying()); } + + mOnPlaybackEvent.Notify( + MediaPlaybackEvent{ MediaPlaybackEvent::PlaybackStarted, mPlaybackOffset }); } void @@ -3316,6 +3316,14 @@ MediaDecoderStateMachine::StartMediaSink() &MediaDecoderStateMachine::OnMediaSinkVideoError) ->Track(mMediaSinkVideoPromise); } + // Remember the initial offset when playback starts. This will be used + // to calculate the rate at which bytes are consumed as playback moves on. + RefPtr sample = mAudioQueue.PeekFront(); + mPlaybackOffset = sample ? sample->mOffset : 0; + sample = mVideoQueue.PeekFront(); + if (sample && sample->mOffset > mPlaybackOffset) { + mPlaybackOffset = sample->mOffset; + } } } @@ -3494,8 +3502,6 @@ MediaDecoderStateMachine::ResetDecode(TrackSet aTracks) mAudioWaitRequest.DisconnectIfExists(); } - mPlaybackOffset = 0; - mReader->ResetDecode(aTracks); } @@ -3551,6 +3557,14 @@ MediaDecoderStateMachine::UpdatePlaybackPositionPeriodically() int64_t delay = std::max(1, AUDIO_DURATION_USECS / mPlaybackRate); ScheduleStateMachineIn(TimeUnit::FromMicroseconds(delay)); + + // Notify the listener as we progress in the playback offset. Note it would + // be too intensive to send notifications for each popped audio/video sample. + // It is good enough to send 'PlaybackProgressed' events every 40us (defined + // by AUDIO_DURATION_USECS), and we ensure 'PlaybackProgressed' events are + // always sent after 'PlaybackStarted' and before 'PlaybackStopped'. + mOnPlaybackEvent.Notify(MediaPlaybackEvent{ + MediaPlaybackEvent::PlaybackProgressed, mPlaybackOffset }); } void diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index f05a9ba5d338..27d535c903bf 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -120,6 +120,7 @@ struct MediaPlaybackEvent { PlaybackStarted, PlaybackStopped, + PlaybackProgressed, PlaybackEnded, SeekStarted, Loop, @@ -140,6 +141,13 @@ struct MediaPlaybackEvent , mData(Nothing{}) { } + + template + MediaPlaybackEvent(EventType aType, T&& aArg) + : mType(aType) + , mData(Forward(aArg)) + { + } }; enum class VideoDecodeMode : uint8_t @@ -684,6 +692,9 @@ private: bool mSeamlessLoopingAllowed; + // Current playback position in the stream in bytes. + int64_t mPlaybackOffset = 0; + private: // The buffered range. Mirrored from the decoder thread. Mirror mBuffered; @@ -718,9 +729,6 @@ private: // playback position. Canonical mCurrentPosition; - // Current playback position in the stream in bytes. - Canonical mPlaybackOffset; - // Used to distinguish whether the audio is producing sound. Canonical mIsAudioDataAudible; @@ -735,10 +743,6 @@ public: { return &mCurrentPosition; } - AbstractCanonical* CanonicalPlaybackOffset() - { - return &mPlaybackOffset; - } AbstractCanonical* CanonicalIsAudioDataAudible() { return &mIsAudioDataAudible; From f2f9809386302fd1eba997f2be52dec8fbd31266 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Fri, 1 Dec 2017 12:25:00 +0800 Subject: [PATCH 029/219] Bug 1421179. P3 - ComputePlaybackRate() should call mPlaybackStatistics.GetRate() instead of mPlaybackStatistics.GetRateAtLastStop(). r=bechen,gerald Otherwise mPlaybackBytesPerSecond will always be zero before playback is stopped for the 1st time. This is important for a live stream where playback rate can't be calculated from the duration and resource length (-1). MozReview-Commit-ID: GEojREzHVEz --HG-- extra : rebase_source : 6f770b54597abb303631da8e41c3199d92ebda6e --- dom/media/ChannelMediaDecoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/ChannelMediaDecoder.cpp b/dom/media/ChannelMediaDecoder.cpp index 07f7d12db9d4..e554cf646eef 100644 --- a/dom/media/ChannelMediaDecoder.cpp +++ b/dom/media/ChannelMediaDecoder.cpp @@ -411,7 +411,7 @@ ChannelMediaDecoder::ComputePlaybackRate() } bool reliable = false; - mPlaybackBytesPerSecond = mPlaybackStatistics.GetRateAtLastStop(&reliable); + mPlaybackBytesPerSecond = mPlaybackStatistics.GetRate(&reliable); mPlaybackRateReliable = reliable; } From 9ee1f72e41d03189d15960199e723c8308f6fe8e Mon Sep 17 00:00:00 2001 From: Alan Jeffrey Date: Fri, 1 Dec 2017 06:52:26 -0600 Subject: [PATCH 030/219] servo: Merge #19344 - Download the buildbot statistics and save them as CSV (from asajeffrey:buldbot-perf-download-json); r=jdm Download timing data from build.servo.org, and convert it to CSV for feeding to Google Data Studio. --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes do not require tests because this is test infrastructure Source-Repo: https://github.com/servo/servo Source-Revision: 9da7663e29af1a00980c3e7f30f8e0ae4760979d --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 7cf8bcbc2c6636c2346a9357f16564ee66e96653 --- servo/etc/ci/performance/.gitignore | 1 + .../performance/download_buildbot_timings.py | 187 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 servo/etc/ci/performance/download_buildbot_timings.py diff --git a/servo/etc/ci/performance/.gitignore b/servo/etc/ci/performance/.gitignore index 01adf45a40da..2f10274f854a 100644 --- a/servo/etc/ci/performance/.gitignore +++ b/servo/etc/ci/performance/.gitignore @@ -1,6 +1,7 @@ servo/* output.png output/* +.cache/* page_load_test/tp5n/* page_load_test/tp5n.zip venv/* diff --git a/servo/etc/ci/performance/download_buildbot_timings.py b/servo/etc/ci/performance/download_buildbot_timings.py new file mode 100644 index 000000000000..1bc8fd468074 --- /dev/null +++ b/servo/etc/ci/performance/download_buildbot_timings.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import argparse +import csv +from datetime import datetime, date +import httplib2 +import json +from math import floor +import os + + +def main(): + parser = argparse.ArgumentParser( + description="Download buildbot metadata" + ) + parser.add_argument("--index-url", + type=str, + default='http://build.servo.org/json', + help="the URL to get the JSON index data index from. " + "Default: http://build.servo.org/json") + parser.add_argument("--build-url", + type=str, + default='http://build.servo.org/json/builders/{}/builds/{}', + help="the URL to get the JSON build data from. " + "Default: http://build.servo.org/json/builders/{}/builds/{}") + parser.add_argument("--cache-dir", + type=str, + default='.cache', + help="the directory to cache JSON files in. " + "Default: .cache") + parser.add_argument("--cache-name", + type=str, + default='build-{}-{}.json', + help="the filename to cache JSON data in. " + "Default: build-{}-{}.json") + parser.add_argument("--output-dir", + type=str, + default='output', + help="the directory to save the CSV data to. " + "Default: output") + parser.add_argument("--output-name", + type=str, + default='builds-{}-{}.csv', + help="the filename to save the CSV data to. " + "Default: builds-{}-{}.csv") + parser.add_argument("--verbose", "-v", + action='store_true', + help="print every HTTP request") + args = parser.parse_args() + + http = httplib2.Http() + + os.makedirs(args.cache_dir, exist_ok=True) + os.makedirs(args.output_dir, exist_ok=True) + + # Get the index to find out the list of builder names + # Note: this isn't cached + if args.verbose: + print("Downloading index {}.".format(args.index_url)) + (index_headers, index_data) = http.request(args.index_url, "GET", headers={'cache-control': 'no-cache'}) + if args.verbose: + print("Response {}.".format(index_headers)) + index = json.loads(index_data.decode('utf-8')) + + builds = [] + + for builder in index["builders"]: + # The most recent build is at offset -1 + # Fetch it to find out the build number + # Note: this isn't cached + recent_build_url = args.build_url.format(builder, -1) + if args.verbose: + print("Downloading recent build {}.".format(recent_build_url)) + (recent_build_headers, recent_build_data) = http.request( + recent_build_url, + "GET", + headers={'cache-control': 'no-cache'} + ) + if args.verbose: + print("Respose {}.".format(recent_build_headers)) + recent_build = json.loads(recent_build_data.decode('utf-8')) + recent_build_number = recent_build["number"] + + # Download each build, and convert to CSV + for build_number in range(0, recent_build_number): + + # Rather annoyingly, we can't just use the Python http cache, + # because it doesn't cache 404 responses. So we roll our own. + cache_json_name = args.cache_name.format(builder, build_number) + cache_json = os.path.join(args.cache_dir, cache_json_name) + if os.path.isfile(cache_json): + with open(cache_json) as f: + build = json.load(f) + + else: + # Get the build data + build_url = args.build_url.format(builder, build_number) + if args.verbose: + print("Downloading build {}.".format(build_url)) + (build_headers, build_data) = http.request( + build_url, + "GET", + headers={'cache-control': 'no=cache'} + ) + if args.verbose: + print("Response {}.".format(build_headers)) + + # Only parse the JSON if we got back a 200 response. + if build_headers.status == 200: + build = json.loads(build_data.decode('utf-8')) + # Don't cache current builds. + if build.get('currentStep'): + continue + + elif build_headers.status == 404: + build = {} + + else: + continue + + with open(cache_json, 'w+') as f: + json.dump(build, f) + + if 'times' in build: + builds.append(build) + + years = {} + for build in builds: + build_date = date.fromtimestamp(build['times'][0]) + years.setdefault(build_date.year, {}).setdefault(build_date.month, []).append(build) + + for year, months in years.items(): + for month, builds in months.items(): + + output_name = args.output_name.format(year, month) + output = os.path.join(args.output_dir, output_name) + + # Create the CSV file. + if args.verbose: + print('Creating file {}.'.format(output)) + with open(output, 'w+') as output_file: + output_csv = csv.writer(output_file) + + # The CSV column names + output_csv.writerow([ + 'builder', + 'buildNumber', + 'buildTimestamp', + 'stepName', + 'stepText', + 'stepNumber', + 'stepStart', + 'stepFinish' + ]) + + for build in builds: + + builder = build["builderName"] + build_number = build["number"] + build_timestamp = datetime.fromtimestamp(build["times"][0]).replace(microsecond=0) + + # Write out the timing data for each step + for step in build["steps"]: + if step["isFinished"]: + step_name = step["name"] + step_text = ' '.join(step["text"]) + step_number = step["step_number"] + step_start = floor(step["times"][0]) + step_finish = floor(step["times"][1]) + output_csv.writerow([ + builder, + build_number, + build_timestamp, + step_name, + step_text, + step_number, + step_start, + step_finish + ]) + + +if __name__ == "__main__": + main() From a7c09638b3bdc1f3080322d9a6caa633db8193ea Mon Sep 17 00:00:00 2001 From: Sean Lee Date: Wed, 22 Nov 2017 15:57:33 +0800 Subject: [PATCH 031/219] Bug 1416664 - Identify the sections for the fields without the section part of autocomplete attr. r=lchang,ralin MozReview-Commit-ID: 7La8Bn0TF1y --HG-- extra : rebase_source : 4864178cdfbb644912bcfb9dc1a45806f1cae46d --- .../formautofill/FormAutofillHeuristics.jsm | 91 ++++++++++++++----- .../test/fixtures/multiple_section.html | 75 +++++++++++---- .../unit/heuristics/test_multiple_section.js | 31 +++++++ 3 files changed, 153 insertions(+), 44 deletions(-) diff --git a/browser/extensions/formautofill/FormAutofillHeuristics.jsm b/browser/extensions/formautofill/FormAutofillHeuristics.jsm index ed93d618764c..b5d4fa76b908 100644 --- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm +++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm @@ -21,6 +21,7 @@ FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]); const PREF_HEURISTICS_ENABLED = "extensions.formautofill.heuristics.enabled"; const PREF_SECTION_ENABLED = "extensions.formautofill.section.enabled"; +const DEFAULT_SECTION_NAME = "-moz-section-default"; /** * A scanner for traversing all elements in a form and retrieving the field @@ -35,11 +36,13 @@ class FieldScanner { * @param {Array.DOMElement} elements * The elements from a form for each parser. */ - constructor(elements) { + constructor(elements, {allowDuplicates = false, sectionEnabled = true}) { this._elementsWeakRef = Cu.getWeakReference(elements); this.fieldDetails = []; this._parsingIndex = 0; this._sections = []; + this._allowDuplicates = allowDuplicates; + this._sectionEnabled = sectionEnabled; } get _elements() { @@ -112,22 +115,63 @@ class FieldScanner { }); } - getSectionFieldDetails(allowDuplicates) { - // TODO: [Bug 1416664] If there is only one section which is not defined by - // `autocomplete` attribute, the sections should be classified by the - // heuristics. - return this._sections.map(section => { - if (allowDuplicates) { - return section.fieldDetails; + _classifySections() { + let fieldDetails = this._sections[0].fieldDetails; + this._sections = []; + let seenTypes = new Set(); + let previousType; + let sectionCount = 0; + + for (let fieldDetail of fieldDetails) { + if (!fieldDetail.fieldName) { + continue; } - return this._trimFieldDetails(section.fieldDetails); - }); + if (seenTypes.has(fieldDetail.fieldName) && + previousType != fieldDetail.fieldName) { + seenTypes.clear(); + sectionCount++; + } + previousType = fieldDetail.fieldName; + seenTypes.add(fieldDetail.fieldName); + delete fieldDetail._duplicated; + this._pushToSection(DEFAULT_SECTION_NAME + "-" + sectionCount, fieldDetail); + } + } + + /** + * The result is an array contains the sections with its belonging field + * details. If `this._sections` contains one section only with the default + * section name (DEFAULT_SECTION_NAME), `this._classifySections` should be + * able to identify all sections in the heuristic way. + * + * @returns {Array} + * The array with the sections, and the belonging fieldDetails are in + * each section. + */ + getSectionFieldDetails() { + // When the section feature is disabled, `getSectionFieldDetails` should + // provide a single section result. + if (!this._sectionEnabled) { + return [this._getFinalDetails(this.fieldDetails)]; + } + if (this._sections.length == 0) { + return []; + } + if (this._sections.length == 1 && this._sections[0].name == DEFAULT_SECTION_NAME) { + this._classifySections(); + } + + return this._sections.map(section => + this._getFinalDetails(section.fieldDetails) + ); } /** * This function will prepare an autocomplete info object with getInfo * function and push the detail to fieldDetails property. Any duplicated * detail will be marked as _duplicated = true for the parser. + * Any field will be pushed into `this._sections` based on the section name + * in `autocomplete` attribute. * * Any element without the related detail will be used for adding the detail * to the end of field details. @@ -173,7 +217,7 @@ class FieldScanner { if (info.addressType) { names.push(info.addressType); } - return names.length ? names.join(" ") : "-moz-section-default"; + return names.length ? names.join(" ") : DEFAULT_SECTION_NAME; } /** @@ -205,7 +249,10 @@ class FieldScanner { } /** - * Provide the field details without invalid field name and duplicated fields. + * Provide the final field details without invalid field name, and the + * duplicated fields will be removed as well. For the debugging purpose, + * the final `fieldDetails` will include the duplicated fields if + * `_allowDuplicates` is true. * * @param {Array} fieldDetails * The field details for trimming. @@ -213,14 +260,13 @@ class FieldScanner { * The array with the field details without invalid field name and * duplicated fields. */ - _trimFieldDetails(fieldDetails) { + _getFinalDetails(fieldDetails) { + if (this._allowDuplicates) { + return fieldDetails.filter(f => f.fieldName); + } return fieldDetails.filter(f => f.fieldName && !f._duplicated); } - getFieldDetails(allowDuplicates) { - return allowDuplicates ? this.fieldDetails : this._trimFieldDetails(this.fieldDetails); - } - elementExisting(index) { return index < this._elements.length; } @@ -651,7 +697,8 @@ this.FormAutofillHeuristics = { return []; } - let fieldScanner = new FieldScanner(eligibleFields); + let fieldScanner = new FieldScanner(eligibleFields, + {allowDuplicates, sectionEnabled: this._sectionEnabled}); while (!fieldScanner.parsingFinished) { let parsedPhoneFields = this._parsePhoneFields(fieldScanner); let parsedAddressFields = this._parseAddressFields(fieldScanner); @@ -666,13 +713,7 @@ this.FormAutofillHeuristics = { LabelUtils.clearLabelMap(); - if (!this._sectionEnabled) { - // When the section feature is disabled, `getFormInfo` should provide a - // single section result. - return [fieldScanner.getFieldDetails(allowDuplicates)]; - } - - return fieldScanner.getSectionFieldDetails(allowDuplicates); + return fieldScanner.getSectionFieldDetails(); }, _regExpTableHashValue(...signBits) { diff --git a/browser/extensions/formautofill/test/fixtures/multiple_section.html b/browser/extensions/formautofill/test/fixtures/multiple_section.html index fb2ca0378174..c145fa2a77a4 100644 --- a/browser/extensions/formautofill/test/fixtures/multiple_section.html +++ b/browser/extensions/formautofill/test/fixtures/multiple_section.html @@ -11,32 +11,69 @@

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+

-
-
+
+

-
-
+
+
+

+ + +

+ + +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+

diff --git a/browser/extensions/formautofill/test/unit/heuristics/test_multiple_section.js b/browser/extensions/formautofill/test/unit/heuristics/test_multiple_section.js index a314ff56442d..39c9c3502153 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/test_multiple_section.js +++ b/browser/extensions/formautofill/test/unit/heuristics/test_multiple_section.js @@ -41,6 +41,37 @@ runHeuristicsTest([ {"section": "section-my", "addressType": "", "contactType": "", "fieldName": "country"}, ], ], + [ + [ + {"section": "", "addressType": "", "contactType": "", "fieldName": "name"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "organization"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "country"}, + ], + [ + {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "country"}, + ], + [ + {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "country"}, + {"section": "", "addressType": "", "contactType": "work", "fieldName": "tel"}, + {"section": "", "addressType": "", "contactType": "work", "fieldName": "email"}, + ], + [ + {"section": "", "addressType": "", "contactType": "home", "fieldName": "tel"}, + {"section": "", "addressType": "", "contactType": "home", "fieldName": "email"}, + ], + ], ], }, ], "../../fixtures/"); From f82090e4d86782a016772ce63154d79e772f80f0 Mon Sep 17 00:00:00 2001 From: Sean Lee Date: Mon, 27 Nov 2017 16:42:12 +0800 Subject: [PATCH 032/219] Bug 1416664 - Move the duplication logic to _trimFieldDetails function. r=lchang,ralin --HG-- extra : rebase_source : a1ea77e58f2b03e62ac5603a0e62de6c4df5dbde --- .../formautofill/FormAutofillHeuristics.jsm | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/browser/extensions/formautofill/FormAutofillHeuristics.jsm b/browser/extensions/formautofill/FormAutofillHeuristics.jsm index b5d4fa76b908..cb7a5bf47ca9 100644 --- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm +++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm @@ -133,7 +133,6 @@ class FieldScanner { } previousType = fieldDetail.fieldName; seenTypes.add(fieldDetail.fieldName); - delete fieldDetail._duplicated; this._pushToSection(DEFAULT_SECTION_NAME + "-" + sectionCount, fieldDetail); } } @@ -168,8 +167,7 @@ class FieldScanner { /** * This function will prepare an autocomplete info object with getInfo - * function and push the detail to fieldDetails property. Any duplicated - * detail will be marked as _duplicated = true for the parser. + * function and push the detail to fieldDetails property. * Any field will be pushed into `this._sections` based on the section name * in `autocomplete` attribute. * @@ -198,13 +196,6 @@ class FieldScanner { fieldInfo._reason = info._reason; } - // Store the association between the field metadata and the element. - if (this.findSameField(info) != -1) { - // A field with the same identifier already exists. - log.debug("Not collecting a field matching another with the same info:", info); - fieldInfo._duplicated = true; - } - this.fieldDetails.push(fieldInfo); this._pushToSection(this._getSectionName(fieldInfo), fieldInfo); } @@ -234,18 +225,12 @@ class FieldScanner { throw new Error("Try to update the non-existing field detail."); } this.fieldDetails[index].fieldName = fieldName; - - delete this.fieldDetails[index]._duplicated; - let indexSame = this.findSameField(this.fieldDetails[index]); - if (indexSame != index && indexSame != -1) { - this.fieldDetails[index]._duplicated = true; - } } - findSameField(info) { - return this.fieldDetails.findIndex(f => f.section == info.section && - f.addressType == info.addressType && - f.fieldName == info.fieldName); + _isSameField(field1, field2) { + return field1.section == field2.section && + field1.addressType == field2.addressType && + field1.fieldName == field2.fieldName; } /** @@ -264,7 +249,16 @@ class FieldScanner { if (this._allowDuplicates) { return fieldDetails.filter(f => f.fieldName); } - return fieldDetails.filter(f => f.fieldName && !f._duplicated); + + let dedupedFieldDetails = []; + for (let fieldDetail of fieldDetails) { + if (fieldDetail.fieldName && !dedupedFieldDetails.find(f => this._isSameField(fieldDetail, f))) { + dedupedFieldDetails.push(fieldDetail); + } else { + log.debug("Not collecting an invalid field or matching another with the same info:", fieldDetail); + } + } + return dedupedFieldDetails; } elementExisting(index) { From 6eccd27e0ca2b9ea29e1670f2e0d5c8df80594e4 Mon Sep 17 00:00:00 2001 From: Sean Lee Date: Mon, 27 Nov 2017 17:14:35 +0800 Subject: [PATCH 033/219] Bug 1416664 - Modify the heuristic tests to verify the multiple section feature. r=lchang,ralin MozReview-Commit-ID: HWZarpBVFYp --HG-- extra : rebase_source : e112c105d0e402529805b15adcc5a9e8ab79d60a --- .../heuristics/third_party/test_BestBuy.js | 4 ++-- .../unit/heuristics/third_party/test_CDW.js | 7 ++++-- .../heuristics/third_party/test_CostCo.js | 5 ++-- .../unit/heuristics/third_party/test_Macys.js | 7 ++---- .../heuristics/third_party/test_NewEgg.js | 3 +++ .../third_party/test_OfficeDepot.js | 4 ++-- .../unit/heuristics/third_party/test_QVC.js | 10 ++++---- .../unit/heuristics/third_party/test_Sears.js | 20 ++++++++-------- .../heuristics/third_party/test_Staples.js | 4 ++-- .../heuristics/third_party/test_Walmart.js | 6 ++--- .../test/unit/test_collectFormFields.js | 23 +++++++++++++------ 11 files changed, 52 insertions(+), 41 deletions(-) diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_BestBuy.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_BestBuy.js index f0a49f62f9bd..d5331f9fcfe1 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_BestBuy.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_BestBuy.js @@ -6,7 +6,7 @@ runHeuristicsTest([ { fixturePath: "Checkout_ShippingAddress.html", expectedResult: [ - [[]], // Search form + [], // Search form [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"}, @@ -26,7 +26,7 @@ runHeuristicsTest([ }, { fixturePath: "Checkout_Payment.html", expectedResult: [ - [[]], // Search form + [], // Search form [[ // Sign up {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, ]], diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CDW.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CDW.js index 4caf004ce429..193a7f925a19 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CDW.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CDW.js @@ -20,9 +20,10 @@ runHeuristicsTest([ {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"}, + ], [ // The below "tel-extension" is correct and removed due to the // duplicated field above. -// {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"}, ]], [], ], @@ -47,6 +48,8 @@ runHeuristicsTest([ {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"}, // {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"}, + ], [ + {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"}, ]], [], ], @@ -54,7 +57,7 @@ runHeuristicsTest([ fixturePath: "Checkout_Logon.html", expectedResult: [ [], - [[]], + [], [], ], }, diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CostCo.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CostCo.js index ca7d752301f5..45d1911e1dc0 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CostCo.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CostCo.js @@ -50,7 +50,6 @@ runHeuristicsTest([ {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"}, - // {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"}, // ac-off {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-name"}, // ac-off ]], @@ -58,7 +57,7 @@ runHeuristicsTest([ {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off ]], [], - [[]], + [], [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "additional-name"}, // middle-name initial @@ -96,7 +95,7 @@ runHeuristicsTest([ }, { fixturePath: "SignIn.html", expectedResult: [ - [[]], + [], [], [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Macys.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Macys.js index 5cbc2676dbe2..8d82f83047af 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Macys.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Macys.js @@ -16,10 +16,7 @@ runHeuristicsTest([ {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"}, ]], - [ -/* -*/ - ], + [], ], }, { fixturePath: "Checkout_Payment.html", @@ -56,7 +53,7 @@ runHeuristicsTest([ [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, ]], - [[]], + [], [], [], ], diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_NewEgg.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_NewEgg.js index 20b256a6220a..a118c971b45e 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_NewEgg.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_NewEgg.js @@ -26,6 +26,9 @@ runHeuristicsTest([ [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-name"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off + ], [ + {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-name"}, + {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"}, // {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"}, diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_OfficeDepot.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_OfficeDepot.js index 0c3993cbd5c0..0fa6dbabb8e8 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_OfficeDepot.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_OfficeDepot.js @@ -55,9 +55,9 @@ runHeuristicsTest([ }, { fixturePath: "SignIn.html", expectedResult: [ - [[ // ac-off + [ // ac-off // {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, - ]], + ], ], }, ], "../../../fixtures/third_party/OfficeDepot/"); diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_QVC.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_QVC.js index 092ce48c23db..968a8885c0bc 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_QVC.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_QVC.js @@ -13,11 +13,11 @@ runHeuristicsTest([ // {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-day"}, // select // {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-year"}, // {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-type"}, - {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp"}, - // {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"}, + ], [ + {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, ]], [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, @@ -33,11 +33,11 @@ runHeuristicsTest([ // {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-day"}, // select // {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-year"}, // select // {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-type"}, // select - {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp"}, - // {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"}, + ], [ + {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off ]], [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, @@ -46,7 +46,7 @@ runHeuristicsTest([ }, { fixturePath: "SignIn.html", expectedResult: [ - [[]], + [], [[ // Sign in {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, ]], diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Sears.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Sears.js index a397c00b991a..790a831e5273 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Sears.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Sears.js @@ -7,11 +7,11 @@ runHeuristicsTest([ fixturePath: "ShippingAddress.html", expectedResult: [ [], - [[]], // search form, ac-off - [[ // ac-off + [], // search form, ac-off + [ // ac-off // {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, - ]], - [[ // check-out, ac-off + ], + [ // check-out, ac-off /* {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"}, @@ -24,7 +24,7 @@ runHeuristicsTest([ // {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, */ - ]], + ], [ // ac-off /* {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, @@ -40,18 +40,18 @@ runHeuristicsTest([ // {"section": "", "addressType": "", "contactType": "", "fieldName": "new-password"}, */ ], - [[ // ac-off + [ // ac-off // {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, - ]], - [[ // ac-off + ], + [ // ac-off // {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, - ]], + ], ], }, { fixturePath: "PaymentOptions.html", expectedResult: [ [], - [[]], // search + [], // search [[ // credit card {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // FIXME: bug 1392958 - Cardholder name field should be detected as cc-name diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Staples.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Staples.js index ea7466566a75..0512d15bb15e 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Staples.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Staples.js @@ -6,7 +6,7 @@ runHeuristicsTest([ { fixturePath: "Basic.html", expectedResult: [ - [[ // ac-off + [ // ac-off // {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"}, // {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"}, // {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"}, @@ -14,7 +14,7 @@ runHeuristicsTest([ // {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"}, // {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"}, // {"section": "", "addressType": "", "contactType": "", "fieldName": "organization"}, - ]], + ], ], }, { fixturePath: "Basic_ac_on.html", diff --git a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Walmart.js b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Walmart.js index e350482be09b..90df86bd0e89 100644 --- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Walmart.js +++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Walmart.js @@ -9,7 +9,7 @@ runHeuristicsTest([ [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"}, ]], - [[]], + [], [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, // {"section": "", "addressType": "", "contactType": "", "fieldName": "password"}, // ac-off @@ -25,7 +25,7 @@ runHeuristicsTest([ }, { fixturePath: "Payment.html", expectedResult: [ - [[]], + [], [ [ {"section": "section-payment", "addressType": "", "contactType": "", "fieldName": "given-name"}, @@ -50,7 +50,7 @@ runHeuristicsTest([ [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"}, ]], - [[]], + [], [[ {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"}, diff --git a/browser/extensions/formautofill/test/unit/test_collectFormFields.js b/browser/extensions/formautofill/test/unit/test_collectFormFields.js index 84c366368660..b97173171ea8 100644 --- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js +++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js @@ -292,10 +292,22 @@ const TESTCASES = [ {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"}, + ], + creditCardFieldDetails: [], + }, { + addressFieldDetails: [ {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"}, + + // TODO Bug 1421181 - "tel-country-code" field should belong to the next + // section. There should be a way to group the related fields during the + // parsing stage. {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-country-code"}, + ], + creditCardFieldDetails: [], + }, { + addressFieldDetails: [ {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"}, {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"}, @@ -461,13 +473,10 @@ for (let tc of TESTCASES) { Assert.equal(detail.elementWeakRef.get(), testCaseDetails[index].elementWeakRef.get(), "DOM reference"); }); } - for (let i = 0; i < testcase.sections.length; i++) { - let section = testcase.sections[i]; - [ - section.addressFieldDetails, - section.creditCardFieldDetails, - ].forEach(details => setElementWeakRef(details)); - } + setElementWeakRef(testcase.sections.reduce((fieldDetails, section) => { + fieldDetails.push(...section.addressFieldDetails, ...section.creditCardFieldDetails); + return fieldDetails; + }, [])); setElementWeakRef(testcase.validFieldDetails); let handler = new FormAutofillHandler(formLike); From f8ea506f33245f1dfea0f4c0d1499e965bac307e Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 1 Dec 2017 09:50:05 -0500 Subject: [PATCH 034/219] Bug 1421275 - Update webrender to commit e3dd85359580074f4ca4a554d9a3c85779f8de64. r=jrmuizel Includes regenerated FFI header file. MozReview-Commit-ID: 8yVB4QE87eJ --HG-- extra : rebase_source : 6d38d002edbcbc77f044d6307c5cf1212af84555 --- gfx/doc/README.webrender | 2 +- gfx/webrender/Cargo.toml | 10 +- gfx/webrender/src/border.rs | 25 +- gfx/webrender/src/box_shadow.rs | 43 ++- gfx/webrender/src/clip_scroll_tree.rs | 2 +- gfx/webrender/src/debug_server.rs | 53 +++- gfx/webrender/src/device.rs | 57 ++-- gfx/webrender/src/frame.rs | 39 ++- gfx/webrender/src/frame_builder.rs | 80 ++++-- gfx/webrender/src/glyph_rasterizer.rs | 13 +- gfx/webrender/src/gpu_cache.rs | 37 ++- gfx/webrender/src/internal_types.rs | 15 + gfx/webrender/src/lib.rs | 5 + gfx/webrender/src/picture.rs | 16 +- gfx/webrender/src/platform/macos/font.rs | 21 +- gfx/webrender/src/platform/unix/font.rs | 35 +-- gfx/webrender/src/platform/windows/font.rs | 73 +++-- gfx/webrender/src/prim_store.rs | 4 +- gfx/webrender/src/profiler.rs | 114 +++++++- gfx/webrender/src/query.rs | 18 +- gfx/webrender/src/render_backend.rs | 4 +- gfx/webrender/src/render_task.rs | 96 ++++++- gfx/webrender/src/renderer.rs | 265 ++++++++++++++---- gfx/webrender/src/resource_cache.rs | 4 +- gfx/webrender/src/tiling.rs | 5 - gfx/webrender_api/Cargo.toml | 6 +- gfx/webrender_api/src/api.rs | 4 + gfx/webrender_api/src/display_item.rs | 20 +- gfx/webrender_api/src/display_list.rs | 1 - gfx/webrender_api/src/font.rs | 64 +++-- gfx/webrender_bindings/Cargo.toml | 6 +- .../webrender_ffi_generated.h | 18 +- 32 files changed, 826 insertions(+), 329 deletions(-) diff --git a/gfx/doc/README.webrender b/gfx/doc/README.webrender index 0e9f30104ffa..bbdac84de7f1 100644 --- a/gfx/doc/README.webrender +++ b/gfx/doc/README.webrender @@ -175,4 +175,4 @@ Troubleshooting tips: ------------------------------------------------------------------------------- The version of WebRender currently in the tree is: -e30886d78c91bdd433fd978a39c511ef9416608e +e3dd85359580074f4ca4a554d9a3c85779f8de64 diff --git a/gfx/webrender/Cargo.toml b/gfx/webrender/Cargo.toml index 3587f09be39d..4ee4b6f38711 100644 --- a/gfx/webrender/Cargo.toml +++ b/gfx/webrender/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender" -version = "0.54.0" +version = "0.55.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" @@ -10,7 +10,7 @@ build = "build.rs" default = ["freetype-lib"] freetype-lib = ["freetype/servo-freetype-sys"] profiler = ["thread_profiler/thread_profiler"] -debugger = ["ws", "serde_json", "serde", "serde_derive"] +debugger = ["ws", "serde_json", "serde", "serde_derive", "image", "base64"] [dependencies] app_units = "0.5.6" @@ -18,7 +18,7 @@ bincode = "0.9" byteorder = "1.0" euclid = "0.15.5" fxhash = "0.2.1" -gleam = "0.4.14" +gleam = "0.4.15" lazy_static = "0.2" log = "0.3" num-traits = "0.1.32" @@ -32,6 +32,8 @@ ws = { optional = true, version = "0.7.3" } serde_json = { optional = true, version = "1.0" } serde = { optional = true, version = "1.0" } serde_derive = { optional = true, version = "1.0" } +image = { optional = true, version = "0.17" } +base64 = { optional = true, version = "0.3.0" } [dev-dependencies] angle = {git = "https://github.com/servo/angle", branch = "servo"} @@ -43,7 +45,7 @@ servo-glutin = "0.13" # for the example apps freetype = { version = "0.3", default-features = false } [target.'cfg(target_os = "windows")'.dependencies] -dwrote = "0.4" +dwrote = "0.4.1" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.4" diff --git a/gfx/webrender/src/border.rs b/gfx/webrender/src/border.rs index d699b51783ad..ed1ecd50a104 100644 --- a/gfx/webrender/src/border.rs +++ b/gfx/webrender/src/border.rs @@ -3,13 +3,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF}; -use api::{EdgeAaSegmentMask, LayerPoint, LayerRect}; +use api::{LayerPoint, LayerRect}; use api::{LayerPrimitiveInfo, LayerSize, NormalBorder, RepeatMode}; use clip::ClipSource; use ellipse::Ellipse; use frame_builder::FrameBuilder; use gpu_cache::GpuDataRequest; -use prim_store::{BorderPrimitiveCpu, RectangleContent, PrimitiveContainer, TexelRect}; +use internal_types::EdgeAaSegmentMask; +use prim_store::{BorderPrimitiveCpu, PrimitiveContainer, TexelRect}; use util::{lerp, pack_as_float}; #[repr(u8)] @@ -382,12 +383,11 @@ impl FrameBuilder { // Add a solid rectangle for each visible edge/corner combination. if top_edge == BorderEdgeKind::Solid { info.rect = LayerRect::new(p0, LayerSize::new(rect_width, top_len)); - info.edge_aa_segment_mask = EdgeAaSegmentMask::BOTTOM; self.add_solid_rectangle( clip_and_scroll, &info, - RectangleContent::Fill(border.top.color), - None, + border.top.color, + EdgeAaSegmentMask::BOTTOM, ); } if left_edge == BorderEdgeKind::Solid { @@ -395,12 +395,11 @@ impl FrameBuilder { LayerPoint::new(p0.x, p0.y + top_len), LayerSize::new(left_len, rect_height - top_len - bottom_len), ); - info.edge_aa_segment_mask = EdgeAaSegmentMask::RIGHT; self.add_solid_rectangle( clip_and_scroll, &info, - RectangleContent::Fill(border.left.color), - None, + border.left.color, + EdgeAaSegmentMask::RIGHT, ); } if right_edge == BorderEdgeKind::Solid { @@ -408,12 +407,11 @@ impl FrameBuilder { LayerPoint::new(p1.x - right_len, p0.y + top_len), LayerSize::new(right_len, rect_height - top_len - bottom_len), ); - info.edge_aa_segment_mask = EdgeAaSegmentMask::LEFT; self.add_solid_rectangle( clip_and_scroll, &info, - RectangleContent::Fill(border.right.color), - None, + border.right.color, + EdgeAaSegmentMask::LEFT, ); } if bottom_edge == BorderEdgeKind::Solid { @@ -421,12 +419,11 @@ impl FrameBuilder { LayerPoint::new(p0.x, p1.y - bottom_len), LayerSize::new(rect_width, bottom_len), ); - info.edge_aa_segment_mask = EdgeAaSegmentMask::TOP; self.add_solid_rectangle( clip_and_scroll, &info, - RectangleContent::Fill(border.bottom.color), - None, + border.bottom.color, + EdgeAaSegmentMask::TOP, ); } } else { diff --git a/gfx/webrender/src/box_shadow.rs b/gfx/webrender/src/box_shadow.rs index 6027f0cdd691..d0a253b9dc46 100644 --- a/gfx/webrender/src/box_shadow.rs +++ b/gfx/webrender/src/box_shadow.rs @@ -4,10 +4,12 @@ use api::{BorderRadiusKind, ColorF, LayerPoint, LayerRect, LayerSize, LayerVector2D}; use api::{BorderRadius, BoxShadowClipMode, LayoutSize, LayerPrimitiveInfo}; -use api::{ClipMode, ClipAndScrollInfo, ComplexClipRegion, EdgeAaSegmentMask, LocalClip}; +use api::{ClipMode, ClipAndScrollInfo, ComplexClipRegion, LocalClip}; use api::{PipelineId}; +use app_units::Au; use clip::ClipSource; use frame_builder::FrameBuilder; +use internal_types::EdgeAaSegmentMask; use prim_store::{PrimitiveContainer, RectangleContent, RectanglePrimitive}; use prim_store::{BrushMaskKind, BrushKind, BrushPrimitive}; use picture::PicturePrimitive; @@ -26,6 +28,25 @@ pub const MAX_BLUR_RADIUS : f32 = 300.; // blurred without being affected by the border radius. pub const MASK_CORNER_PADDING: f32 = 4.0; +#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] +pub struct BoxShadowCacheKey { + pub width: Au, + pub height: Au, + pub blur_radius: Au, + pub spread_radius: Au, + pub offset_x: Au, + pub offset_y: Au, + pub br_top_left_w: Au, + pub br_top_left_h: Au, + pub br_top_right_w: Au, + pub br_top_right_h: Au, + pub br_bottom_left_w: Au, + pub br_bottom_left_h: Au, + pub br_bottom_right_w: Au, + pub br_bottom_right_h: Au, + pub clip_mode: BoxShadowClipMode, +} + impl FrameBuilder { pub fn add_box_shadow( &mut self, @@ -119,6 +140,24 @@ impl FrameBuilder { let blur_offset = BLUR_SAMPLE_SCALE * blur_radius; let mut extra_clips = vec![]; + let cache_key = BoxShadowCacheKey { + width: Au::from_f32_px(shadow_rect.size.width), + height: Au::from_f32_px(shadow_rect.size.height), + blur_radius: Au::from_f32_px(blur_radius), + spread_radius: Au::from_f32_px(spread_radius), + offset_x: Au::from_f32_px(box_offset.x), + offset_y: Au::from_f32_px(box_offset.y), + br_top_left_w: Au::from_f32_px(border_radius.top_left.width), + br_top_left_h: Au::from_f32_px(border_radius.top_left.height), + br_top_right_w: Au::from_f32_px(border_radius.top_right.width), + br_top_right_h: Au::from_f32_px(border_radius.top_right.height), + br_bottom_left_w: Au::from_f32_px(border_radius.bottom_left.width), + br_bottom_left_h: Au::from_f32_px(border_radius.bottom_left.height), + br_bottom_right_w: Au::from_f32_px(border_radius.bottom_right.width), + br_bottom_right_h: Au::from_f32_px(border_radius.bottom_right.height), + clip_mode, + }; + match clip_mode { BoxShadowClipMode::Outset => { let width; @@ -192,6 +231,7 @@ impl FrameBuilder { Vec::new(), clip_mode, radii_kind, + cache_key, pipeline_id, ); pic_prim.add_primitive( @@ -270,6 +310,7 @@ impl FrameBuilder { BoxShadowClipMode::Inset, // TODO(gw): Make use of optimization for inset. BorderRadiusKind::NonUniform, + cache_key, pipeline_id, ); pic_prim.add_primitive( diff --git a/gfx/webrender/src/clip_scroll_tree.rs b/gfx/webrender/src/clip_scroll_tree.rs index e52c3013a488..360f85992b45 100644 --- a/gfx/webrender/src/clip_scroll_tree.rs +++ b/gfx/webrender/src/clip_scroll_tree.rs @@ -82,7 +82,7 @@ pub struct TransformUpdateState { } impl ClipScrollTree { - pub fn new() -> ClipScrollTree { + pub fn new() -> Self { let dummy_pipeline = PipelineId::dummy(); ClipScrollTree { nodes: FastHashMap::default(), diff --git a/gfx/webrender/src/debug_server.rs b/gfx/webrender/src/debug_server.rs index ba454ec4c95d..d75226c31aaa 100644 --- a/gfx/webrender/src/debug_server.rs +++ b/gfx/webrender/src/debug_server.rs @@ -9,6 +9,8 @@ use std::sync::mpsc::{channel, Receiver}; use std::sync::mpsc::Sender; use std::thread; use ws; +use base64::encode; +use image; // Messages that are sent from the render backend to the renderer // debug command queue. These are sent in a separate queue so @@ -58,8 +60,10 @@ impl ws::Handler for Server { "enable_gpu_sample_queries" => DebugCommand::EnableGpuSampleQueries(true), "disable_gpu_sample_queries" => DebugCommand::EnableGpuSampleQueries(false), "fetch_passes" => DebugCommand::FetchPasses, + "fetch_screenshot" => DebugCommand::FetchScreenshot, "fetch_documents" => DebugCommand::FetchDocuments, - "fetch_clipscrolltree" => DebugCommand::FetchClipScrollTree, + "fetch_clip_scroll_tree" => DebugCommand::FetchClipScrollTree, + "fetch_render_tasks" => DebugCommand::FetchRenderTasks, msg => { println!("unknown msg {}", msg); return Ok(()); @@ -255,7 +259,7 @@ pub struct DocumentList { } impl DocumentList { - pub fn new() -> DocumentList { + pub fn new() -> Self { DocumentList { kind: "documents", root: TreeNode::new("root"), @@ -267,6 +271,28 @@ impl DocumentList { } } +#[derive(Serialize)] +pub struct Screenshot { + kind: &'static str, + data: String +} + +impl Screenshot { + pub fn new(width: u32, height: u32, data: Vec) -> Self { + let mut output = Vec::with_capacity((width * height) as usize); + { + let encoder = image::png::PNGEncoder::new(&mut output); + encoder.encode(&data, width, height, image::ColorType::RGBA(8)).unwrap(); + } + + let data = encode(&output); + Screenshot { + kind: "screenshot", + data + } + } +} + // A serializable list of debug information about clip-scroll trees // that can be sent to the client @@ -277,9 +303,28 @@ pub struct ClipScrollTreeList { } impl ClipScrollTreeList { - pub fn new() -> ClipScrollTreeList { + pub fn new() -> Self { ClipScrollTreeList { - kind: "clipscrolltree", + kind: "clip_scroll_tree", + root: TreeNode::new("root"), + } + } + + pub fn add(&mut self, item: TreeNode) { + self.root.add_child(item); + } +} + +#[derive(Serialize)] +pub struct RenderTaskList { + kind: &'static str, + root: TreeNode, +} + +impl RenderTaskList { + pub fn new() -> Self { + RenderTaskList { + kind: "render_tasks", root: TreeNode::new("root"), } } diff --git a/gfx/webrender/src/device.rs b/gfx/webrender/src/device.rs index 97cba8b9f737..c3066f199b8b 100644 --- a/gfx/webrender/src/device.rs +++ b/gfx/webrender/src/device.rs @@ -1424,6 +1424,15 @@ impl Device { } } + pub fn read_pixels(&mut self, width: i32, height: i32) -> Vec { + self.gl.read_pixels( + 0, 0, + width as i32, height as i32, + gl::RGBA, + gl::UNSIGNED_BYTE + ) + } + pub fn bind_vao(&mut self, vao: &VAO) { debug_assert!(self.inside_frame); @@ -1603,29 +1612,11 @@ impl Device { self.frame_id.0 += 1; } - pub fn clear_target(&self, color: Option<[f32; 4]>, depth: Option) { - let mut clear_bits = 0; - - if let Some(color) = color { - self.gl.clear_color(color[0], color[1], color[2], color[3]); - clear_bits |= gl::COLOR_BUFFER_BIT; - } - - if let Some(depth) = depth { - self.gl.clear_depth(depth as f64); - clear_bits |= gl::DEPTH_BUFFER_BIT; - } - - if clear_bits != 0 { - self.gl.clear(clear_bits); - } - } - - pub fn clear_target_rect( + pub fn clear_target( &self, color: Option<[f32; 4]>, depth: Option, - rect: DeviceIntRect, + rect: Option, ) { let mut clear_bits = 0; @@ -1635,20 +1626,28 @@ impl Device { } if let Some(depth) = depth { + debug_assert_ne!(self.gl.get_boolean_v(gl::DEPTH_WRITEMASK), 0); self.gl.clear_depth(depth as f64); clear_bits |= gl::DEPTH_BUFFER_BIT; } if clear_bits != 0 { - self.gl.enable(gl::SCISSOR_TEST); - self.gl.scissor( - rect.origin.x, - rect.origin.y, - rect.size.width, - rect.size.height, - ); - self.gl.clear(clear_bits); - self.gl.disable(gl::SCISSOR_TEST); + match rect { + Some(rect) => { + self.gl.enable(gl::SCISSOR_TEST); + self.gl.scissor( + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height, + ); + self.gl.clear(clear_bits); + self.gl.disable(gl::SCISSOR_TEST); + } + None => { + self.gl.clear(clear_bits); + } + } } } diff --git a/gfx/webrender/src/frame.rs b/gfx/webrender/src/frame.rs index 9f0921ff139b..4b476a62dc40 100644 --- a/gfx/webrender/src/frame.rs +++ b/gfx/webrender/src/frame.rs @@ -17,8 +17,7 @@ use clip_scroll_tree::{ClipScrollTree, ScrollStates}; use euclid::rect; use frame_builder::{FrameBuilder, FrameBuilderConfig, ScrollbarInfo}; use gpu_cache::GpuCache; -use internal_types::{FastHashMap, FastHashSet, RenderedDocument}; -use prim_store::RectangleContent; +use internal_types::{EdgeAaSegmentMask, FastHashMap, FastHashSet, RenderedDocument}; use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters}; use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap}; use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties}; @@ -111,8 +110,8 @@ impl<'a> FlattenContext<'a> { self.builder.add_solid_rectangle( ClipAndScrollInfo::simple(root_reference_frame_id), &info, - RectangleContent::Fill(bg_color), - None, + bg_color, + EdgeAaSegmentMask::empty(), ); } } @@ -128,11 +127,11 @@ impl<'a> FlattenContext<'a> { if self.builder.config.enable_scrollbars { let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0)); let container_rect = LayerRect::new(LayerPoint::zero(), *frame_size); - self.builder.add_solid_rectangle( + self.builder.add_scroll_bar( ClipAndScrollInfo::simple(root_reference_frame_id), &LayerPrimitiveInfo::new(scrollbar_rect), - RectangleContent::Fill(DEFAULT_SCROLLBAR_COLOR), - Some(ScrollbarInfo(root_scroll_frame_id, container_rect)), + DEFAULT_SCROLLBAR_COLOR, + ScrollbarInfo(root_scroll_frame_id, container_rect), ); } @@ -451,23 +450,21 @@ impl<'a> FlattenContext<'a> { SpecificDisplayItem::Rectangle(ref info) => { if !self.try_to_add_rectangle_splitting_on_clip( &prim_info, - RectangleContent::Fill(info.color), + info.color, &clip_and_scroll, ) { self.builder.add_solid_rectangle( clip_and_scroll, &prim_info, - RectangleContent::Fill(info.color), - None, + info.color, + EdgeAaSegmentMask::empty(), ); } } SpecificDisplayItem::ClearRectangle => { - self.builder.add_solid_rectangle( + self.builder.add_clear_rectangle( clip_and_scroll, &prim_info, - RectangleContent::Clear, - None, ); } SpecificDisplayItem::Line(ref info) => { @@ -641,7 +638,7 @@ impl<'a> FlattenContext<'a> { fn try_to_add_rectangle_splitting_on_clip( &mut self, info: &LayerPrimitiveInfo, - content: RectangleContent, + color: ColorF, clip_and_scroll: &ClipAndScrollInfo, ) -> bool { if info.rect.size.area() < 200.0 { // arbitrary threshold @@ -651,10 +648,8 @@ impl<'a> FlattenContext<'a> { // If this rectangle is not opaque, splitting the rectangle up // into an inner opaque region just ends up hurting batching and // doing more work than necessary. - if let RectangleContent::Fill(ColorF{a, ..}) = content { - if a != 1.0 { - return false; - } + if color.a != 1.0 { + return false; } self.opaque_parts.clear(); @@ -688,8 +683,8 @@ impl<'a> FlattenContext<'a> { self.builder.add_solid_rectangle( *clip_and_scroll, &prim_info, - content, - None, + color, + EdgeAaSegmentMask::empty(), ); has_opaque = true; } @@ -709,8 +704,8 @@ impl<'a> FlattenContext<'a> { self.builder.add_solid_rectangle( *clip_and_scroll, &prim_info, - content, - None, + color, + EdgeAaSegmentMask::empty(), ); } true diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs index 3dddd044c1a6..9c75b1507d7a 100644 --- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -21,7 +21,7 @@ use euclid::{SideOffsets2D, vec2}; use frame::FrameId; use glyph_rasterizer::FontInstance; use gpu_cache::GpuCache; -use internal_types::{FastHashMap, FastHashSet}; +use internal_types::{EdgeAaSegmentMask, FastHashMap, FastHashSet}; use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, RasterizationSpace}; use prim_store::{TexelRect, YuvImagePrimitiveCpu}; use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind}; @@ -755,20 +755,61 @@ impl FrameBuilder { &mut self, clip_and_scroll: ClipAndScrollInfo, info: &LayerPrimitiveInfo, - content: RectangleContent, - scrollbar_info: Option, + color: ColorF, + edge_aa_segment_mask: EdgeAaSegmentMask, ) { - if let RectangleContent::Fill(ColorF{a, ..}) = content { - if a == 0.0 { - // Don't add transparent rectangles to the draw list, but do consider them for hit - // testing. This allows specifying invisible hit testing areas. - self.add_primitive_to_hit_testing_list(info, clip_and_scroll); - return; - } + if color.a == 0.0 { + // Don't add transparent rectangles to the draw list, but do consider them for hit + // testing. This allows specifying invisible hit testing areas. + self.add_primitive_to_hit_testing_list(info, clip_and_scroll); + return; } + let prim = RectanglePrimitive { - content, - edge_aa_segment_mask: info.edge_aa_segment_mask, + content: RectangleContent::Fill(color), + edge_aa_segment_mask, + }; + + self.add_primitive( + clip_and_scroll, + info, + Vec::new(), + PrimitiveContainer::Rectangle(prim), + ); + } + + pub fn add_clear_rectangle( + &mut self, + clip_and_scroll: ClipAndScrollInfo, + info: &LayerPrimitiveInfo, + ) { + let prim = RectanglePrimitive { + content: RectangleContent::Clear, + edge_aa_segment_mask: EdgeAaSegmentMask::empty(), + }; + + self.add_primitive( + clip_and_scroll, + info, + Vec::new(), + PrimitiveContainer::Rectangle(prim), + ); + } + + pub fn add_scroll_bar( + &mut self, + clip_and_scroll: ClipAndScrollInfo, + info: &LayerPrimitiveInfo, + color: ColorF, + scrollbar_info: ScrollbarInfo, + ) { + if color.a == 0.0 { + return; + } + + let prim = RectanglePrimitive { + content: RectangleContent::Fill(color), + edge_aa_segment_mask: EdgeAaSegmentMask::empty(), }; let prim_index = self.add_primitive( @@ -778,13 +819,11 @@ impl FrameBuilder { PrimitiveContainer::Rectangle(prim), ); - if let Some(ScrollbarInfo(clip_id, frame_rect)) = scrollbar_info { - self.scrollbar_prims.push(ScrollbarPrimitive { - prim_index, - clip_id, - frame_rect, - }); - } + self.scrollbar_prims.push(ScrollbarPrimitive { + prim_index, + clip_id: scrollbar_info.0, + frame_rect: scrollbar_info.1, + }); } pub fn add_line( @@ -1286,9 +1325,9 @@ impl FrameBuilder { font.bg_color, render_mode, font.subpx_dir, + font.flags, font.platform_options, font.variations.clone(), - font.synthetic_italics, ); let prim = TextRunPrimitiveCpu { font: prim_font, @@ -1594,6 +1633,7 @@ impl FrameBuilder { ClearMode::Transparent, RasterizationSpace::Screen, child_tasks, + None, ); pic.render_task_id = Some(render_tasks.add(root_render_task)); diff --git a/gfx/webrender/src/glyph_rasterizer.rs b/gfx/webrender/src/glyph_rasterizer.rs index f18ea886ac60..8a458fc112a7 100644 --- a/gfx/webrender/src/glyph_rasterizer.rs +++ b/gfx/webrender/src/glyph_rasterizer.rs @@ -5,8 +5,9 @@ #[cfg(test)] use api::{IdNamespace, LayoutPoint}; use api::{ColorF, ColorU, DevicePoint, DeviceUintSize}; -use api::{FontInstancePlatformOptions, FontRenderMode, FontVariation}; -use api::{FontKey, FontTemplate, GlyphDimensions, GlyphKey, SubpixelDirection}; +use api::{FontInstanceFlags, FontInstancePlatformOptions}; +use api::{FontKey, FontRenderMode, FontTemplate, FontVariation}; +use api::{GlyphDimensions, GlyphKey, SubpixelDirection}; use api::{ImageData, ImageDescriptor, ImageFormat, LayerToWorldTransform}; use app_units::Au; use device::TextureFilter; @@ -139,9 +140,9 @@ pub struct FontInstance { pub bg_color: ColorU, pub render_mode: FontRenderMode, pub subpx_dir: SubpixelDirection, + pub flags: FontInstanceFlags, pub platform_options: Option, pub variations: Vec, - pub synthetic_italics: bool, pub transform: FontTransform, } @@ -153,9 +154,9 @@ impl FontInstance { bg_color: ColorU, render_mode: FontRenderMode, subpx_dir: SubpixelDirection, + flags: FontInstanceFlags, platform_options: Option, variations: Vec, - synthetic_italics: bool, ) -> Self { FontInstance { font_key, @@ -164,9 +165,9 @@ impl FontInstance { bg_color, render_mode, subpx_dir, + flags, platform_options, variations, - synthetic_italics, transform: FontTransform::identity(), } } @@ -611,9 +612,9 @@ fn raterize_200_glyphs() { ColorU::new(0, 0, 0, 0), FontRenderMode::Subpixel, SubpixelDirection::Horizontal, + Default::default(), None, Vec::new(), - false, ); let mut glyph_keys = Vec::with_capacity(200); diff --git a/gfx/webrender/src/gpu_cache.rs b/gfx/webrender/src/gpu_cache.rs index 9ab4f8297721..75dbef723de5 100644 --- a/gfx/webrender/src/gpu_cache.rs +++ b/gfx/webrender/src/gpu_cache.rs @@ -113,7 +113,7 @@ pub struct GpuCacheHandle { } impl GpuCacheHandle { - pub fn new() -> GpuCacheHandle { + pub fn new() -> Self { GpuCacheHandle { location: None } } } @@ -170,7 +170,7 @@ struct Block { } impl Block { - fn new(address: GpuCacheAddress, next: Option, frame_id: FrameId) -> Block { + fn new(address: GpuCacheAddress, next: Option, frame_id: FrameId) -> Self { Block { address, next, @@ -193,7 +193,7 @@ struct Row { } impl Row { - fn new(block_count_per_item: usize) -> Row { + fn new(block_count_per_item: usize) -> Self { Row { block_count_per_item, } @@ -300,7 +300,7 @@ struct Texture { } impl Texture { - fn new() -> Texture { + fn new() -> Self { Texture { height: GPU_CACHE_INITIAL_HEIGHT, blocks: Vec::new(), @@ -449,6 +449,7 @@ pub struct GpuDataRequest<'a> { handle: &'a mut GpuCacheHandle, frame_id: FrameId, start_index: usize, + max_block_count: usize, texture: &'a mut Texture, } @@ -477,7 +478,9 @@ impl<'a> GpuDataRequest<'a> { impl<'a> Drop for GpuDataRequest<'a> { fn drop(&mut self) { // Push the data to the texture pending updates list. - let block_count = self.texture.pending_blocks.len() - self.start_index; + let block_count = self.current_used_block_num(); + debug_assert!(block_count <= self.max_block_count); + let location = self.texture .push_data(Some(self.start_index), block_count, self.frame_id); self.handle.location = Some(location); @@ -491,13 +494,17 @@ pub struct GpuCache { frame_id: FrameId, /// CPU-side texture allocator. texture: Texture, + /// Number of blocks requested this frame that don't + /// need to be re-uploaded. + saved_block_count: usize, } impl GpuCache { - pub fn new() -> GpuCache { + pub fn new() -> Self { GpuCache { frame_id: FrameId::new(0), texture: Texture::new(), + saved_block_count: 0, } } @@ -506,6 +513,7 @@ impl GpuCache { debug_assert!(self.texture.pending_blocks.is_empty()); self.frame_id = self.frame_id + 1; self.texture.evict_old_blocks(self.frame_id); + self.saved_block_count = 0; } // Invalidate a (possibly) existing block in the cache. @@ -521,12 +529,17 @@ impl GpuCache { // Request a resource be added to the cache. If the resource /// is already in the cache, `None` will be returned. pub fn request<'a>(&'a mut self, handle: &'a mut GpuCacheHandle) -> Option> { + let mut max_block_count = MAX_VERTEX_TEXTURE_WIDTH; // Check if the allocation for this handle is still valid. if let Some(ref location) = handle.location { let block = &mut self.texture.blocks[location.block_index.0]; + max_block_count = self.texture.rows[block.address.v as usize].block_count_per_item; if block.epoch == location.epoch { - // Mark last access time to avoid evicting this block. - block.last_access_time = self.frame_id; + if block.last_access_time != self.frame_id { + // Mark last access time to avoid evicting this block. + block.last_access_time = self.frame_id; + self.saved_block_count += max_block_count; + } return None; } } @@ -536,6 +549,7 @@ impl GpuCache { frame_id: self.frame_id, start_index: self.texture.pending_blocks.len(), texture: &mut self.texture, + max_block_count, }) } @@ -571,10 +585,15 @@ impl GpuCache { &mut self, profile_counters: &mut GpuCacheProfileCounters, ) -> GpuCacheUpdateList { - profile_counters.allocated_rows.set(self.texture.rows.len()); + profile_counters + .allocated_rows + .set(self.texture.rows.len()); profile_counters .allocated_blocks .set(self.texture.allocated_block_count); + profile_counters + .saved_blocks + .set(self.saved_block_count); GpuCacheUpdateList { height: self.texture.height, diff --git a/gfx/webrender/src/internal_types.rs b/gfx/webrender/src/internal_types.rs index f80bde776509..a5c3a92410fc 100644 --- a/gfx/webrender/src/internal_types.rs +++ b/gfx/webrender/src/internal_types.rs @@ -190,3 +190,18 @@ pub struct UvRect { pub uv0: DevicePoint, pub uv1: DevicePoint, } + +bitflags! { + /// Each bit of the edge AA mask is: + /// 0, when the edge of the primitive needs to be considered for AA + /// 1, when the edge of the segment needs to be considered for AA + /// + /// *Note*: the bit values have to match the shader logic in + /// `write_transform_vertex()` function. + pub struct EdgeAaSegmentMask: u8 { + const LEFT = 0x1; + const TOP = 0x2; + const RIGHT = 0x4; + const BOTTOM = 0x8; + } +} diff --git a/gfx/webrender/src/lib.rs b/gfx/webrender/src/lib.rs index 9c164da6fa6a..8047f847776d 100644 --- a/gfx/webrender/src/lib.rs +++ b/gfx/webrender/src/lib.rs @@ -147,6 +147,11 @@ extern crate serde_json; extern crate time; #[cfg(feature = "debugger")] extern crate ws; +#[cfg(feature = "debugger")] +extern crate image; +#[cfg(feature = "debugger")] +extern crate base64; + pub extern crate webrender_api; #[doc(hidden)] diff --git a/gfx/webrender/src/picture.rs b/gfx/webrender/src/picture.rs index 6e75150a7381..0bd529e4beac 100644 --- a/gfx/webrender/src/picture.rs +++ b/gfx/webrender/src/picture.rs @@ -6,7 +6,7 @@ use api::{BorderRadiusKind, ColorF, ClipAndScrollInfo, FilterOp, MixBlendMode}; use api::{device_length, DeviceIntRect, DeviceIntSize, PipelineId}; use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerSize, LayerVector2D, Shadow}; use api::{ClipId, PremultipliedColorF}; -use box_shadow::BLUR_SAMPLE_SCALE; +use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey}; use frame_builder::PrimitiveContext; use gpu_cache::GpuDataRequest; use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect}; @@ -61,6 +61,7 @@ pub enum PictureKind { clip_mode: BoxShadowClipMode, radii_kind: BorderRadiusKind, content_rect: LayerRect, + cache_key: BoxShadowCacheKey, }, Image { // If a mix-blend-mode, contains the render task for @@ -153,6 +154,7 @@ impl PicturePrimitive { blur_regions: Vec, clip_mode: BoxShadowClipMode, radii_kind: BorderRadiusKind, + cache_key: BoxShadowCacheKey, pipeline_id: PipelineId, ) -> Self { PicturePrimitive { @@ -165,6 +167,7 @@ impl PicturePrimitive { clip_mode, radii_kind, content_rect: LayerRect::zero(), + cache_key, }, pipeline_id, cull_children: false, @@ -317,6 +320,7 @@ impl PicturePrimitive { ClearMode::Transparent, self.rasterization_kind, child_tasks, + None, ); let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio); @@ -331,6 +335,7 @@ impl PicturePrimitive { &[], ClearMode::Transparent, PremultipliedColorF::TRANSPARENT, + None, ); let blur_render_task_id = render_tasks.add(blur_render_task); @@ -347,6 +352,7 @@ impl PicturePrimitive { ClearMode::Transparent, self.rasterization_kind, child_tasks, + None, ); let readback_task_id = render_tasks.add(RenderTask::new_readback(*prim_screen_rect)); @@ -376,6 +382,7 @@ impl PicturePrimitive { ClearMode::Transparent, self.rasterization_kind, child_tasks, + None, ); self.render_task_id = Some(render_tasks.add(picture_task)); @@ -392,6 +399,7 @@ impl PicturePrimitive { ClearMode::Transparent, self.rasterization_kind, child_tasks, + None, ); self.render_task_id = Some(render_tasks.add(picture_task)); @@ -436,6 +444,7 @@ impl PicturePrimitive { ClearMode::Transparent, self.rasterization_kind, Vec::new(), + None, ); let picture_task_id = render_tasks.add(picture_task); @@ -448,11 +457,12 @@ impl PicturePrimitive { &[], ClearMode::Transparent, color.premultiplied(), + None, ); self.render_task_id = Some(render_tasks.add(render_task)); } - PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, content_rect, .. } => { + PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, content_rect, cache_key, .. } => { let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio); // TODO(gw): Rounding the content rect here to device pixels is not @@ -490,6 +500,7 @@ impl PicturePrimitive { ClearMode::Zero, self.rasterization_kind, Vec::new(), + Some(cache_key), ); let picture_task_id = render_tasks.add(picture_task); @@ -502,6 +513,7 @@ impl PicturePrimitive { blur_regions, blur_clear_mode, color.premultiplied(), + Some(cache_key), ); self.render_task_id = Some(render_tasks.add(render_task)); diff --git a/gfx/webrender/src/platform/macos/font.rs b/gfx/webrender/src/platform/macos/font.rs index 24718bbece0c..e896a9beea9a 100644 --- a/gfx/webrender/src/platform/macos/font.rs +++ b/gfx/webrender/src/platform/macos/font.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions}; -use api::{FontVariation, NativeFontHandle}; +use api::{FontInstanceFlags, FontVariation, NativeFontHandle}; use api::{GlyphKey, SubpixelDirection}; use app_units::Au; use core_foundation::array::{CFArray, CFArrayRef}; @@ -282,7 +282,7 @@ impl FontContext { Err(_) => return, Ok(cg_font) => cg_font, }; - self.cg_fonts.insert((*font_key).clone(), cg_font); + self.cg_fonts.insert(*font_key, cg_font); } pub fn add_native_font(&mut self, font_key: &FontKey, native_font_handle: NativeFontHandle) { @@ -291,21 +291,12 @@ impl FontContext { } self.cg_fonts - .insert((*font_key).clone(), native_font_handle.0); + .insert(*font_key, native_font_handle.0); } pub fn delete_font(&mut self, font_key: &FontKey) { if let Some(_) = self.cg_fonts.remove(font_key) { - // Unstable Rust has a retain() method on HashMap that will - // let us do this in-place. https://github.com/rust-lang/rust/issues/36648 - let ct_font_keys = self.ct_fonts - .keys() - .filter(|k| k.0 == *font_key) - .cloned() - .collect::>(); - for ct_font_key in ct_font_keys { - self.ct_fonts.remove(&ct_font_key); - } + self.ct_fonts.retain(|k, _| k.0 != *font_key); } } @@ -425,7 +416,7 @@ impl FontContext { font.subpx_dir = SubpixelDirection::None; } FontRenderMode::Alpha => { - font.color = if font.platform_options.unwrap_or_default().font_smoothing { + font.color = if font.flags.contains(FontInstanceFlags::FONT_SMOOTHING) { // Only the G channel is used to index grayscale tables, // so use R and B to preserve light/dark determination. let ColorU { g, a, .. } = font.color.luminance_color().quantized_ceil(); @@ -545,7 +536,7 @@ impl FontContext { // to both the Subpixel and the "Alpha + smoothing" modes, but not to // the "Alpha without smoothing" and Mono modes. let use_white_on_black = should_use_white_on_black(font.color); - let use_font_smoothing = font.platform_options.unwrap_or_default().font_smoothing; + let use_font_smoothing = font.flags.contains(FontInstanceFlags::FONT_SMOOTHING); let (antialias, smooth, text_color, bg_color, bg_alpha, invert) = match (font.render_mode, use_font_smoothing) { (FontRenderMode::Subpixel, _) | diff --git a/gfx/webrender/src/platform/unix/font.rs b/gfx/webrender/src/platform/unix/font.rs index 8dd2886ceeef..c000aba8bb01 100644 --- a/gfx/webrender/src/platform/unix/font.rs +++ b/gfx/webrender/src/platform/unix/font.rs @@ -4,9 +4,7 @@ use api::{ColorU, GlyphDimensions, GlyphKey, FontKey, FontRenderMode}; use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting}; -use api::{NativeFontHandle, SubpixelDirection}; -use api::{FONT_FORCE_AUTOHINT, FONT_NO_AUTOHINT, FONT_EMBEDDED_BITMAP}; -use api::{FONT_EMBOLDEN, FONT_VERTICAL_LAYOUT, FONT_SUBPIXEL_BGR}; +use api::{FontInstanceFlags, NativeFontHandle, SubpixelDirection}; use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode}; use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32}; use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos}; @@ -153,7 +151,7 @@ impl FontContext { let face = self.faces.get(&font.font_key).unwrap(); let mut load_flags = FT_LOAD_DEFAULT; - let FontInstancePlatformOptions { flags, hinting, .. } = font.platform_options.unwrap_or_default(); + let FontInstancePlatformOptions { hinting, .. } = font.platform_options.unwrap_or_default(); match (hinting, font.render_mode) { (FontHinting::None, _) => load_flags |= FT_LOAD_NO_HINTING, (FontHinting::Mono, _) => load_flags = FT_LOAD_TARGET_MONO, @@ -163,24 +161,24 @@ impl FontContext { SubpixelDirection::Vertical => FT_LOAD_TARGET_LCD_V, _ => FT_LOAD_TARGET_LCD, }; - if (flags & FONT_FORCE_AUTOHINT) != 0 { + if font.flags.contains(FontInstanceFlags::FORCE_AUTOHINT) { load_flags |= FT_LOAD_FORCE_AUTOHINT; } } _ => { - if (flags & FONT_FORCE_AUTOHINT) != 0 { + if font.flags.contains(FontInstanceFlags::FORCE_AUTOHINT) { load_flags |= FT_LOAD_FORCE_AUTOHINT; } } } - if (flags & FONT_NO_AUTOHINT) != 0 { + if font.flags.contains(FontInstanceFlags::NO_AUTOHINT) { load_flags |= FT_LOAD_NO_AUTOHINT; } - if (flags & FONT_EMBEDDED_BITMAP) == 0 { + if font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS) { load_flags |= FT_LOAD_NO_BITMAP; } - if (flags & FONT_VERTICAL_LAYOUT) != 0 { + if font.flags.contains(FontInstanceFlags::VERTICAL_LAYOUT) { load_flags |= FT_LOAD_VERTICAL_LAYOUT; } @@ -224,7 +222,7 @@ impl FontContext { let slot = unsafe { (*face.face).glyph }; assert!(slot != ptr::null_mut()); - if (flags & FONT_EMBOLDEN) != 0 { + if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) { unsafe { FT_GlyphSlot_Embolden(slot) }; } @@ -385,15 +383,9 @@ impl FontContext { let face_flags = unsafe { (*face.face).face_flags }; // If the face has embedded bitmaps, they should only be used if either // embedded bitmaps are explicitly requested or if the face has no outline. - if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 { - let FontInstancePlatformOptions { flags, .. } = font.platform_options.unwrap_or_default(); - if (flags & FONT_EMBEDDED_BITMAP) != 0 { - return true; - } - (face_flags & (FT_FACE_FLAG_SCALABLE as FT_Long)) == 0 - } else { - false - } + (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 && + (font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS) || + (face_flags & (FT_FACE_FLAG_SCALABLE as FT_Long)) == 0) } fn choose_bitmap_size(&self, face: FT_Face, requested_size: f64) -> FT_Error { @@ -451,7 +443,7 @@ impl FontContext { dy - ((cbox.yMin + dy) & !63), ); - if font.synthetic_italics { + if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) { FT_GlyphSlot_Oblique(slot); } } @@ -555,8 +547,7 @@ impl FontContext { // Extract the final glyph from FT format into RGBA8 format, which is // what WR expects. - let FontInstancePlatformOptions { flags, .. } = font.platform_options.unwrap_or_default(); - let subpixel_bgr = (flags & FONT_SUBPIXEL_BGR) != 0; + let subpixel_bgr = font.flags.contains(FontInstanceFlags::SUBPIXEL_BGR); let mut src_row = bitmap.buffer; let mut dest: usize = 0; while dest < final_buffer.len() { diff --git a/gfx/webrender/src/platform/windows/font.rs b/gfx/webrender/src/platform/windows/font.rs index 7df7eb8ce981..dbce8962a55e 100644 --- a/gfx/webrender/src/platform/windows/font.rs +++ b/gfx/webrender/src/platform/windows/font.rs @@ -2,12 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{FontInstancePlatformOptions, FontKey, FontRenderMode}; +use api::{FontInstanceFlags, FontKey, FontRenderMode}; use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection}; use dwrote; use gamma_lut::{ColorLut, GammaLut}; use glyph_rasterizer::{FontInstance, RasterizedGlyph}; use internal_types::FastHashMap; +use std::collections::hash_map::Entry; use std::sync::Arc; lazy_static! { @@ -21,6 +22,7 @@ lazy_static! { pub struct FontContext { fonts: FastHashMap, + simulations: FastHashMap<(FontKey, dwrote::DWRITE_FONT_SIMULATIONS), dwrote::FontFace>, gamma_lut: GammaLut, gdi_gamma_lut: GammaLut, } @@ -39,15 +41,12 @@ fn dwrite_texture_type(render_mode: FontRenderMode) -> dwrote::DWRITE_TEXTURE_TY } fn dwrite_measure_mode( - render_mode: FontRenderMode, - options: Option, + font: &FontInstance, ) -> dwrote::DWRITE_MEASURING_MODE { - let FontInstancePlatformOptions { force_gdi_rendering, .. } = - options.unwrap_or_default(); - if force_gdi_rendering { + if font.flags.contains(FontInstanceFlags::FORCE_GDI) { dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC } else { - match render_mode { + match font.render_mode { FontRenderMode::Mono | FontRenderMode::Bitmap => dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC, FontRenderMode::Alpha | FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_NATURAL, } @@ -56,18 +55,15 @@ fn dwrite_measure_mode( fn dwrite_render_mode( font_face: &dwrote::FontFace, - render_mode: FontRenderMode, + font: &FontInstance, em_size: f32, measure_mode: dwrote::DWRITE_MEASURING_MODE, - options: Option, ) -> dwrote::DWRITE_RENDERING_MODE { - let dwrite_render_mode = match render_mode { + let dwrite_render_mode = match font.render_mode { FontRenderMode::Bitmap => dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC, FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED, FontRenderMode::Alpha | FontRenderMode::Subpixel => { - let FontInstancePlatformOptions { force_gdi_rendering, .. } = - options.unwrap_or_default(); - if force_gdi_rendering { + if font.flags.contains(FontInstanceFlags::FORCE_GDI) { dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC } else { font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode) @@ -93,6 +89,7 @@ impl FontContext { let gdi_gamma = 2.3; FontContext { fonts: FastHashMap::default(), + simulations: FastHashMap::default(), gamma_lut: GammaLut::new(contrast, gamma, gamma), gdi_gamma_lut: GammaLut::new(contrast, gdi_gamma, gdi_gamma), } @@ -109,7 +106,7 @@ impl FontContext { if let Some(font_file) = dwrote::FontFile::new_from_data(&**data) { let face = font_file.create_face(index, dwrote::DWRITE_FONT_SIMULATIONS_NONE); - self.fonts.insert((*font_key).clone(), face); + self.fonts.insert(*font_key, face); } else { // XXX add_raw_font needs to have a way to return an error debug!("DWrite WR failed to load font from data, using Arial instead"); @@ -125,11 +122,13 @@ impl FontContext { let system_fc = dwrote::FontCollection::system(); let font = system_fc.get_font_from_descriptor(&font_handle).unwrap(); let face = font.create_font_face(); - self.fonts.insert((*font_key).clone(), face); + self.fonts.insert(*font_key, face); } pub fn delete_font(&mut self, font_key: &FontKey) { - self.fonts.remove(font_key); + if let Some(_) = self.fonts.remove(font_key) { + self.simulations.retain(|k, _| k.0 != *font_key); + } } // Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite @@ -150,12 +149,35 @@ impl FontContext { } } + fn get_font_face( + &mut self, + font: &FontInstance, + ) -> &dwrote::FontFace { + if !font.flags.intersects(FontInstanceFlags::SYNTHETIC_BOLD | FontInstanceFlags::SYNTHETIC_ITALICS) { + return self.fonts.get(&font.font_key).unwrap(); + } + let mut sims = dwrote::DWRITE_FONT_SIMULATIONS_NONE; + if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) { + sims = sims | dwrote::DWRITE_FONT_SIMULATIONS_BOLD; + } + if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) { + sims = sims | dwrote::DWRITE_FONT_SIMULATIONS_OBLIQUE; + } + match self.simulations.entry((font.font_key, sims)) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let normal_face = self.fonts.get(&font.font_key).unwrap(); + entry.insert(normal_face.create_font_face_with_simulations(sims)) + } + } + } + fn create_glyph_analysis( - &self, + &mut self, font: &FontInstance, key: &GlyphKey, ) -> dwrote::GlyphRunAnalysis { - let face = self.fonts.get(&font.font_key).unwrap(); + let face = self.get_font_face(font); let glyph = key.index as u16; let advance = 0.0f32; let offset = dwrote::GlyphOffset { @@ -177,13 +199,12 @@ impl FontContext { bidiLevel: 0, }; - let dwrite_measure_mode = dwrite_measure_mode(font.render_mode, font.platform_options); + let dwrite_measure_mode = dwrite_measure_mode(font); let dwrite_render_mode = dwrite_render_mode( face, - font.render_mode, + font, size, dwrite_measure_mode, - font.platform_options, ); let (x_offset, y_offset) = font.get_subpx_offset(key); @@ -216,7 +237,7 @@ impl FontContext { // TODO: Pipe GlyphOptions into glyph_dimensions too pub fn get_glyph_dimensions( - &self, + &mut self, font: &FontInstance, key: &GlyphKey, ) -> Option { @@ -237,7 +258,7 @@ impl FontContext { return None; } - let face = self.fonts.get(&font.font_key).unwrap(); + let face = self.get_font_face(font); face.get_design_glyph_metrics(&[key.index as u16], false) .first() .map(|metrics| { @@ -301,7 +322,7 @@ impl FontContext { // If bitmaps are requested, then treat as a bitmap font to disable transforms. // If mono AA is requested, let that take priority over using bitmaps. font.render_mode != FontRenderMode::Mono && - font.platform_options.unwrap_or_default().use_embedded_bitmap + font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS) } pub fn prepare_font(font: &mut FontInstance) { @@ -345,9 +366,7 @@ impl FontContext { let lut_correction = match font.render_mode { FontRenderMode::Mono | FontRenderMode::Bitmap => &self.gdi_gamma_lut, FontRenderMode::Alpha | FontRenderMode::Subpixel => { - let FontInstancePlatformOptions { force_gdi_rendering, .. } = - font.platform_options.unwrap_or_default(); - if force_gdi_rendering { + if font.flags.contains(FontInstanceFlags::FORCE_GDI) { &self.gdi_gamma_lut } else { &self.gamma_lut diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index 0be4a00f3050..79e0afcfd0d3 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -6,14 +6,14 @@ use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRe use api::{DevicePoint, ExtendMode, FontRenderMode, GlyphInstance, GlyphKey}; use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect}; use api::{ClipMode, LayerSize, LayerVector2D, LayerToWorldTransform, LineOrientation, LineStyle}; -use api::{ClipAndScrollInfo, EdgeAaSegmentMask, PremultipliedColorF, TileOffset}; +use api::{ClipAndScrollInfo, PremultipliedColorF, TileOffset}; use api::{ClipId, LayerTransform, PipelineId, YuvColorSpace, YuvFormat}; use border::BorderCornerInstance; use clip_scroll_tree::{CoordinateSystemId, ClipScrollTree}; use clip::{ClipSourcesHandle, ClipStore}; use frame_builder::PrimitiveContext; use glyph_rasterizer::{FontInstance, FontTransform}; -use internal_types::FastHashMap; +use internal_types::{EdgeAaSegmentMask, FastHashMap}; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; use picture::{PictureKind, PicturePrimitive, RasterizationSpace}; diff --git a/gfx/webrender/src/profiler.rs b/gfx/webrender/src/profiler.rs index 737e088617fd..1cd9b3d364bd 100644 --- a/gfx/webrender/src/profiler.rs +++ b/gfx/webrender/src/profiler.rs @@ -8,6 +8,7 @@ use euclid::{Point2D, Rect, Size2D, vec2}; use query::{GpuSampler, GpuTimer, NamedTag}; use std::collections::vec_deque::VecDeque; use internal_types::FastHashMap; +use renderer::MAX_VERTEX_TEXTURE_WIDTH; use std::{f32, mem}; use time::precise_time_ns; @@ -350,13 +351,19 @@ impl TextureCacheProfileCounters { pub struct GpuCacheProfileCounters { pub allocated_rows: IntProfileCounter, pub allocated_blocks: IntProfileCounter, + pub updated_rows: IntProfileCounter, + pub updated_blocks: IntProfileCounter, + pub saved_blocks: IntProfileCounter, } impl GpuCacheProfileCounters { pub fn new() -> Self { GpuCacheProfileCounters { - allocated_rows: IntProfileCounter::new("GPU cache rows"), - allocated_blocks: IntProfileCounter::new("GPU cache blocks"), + allocated_rows: IntProfileCounter::new("GPU cache rows: total"), + updated_rows: IntProfileCounter::new("GPU cache rows: updated"), + allocated_blocks: IntProfileCounter::new("GPU cache blocks: total"), + updated_blocks: IntProfileCounter::new("GPU cache blocks: updated"), + saved_blocks: IntProfileCounter::new("GPU cache blocks: saved"), } } } @@ -825,6 +832,99 @@ impl Profiler { } } + fn draw_gpu_cache_bar( + &mut self, + label: &str, + label_color: ColorU, + counters: &[(ColorU, &IntProfileCounter)], + debug_renderer: &mut DebugRenderer, + ) -> Rect { + let mut rect = debug_renderer.add_text( + self.x_left, + self.y_left, + label, + label_color, + ); + + let x_base = rect.origin.x + rect.size.width + 10.0; + let height = debug_renderer.line_height(); + let width = (self.x_right - 30.0 - x_base).max(0.0); + let total_value = counters.last().unwrap().1.value; + let scale = width / total_value as f32; + let mut x_current = x_base; + + for &(color, counter) in counters { + let x_stop = x_base + counter.value as f32 * scale; + debug_renderer.add_quad( + x_current, + rect.origin.y, + x_stop, + rect.origin.y + height, + color, + color, + ); + x_current = x_stop; + } + + self.y_left += height; + + rect.size.width += width + 10.0; + rect + } + + fn draw_gpu_cache_bars( + &mut self, + counters: &GpuCacheProfileCounters, + debug_renderer: &mut DebugRenderer, + ) { + let color_updated = ColorU::new(0xFF, 0, 0, 0xFF); + let color_free = ColorU::new(0, 0, 0xFF, 0xFF); + let color_saved = ColorU::new(0, 0xFF, 0, 0xFF); + + let requested_blocks = IntProfileCounter { + description: "", + value: counters.updated_blocks.value + counters.saved_blocks.value, + }; + let total_blocks = IntProfileCounter { + description: "", + value: counters.allocated_rows.value * MAX_VERTEX_TEXTURE_WIDTH, + }; + + let rect0 = self.draw_gpu_cache_bar( + &format!("GPU cache rows ({}):", counters.allocated_rows.value), + ColorU::new(255, 255, 255, 255), + &[ + (color_updated, &counters.updated_rows), + (color_free, &counters.allocated_rows), + ], + debug_renderer, + ); + + let rect1 = self.draw_gpu_cache_bar( + "GPU cache blocks", + ColorU::new(255, 255, 0, 255), + &[ + (color_updated, &counters.updated_blocks), + (color_saved, &requested_blocks), + (color_free, &counters.allocated_blocks), + (ColorU::new(0, 0, 0, 255), &total_blocks), + ], + debug_renderer, + ); + + let total_rect = rect0.union(&rect1).inflate(10.0, 10.0); + debug_renderer.add_quad( + total_rect.origin.x, + total_rect.origin.y, + total_rect.origin.x + total_rect.size.width, + total_rect.origin.y + total_rect.size.height, + ColorF::new(0.1, 0.1, 0.1, 0.8).into(), + ColorF::new(0.2, 0.2, 0.2, 0.8).into(), + ); + + self.y_left = total_rect.origin.y + total_rect.size.height + 30.0; + } + pub fn draw_profile( &mut self, frame_profiles: &[FrameProfileCounters], @@ -848,15 +948,11 @@ impl Profiler { renderer_timers.gpu_time.set(gpu_time); self.draw_counters(&[&renderer_profile.frame_time], debug_renderer, true); + self.draw_counters(&[&renderer_profile.frame_counter], debug_renderer, true); - self.draw_counters( - &[ - &renderer_profile.frame_counter, - &backend_profile.resources.gpu_cache.allocated_rows, - &backend_profile.resources.gpu_cache.allocated_blocks, - ], + self.draw_gpu_cache_bars( + &backend_profile.resources.gpu_cache, debug_renderer, - true, ); self.draw_counters( diff --git a/gfx/webrender/src/query.rs b/gfx/webrender/src/query.rs index 2fb6af33cc8c..999abc749115 100644 --- a/gfx/webrender/src/query.rs +++ b/gfx/webrender/src/query.rs @@ -269,31 +269,23 @@ impl GpuProfiler { #[must_use] pub struct GpuMarker { - gl: Option>, + gl: Rc, } impl GpuMarker { fn new(gl: &Rc, message: &str) -> Self { - if gl.get_type() == gl::GlType::Gl { - gl.push_group_marker_ext(message); - GpuMarker { gl: Some(Rc::clone(gl)) } - } else { - GpuMarker { gl: None } - } + gl.push_group_marker_ext(message); + GpuMarker { gl: Rc::clone(gl) } } fn fire(gl: &Rc, message: &str) { - if gl.get_type() == gl::GlType::Gl { - gl.insert_event_marker_ext(message); - } + gl.insert_event_marker_ext(message); } } impl Drop for GpuMarker { fn drop(&mut self) { - if let Some(ref gl) = self.gl { - gl.pop_group_marker_ext(); - } + self.gl.pop_group_marker_ext(); } } diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index 721b411bbfab..b4b8ec358429 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -648,7 +648,7 @@ impl RenderBackend { let mut debug_doc = debug_server::TreeNode::new("document"); for (_, pipeline) in &doc.scene.pipelines { - let mut debug_dl = debug_server::TreeNode::new("display_list"); + let mut debug_dl = debug_server::TreeNode::new("display-list"); self.traverse_items(&mut pipeline.display_list.iter(), &mut debug_dl); debug_doc.add_child(debug_dl); } @@ -669,7 +669,7 @@ impl RenderBackend { let mut debug_root = debug_server::ClipScrollTreeList::new(); for (_, doc) in &self.documents { - let debug_node = debug_server::TreeNode::new("document clip_scroll tree"); + let debug_node = debug_server::TreeNode::new("document clip-scroll tree"); let mut builder = debug_server::TreeNodeBuilder::new(debug_node); // TODO(gw): Restructure the storage of clip-scroll tree, clip store diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs index 1a31d9b4ad74..760c2df3aeb4 100644 --- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -2,13 +2,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; +use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixel}; use api::{LayerPoint, LayerRect, PremultipliedColorF}; +use box_shadow::BoxShadowCacheKey; use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore}; use clip_scroll_tree::CoordinateSystemId; +use euclid::TypedSize2D; use gpu_types::{ClipScrollNodeIndex}; use picture::RasterizationSpace; use prim_store::{PrimitiveIndex}; +#[cfg(feature = "debugger")] +use print_tree::{PrintTreePrinter}; use std::{cmp, ops, usize, f32, i32}; use std::rc::Rc; use tiling::{RenderPass, RenderTargetIndex}; @@ -149,6 +153,9 @@ impl ops::IndexMut for RenderTaskTree { pub enum RenderTaskKey { /// Draw the alpha mask for a shared clip. CacheMask(ClipId), + CacheScaling(BoxShadowCacheKey, TypedSize2D), + CacheBlur(BoxShadowCacheKey, i32), + CachePicture(BoxShadowCacheKey), } #[derive(Debug)] @@ -250,6 +257,18 @@ pub struct BlurTask { pub scale_factor: f32, } +impl BlurTask { + #[cfg(feature = "debugger")] + fn print_with(&self, pt: &mut T) { + pt.add_item(format!("std deviation: {}", self.blur_std_deviation)); + pt.add_item(format!("target: {:?}", self.target_kind)); + pt.add_item(format!("scale: {}", self.scale_factor)); + for region in &self.regions { + pt.add_item(format!("region {:?}", region)); + } + } +} + #[derive(Debug)] pub struct RenderTaskData { pub data: [f32; FLOATS_PER_RENDER_TASK_INFO], @@ -266,7 +285,7 @@ pub enum RenderTaskKind { Scaling(RenderTargetKind), } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum ClearMode { // Applicable to color and alpha targets. Zero, @@ -296,6 +315,7 @@ impl RenderTask { clear_mode: ClearMode, rasterization_kind: RasterizationSpace, children: Vec, + box_shadow_cache_key: Option, ) -> Self { let location = match size { Some(size) => RenderTaskLocation::Dynamic(None, size), @@ -303,7 +323,10 @@ impl RenderTask { }; RenderTask { - cache_key: None, + cache_key: match box_shadow_cache_key { + Some(key) => Some(RenderTaskKey::CachePicture(key)), + None => None, + }, children, location, kind: RenderTaskKind::Picture(PictureTask { @@ -390,6 +413,7 @@ impl RenderTask { regions: &[LayerRect], clear_mode: ClearMode, color: PremultipliedColorF, + box_shadow_cache_key: Option, ) -> Self { // Adjust large std deviation value. let mut adjusted_blur_std_deviation = blur_std_deviation; @@ -408,14 +432,18 @@ impl RenderTask { let downscaling_task = RenderTask::new_scaling( target_kind, downscaling_src_task_id, - adjusted_blur_target_size + adjusted_blur_target_size, + box_shadow_cache_key, ); downscaling_src_task_id = render_tasks.add(downscaling_task); } scale_factor = blur_target_size.width as f32 / adjusted_blur_target_size.width as f32; let blur_task_v = RenderTask { - cache_key: None, + cache_key: match box_shadow_cache_key { + Some(key) => Some(RenderTaskKey::CacheBlur(key, 0)), + None => None, + }, children: vec![downscaling_src_task_id], location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size), kind: RenderTaskKind::VerticalBlur(BlurTask { @@ -431,7 +459,10 @@ impl RenderTask { let blur_task_v_id = render_tasks.add(blur_task_v); let blur_task_h = RenderTask { - cache_key: None, + cache_key: match box_shadow_cache_key { + Some(key) => Some(RenderTaskKey::CacheBlur(key, 1)), + None => None, + }, children: vec![blur_task_v_id], location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size), kind: RenderTaskKind::HorizontalBlur(BlurTask { @@ -451,9 +482,13 @@ impl RenderTask { target_kind: RenderTargetKind, src_task_id: RenderTaskId, target_size: DeviceIntSize, + box_shadow_cache_key: Option, ) -> Self { RenderTask { - cache_key: None, + cache_key: match box_shadow_cache_key { + Some(key) => Some(RenderTaskKey::CacheScaling(key, target_size)), + None => None, + }, children: vec![src_task_id], location: RenderTaskLocation::Dynamic(None, target_size), kind: RenderTaskKind::Scaling(target_kind), @@ -626,4 +661,51 @@ impl RenderTask { } } } + + #[cfg(feature = "debugger")] + pub fn print_with(&self, pt: &mut T, tree: &RenderTaskTree) -> bool { + match self.kind { + RenderTaskKind::Picture(ref task) => { + pt.new_level(format!("Picture of {:?}", task.prim_index)); + pt.add_item(format!("kind: {:?}", task.target_kind)); + pt.add_item(format!("space: {:?}", task.rasterization_kind)); + } + RenderTaskKind::CacheMask(ref task) => { + pt.new_level(format!("CacheMask with {} clips", task.clips.len())); + pt.add_item(format!("rect: {:?}", task.actual_rect)); + pt.add_item(format!("geometry: {:?}", task.geometry_kind)); + } + RenderTaskKind::VerticalBlur(ref task) => { + pt.new_level("VerticalBlur".to_owned()); + task.print_with(pt); + } + RenderTaskKind::HorizontalBlur(ref task) => { + pt.new_level("HorizontalBlur".to_owned()); + task.print_with(pt); + } + RenderTaskKind::Readback(ref rect) => { + pt.new_level("Readback".to_owned()); + pt.add_item(format!("rect: {:?}", rect)); + } + RenderTaskKind::Scaling(ref kind) => { + pt.new_level("Scaling".to_owned()); + pt.add_item(format!("kind: {:?}", kind)); + } + RenderTaskKind::Alias(ref alias_id) => { + pt.add_item(format!("Alias of {:?}", alias_id)); + return false + } + } + + pt.add_item(format!("clear to: {:?}", self.clear_mode)); + + for &child_id in &self.children { + if tree[child_id].print_with(pt, tree) { + pt.add_item(format!("self: {:?}", child_id)) + } + } + + pt.end_level(); + true + } } diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index 8f74206bf33d..27f24d5fc02d 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -12,7 +12,7 @@ use api::{channel, BlobImageRenderer, FontRenderMode}; use api::{ColorF, DocumentId, Epoch, PipelineId, RenderApiSender, RenderNotifier}; use api::{DevicePixel, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; -use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize}; +use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, ColorU}; use api::{ExternalImageId, ExternalImageType, ImageFormat}; use api::{YUV_COLOR_SPACES, YUV_FORMATS}; use api::{YuvColorSpace, YuvFormat}; @@ -64,7 +64,8 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::thread; use texture_cache::TextureCache; use thread_profiler::{register_thread_with_profiler, write_profile}; -use tiling::{AlphaRenderTarget, ColorRenderTarget, RenderPassKind, RenderTargetKind, RenderTargetList}; +use tiling::{AlphaRenderTarget, ColorRenderTarget}; +use tiling::{RenderPass, RenderPassKind, RenderTargetKind, RenderTargetList}; use tiling::{BatchKey, BatchKind, BrushBatchKind, Frame, RenderTarget, ScalingInfo, TransformBatchKind}; use time::precise_time_ns; use util::TransformedRectKind; @@ -246,6 +247,7 @@ bitflags! { const GPU_TIME_QUERIES = 1 << 4; const GPU_SAMPLE_QUERIES= 1 << 5; const DISABLE_BATCHING = 1 << 6; + const EPOCHS = 1 << 7; } } @@ -706,7 +708,7 @@ struct CacheTexture { } impl CacheTexture { - fn new(device: &mut Device) -> CacheTexture { + fn new(device: &mut Device) -> Self { let texture = device.create_texture(TextureTarget::Default); let pbo = device.create_pbo(); @@ -723,7 +725,7 @@ impl CacheTexture { device.delete_texture(self.texture); } - fn apply_patch(&mut self, update: &GpuCacheUpdate, blocks: &[GpuBlockData]) { + fn apply_patch(&mut self, update: &GpuCacheUpdate, blocks: &[GpuBlockData]) -> usize { match update { &GpuCacheUpdate::Copy { block_index, @@ -751,11 +753,13 @@ impl CacheTexture { for i in 0 .. block_count { data[i] = blocks[block_index + i]; } + + block_count } } } - fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) { + fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) -> usize { // See if we need to create or resize the texture. let current_dimensions = self.texture.get_dimensions(); if updates.height > current_dimensions.height { @@ -783,49 +787,58 @@ impl CacheTexture { } } + let mut updated_blocks = 0; for update in &updates.updates { - self.apply_patch(update, &updates.blocks); + updated_blocks += self.apply_patch(update, &updates.blocks); } + updated_blocks } - fn flush(&mut self, device: &mut Device) { + fn flush(&mut self, device: &mut Device) -> usize { // Bind a PBO to do the texture upload. // Updating the texture via PBO avoids CPU-side driver stalls. device.bind_pbo(Some(&self.pbo)); + let mut rows_dirty = 0; + for (row_index, row) in self.rows.iter_mut().enumerate() { - if row.is_dirty { - // Get the data for this row and push to the PBO. - let block_index = row_index * MAX_VERTEX_TEXTURE_WIDTH; - let cpu_blocks = - &self.cpu_blocks[block_index .. (block_index + MAX_VERTEX_TEXTURE_WIDTH)]; - device.update_pbo_data(cpu_blocks); - - // Insert a command to copy the PBO data to the right place in - // the GPU-side cache texture. - device.update_texture_from_pbo( - &self.texture, - 0, - row_index as u32, - MAX_VERTEX_TEXTURE_WIDTH as u32, - 1, - 0, - None, - 0, - ); - - // Orphan the PBO. This is the recommended way to hint to the - // driver to detach the underlying storage from this PBO id. - // Keeping the size the same gives the driver a hint for future - // use of this PBO. - device.orphan_pbo(mem::size_of::() * MAX_VERTEX_TEXTURE_WIDTH); - - row.is_dirty = false; + if !row.is_dirty { + continue; } + + // Get the data for this row and push to the PBO. + let block_index = row_index * MAX_VERTEX_TEXTURE_WIDTH; + let cpu_blocks = + &self.cpu_blocks[block_index .. (block_index + MAX_VERTEX_TEXTURE_WIDTH)]; + device.update_pbo_data(cpu_blocks); + + // Insert a command to copy the PBO data to the right place in + // the GPU-side cache texture. + device.update_texture_from_pbo( + &self.texture, + 0, + row_index as u32, + MAX_VERTEX_TEXTURE_WIDTH as u32, + 1, + 0, + None, + 0, + ); + + // Orphan the PBO. This is the recommended way to hint to the + // driver to detach the underlying storage from this PBO id. + // Keeping the size the same gives the driver a hint for future + // use of this PBO. + device.orphan_pbo(mem::size_of::() * MAX_VERTEX_TEXTURE_WIDTH); + + rows_dirty += 1; + row.is_dirty = false; } // Ensure that other texture updates won't read from this PBO. device.bind_pbo(None); + + rows_dirty } } @@ -2154,6 +2167,22 @@ impl Renderer { } } + #[cfg(not(feature = "debugger"))] + fn get_screenshot_for_debugger(&mut self) -> String { + // Avoid unused param warning. + let _ = &self.debug_server; + String::new() + } + + + #[cfg(feature = "debugger")] + fn get_screenshot_for_debugger(&mut self) -> String { + let data = self.device.read_pixels(1024, 768); + let screenshot = debug_server::Screenshot::new(1024, 768, data); + + serde_json::to_string(&screenshot).unwrap() + } + #[cfg(not(feature = "debugger"))] fn get_passes_for_debugger(&self) -> String { // Avoid unused param warning. @@ -2284,6 +2313,31 @@ impl Renderer { serde_json::to_string(&debug_passes).unwrap() } + #[cfg(not(feature = "debugger"))] + fn get_render_tasks_for_debugger(&self) -> String { + String::new() + } + + #[cfg(feature = "debugger")] + fn get_render_tasks_for_debugger(&self) -> String { + let mut debug_root = debug_server::RenderTaskList::new(); + + for &(_, ref render_doc) in &self.active_documents { + let debug_node = debug_server::TreeNode::new("document render tasks"); + let mut builder = debug_server::TreeNodeBuilder::new(debug_node); + + let render_tasks = &render_doc.frame.render_tasks; + match render_tasks.tasks.last() { + Some(main_task) => main_task.print_with(&mut builder, render_tasks), + None => continue, + }; + + debug_root.add(builder.build()); + } + + serde_json::to_string(&debug_root).unwrap() + } + fn handle_debug_command(&mut self, command: DebugCommand) { match command { DebugCommand::EnableProfiler(enable) => { @@ -2304,12 +2358,20 @@ impl Renderer { DebugCommand::EnableGpuSampleQueries(enable) => { self.set_debug_flag(DebugFlags::GPU_SAMPLE_QUERIES, enable); } - DebugCommand::FetchDocuments => {} + DebugCommand::FetchDocuments | DebugCommand::FetchClipScrollTree => {} + DebugCommand::FetchRenderTasks => { + let json = self.get_render_tasks_for_debugger(); + self.debug_server.send(json); + } DebugCommand::FetchPasses => { let json = self.get_passes_for_debugger(); self.debug_server.send(json); } + DebugCommand::FetchScreenshot => { + let json = self.get_screenshot_for_debugger(); + self.debug_server.send(json); + } } } @@ -2330,6 +2392,32 @@ impl Renderer { (cpu_profiles, gpu_profiles) } + /// Returns `true` if the active rendered documents (that need depth buffer) + /// intersect on the main framebuffer, in which case we don't clear + /// the whole depth and instead clear each document area separately. + fn are_documents_intersecting_depth(&self) -> bool { + let document_rects = self.active_documents + .iter() + .filter_map(|&(_, ref render_doc)| { + match render_doc.frame.passes.last() { + Some(&RenderPass { kind: RenderPassKind::MainFramebuffer(ref target), .. }) + if target.needs_depth() => Some(render_doc.frame.inner_rect), + _ => None, + } + }) + .collect::>(); + + for (i, rect) in document_rects.iter().enumerate() { + for other in &document_rects[i+1 ..] { + if rect.intersects(other) { + return true + } + } + } + + false + } + /// Renders the current frame. /// /// A Frame is supplied by calling [`generate_frame()`][genframe]. @@ -2388,23 +2476,36 @@ impl Renderer { }); profile_timers.cpu_time.profile(|| { + let clear_depth_value = if self.are_documents_intersecting_depth() { + None + } else { + Some(1.0) + }; + //Note: another borrowck dance let mut active_documents = mem::replace(&mut self.active_documents, Vec::default()); // sort by the document layer id active_documents.sort_by_key(|&(_, ref render_doc)| render_doc.frame.layer); - let needs_clear = !active_documents + // don't clear the framebuffer if one of the rendered documents will overwrite it + let needs_color_clear = !active_documents .iter() .any(|&(_, RenderedDocument { ref frame, .. })| { frame.background_color.is_some() && frame.inner_rect.origin == DeviceUintPoint::zero() && frame.inner_rect.size == framebuffer_size }); - // don't clear the framebuffer if one of the rendered documents will overwrite it - if needs_clear { - let clear_color = self.clear_color.map(|color| color.to_array()); + + if needs_color_clear || clear_depth_value.is_some() { + let clear_color = if needs_color_clear { + self.clear_color.map(|color| color.to_array()) + } else { + None + }; self.device.bind_draw_target(None, None); - self.device.clear_target(clear_color, None); + self.device.enable_depth_write(); + self.device.clear_target(clear_color, clear_depth_value, None); + self.device.disable_depth_write(); } // Re-use whatever targets possible from the pool, before @@ -2419,6 +2520,7 @@ impl Renderer { self.draw_tile_frame( frame, framebuffer_size, + clear_depth_value.is_some(), cpu_frame_id, &mut stats ); @@ -2489,12 +2591,18 @@ impl Renderer { fn update_gpu_cache(&mut self, frame: &Frame) { let _gm = self.gpu_profile.start_marker("gpu cache update"); + let mut updated_blocks = 0; for update_list in self.pending_gpu_cache_updates.drain(..) { - self.gpu_cache_texture + updated_blocks += self.gpu_cache_texture .update(&mut self.device, &update_list); } self.update_deferred_resolves(frame); - self.gpu_cache_texture.flush(&mut self.device); + + let updated_rows = self.gpu_cache_texture.flush(&mut self.device); + + let counters = &mut self.backend_profile_counters.resources.gpu_cache; + counters.updated_rows.set(updated_rows); + counters.updated_blocks.set(updated_blocks); } fn update_texture_cache(&mut self) { @@ -2913,6 +3021,7 @@ impl Renderer { target: &ColorRenderTarget, framebuffer_target_rect: DeviceUintRect, target_size: DeviceUintSize, + depth_is_ready: bool, clear_color: Option<[f32; 4]>, render_tasks: &RenderTaskTree, projection: &Transform3D, @@ -2933,38 +3042,37 @@ impl Renderer { self.device.disable_depth(); self.device.set_blend(false); - let depth_clear = if target.needs_depth() { + let depth_clear = if !depth_is_ready && target.needs_depth() { self.device.enable_depth_write(); Some(1.0) } else { None }; - if render_target.is_some() { + let clear_rect = if render_target.is_some() { if self.enable_clear_scissor { // TODO(gw): Applying a scissor rect and minimal clear here // is a very large performance win on the Intel and nVidia // GPUs that I have tested with. It's possible it may be a // performance penalty on other GPU types - we should test this // and consider different code paths. - self.device.clear_target_rect(clear_color, depth_clear, target.used_rect()); + Some(target.used_rect()) } else { - self.device.clear_target(clear_color, depth_clear); + None } } else if framebuffer_target_rect == DeviceUintRect::new(DeviceUintPoint::zero(), target_size) { // whole screen is covered, no need for scissor - self.device.clear_target(clear_color, depth_clear); + None } else { - // Note: for non-intersecting document rectangles, - // we can omit clearing the depth here, and instead - // just clear it for the whole framebuffer at start of the frame. - let mut clear_rect = framebuffer_target_rect.to_i32(); + let mut rect = framebuffer_target_rect.to_i32(); // Note: `framebuffer_target_rect` needs a Y-flip before going to GL // Note: at this point, the target rectangle is not guaranteed to be within the main framebuffer bounds // but `clear_target_rect` is totally fine with negative origin, as long as width & height are positive - clear_rect.origin.y = target_size.height as i32 - clear_rect.origin.y - clear_rect.size.height; - self.device.clear_target_rect(clear_color, depth_clear, clear_rect); - } + rect.origin.y = target_size.height as i32 - rect.origin.y - rect.size.height; + Some(rect) + }; + + self.device.clear_target(clear_color, depth_clear, clear_rect); if depth_clear.is_some() { self.device.disable_depth_write(); @@ -3357,14 +3465,20 @@ impl Renderer { // performance penalty on other GPU types - we should test this // and consider different code paths. let clear_color = [1.0, 1.0, 1.0, 0.0]; - self.device - .clear_target_rect(Some(clear_color), None, target.used_rect()); + self.device.clear_target( + Some(clear_color), + None, + Some(target.used_rect()), + ); let zero_color = [0.0, 0.0, 0.0, 0.0]; for &task_id in &target.zero_clears { let (rect, _) = render_tasks[task_id].get_target_rect(); - self.device - .clear_target_rect(Some(zero_color), None, rect); + self.device.clear_target( + Some(zero_color), + None, + Some(rect), + ); } } @@ -3693,6 +3807,7 @@ impl Renderer { &mut self, frame: &mut Frame, framebuffer_size: DeviceUintSize, + framebuffer_depth_is_ready: bool, frame_id: FrameId, stats: &mut RendererStats, ) { @@ -3742,6 +3857,7 @@ impl Renderer { target, frame.inner_rect, framebuffer_size, + framebuffer_depth_is_ready, clear_color, &frame.render_tasks, &projection, @@ -3794,6 +3910,7 @@ impl Renderer { target, frame.inner_rect, color.max_size, + false, Some([0.0, 0.0, 0.0, 0.0]), &frame.render_tasks, &projection, @@ -3827,6 +3944,7 @@ impl Renderer { self.texture_resolver.end_frame(&mut self.render_target_pool); self.draw_render_target_debug(framebuffer_size); self.draw_texture_cache_debug(framebuffer_size); + self.draw_epoch_debug(); // Garbage collect any frame outputs that weren't used this frame. let device = &mut self.device; @@ -3971,6 +4089,37 @@ impl Renderer { } } + fn draw_epoch_debug(&mut self) { + if !self.debug_flags.contains(DebugFlags::EPOCHS) { + return; + } + + let dy = self.debug.line_height(); + let x0: f32 = 30.0; + let y0: f32 = 30.0; + let mut y = y0; + let mut text_width = 0.0; + for (pipeline, epoch) in &self.pipeline_epoch_map { + y += dy; + let w = self.debug.add_text( + x0, y, + &format!("{:?}: {:?}", pipeline, epoch), + ColorU::new(255, 255, 0, 255), + ).size.width; + text_width = f32::max(text_width, w); + } + + let margin = 10.0; + self.debug.add_quad( + &x0 - margin, + y0 - margin, + x0 + text_width + margin, + y + margin, + ColorU::new(25, 25, 25, 200), + ColorU::new(51, 51, 51, 200), + ); + } + pub fn read_pixels_rgba8(&self, rect: DeviceUintRect) -> Vec { let mut pixels = vec![0u8; (4 * rect.size.width * rect.size.height) as usize]; self.read_pixels_into(rect, ReadPixelsFormat::Rgba8, &mut pixels); diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs index d0ccfda1c86c..36d0a8cc8300 100644 --- a/gfx/webrender/src/resource_cache.rs +++ b/gfx/webrender/src/resource_cache.rs @@ -350,7 +350,7 @@ impl ResourceCache { let FontInstanceOptions { render_mode, subpx_dir, - synthetic_italics, + flags, bg_color, .. } = options.unwrap_or_default(); @@ -362,9 +362,9 @@ impl ResourceCache { bg_color, render_mode, subpx_dir, + flags, platform_options, variations, - synthetic_italics, ); if self.glyph_rasterizer.is_bitmap_font(&instance) { instance.render_mode = instance.render_mode.limit_by(FontRenderMode::Bitmap); diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index df552b03c5a5..9670af66183e 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -1818,11 +1818,6 @@ impl RenderPass { // If so, just skip adding it! Some(cache_key) => match self.dynamic_tasks.entry(cache_key) { Entry::Occupied(entry) => { - // TODO(gw): We can easily handle invalidation of tasks that - // contain children in the future. Since we don't - // have any cases of that yet, just assert to simplify - // the current implementation. - debug_assert!(task.children.is_empty()); debug_assert_eq!(entry.get().rect.size, size); task.kind = RenderTaskKind::Alias(entry.get().task_id); continue; diff --git a/gfx/webrender_api/Cargo.toml b/gfx/webrender_api/Cargo.toml index accb88f3a8a8..cb29a2de96a4 100644 --- a/gfx/webrender_api/Cargo.toml +++ b/gfx/webrender_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender_api" -version = "0.54.0" +version = "0.55.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" @@ -13,7 +13,7 @@ ipc = ["ipc-channel"] app_units = "0.5.6" bitflags = "1.0" bincode = "0.9" -byteorder = "1.0" +byteorder = "1.2.1" euclid = "0.15" ipc-channel = {version = "0.9", optional = true} serde = { version = "1.0", features = ["rc", "derive"] } @@ -24,4 +24,4 @@ core-foundation = "0.4" core-graphics = "0.12.3" [target.'cfg(target_os = "windows")'.dependencies] -dwrote = "0.4" +dwrote = "0.4.1" diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs index e83ab297198d..a70d102050de 100644 --- a/gfx/webrender_api/src/api.rs +++ b/gfx/webrender_api/src/api.rs @@ -263,6 +263,10 @@ pub enum DebugCommand { FetchPasses, /// Fetch clip-scroll tree. FetchClipScrollTree, + /// Fetch render tasks. + FetchRenderTasks, + /// Fetch screenshot. + FetchScreenshot, } #[derive(Clone, Deserialize, Serialize)] diff --git a/gfx/webrender_api/src/display_item.rs b/gfx/webrender_api/src/display_item.rs index 0b7db74a5074..cb94876a8129 100644 --- a/gfx/webrender_api/src/display_item.rs +++ b/gfx/webrender_api/src/display_item.rs @@ -39,22 +39,6 @@ impl ClipAndScrollInfo { } } -bitflags! { - /// Each bit of the edge AA mask is: - /// 0, when the edge of the primitive needs to be considered for AA - /// 1, when the edge of the segment needs to be considered for AA - /// - /// *Note*: the bit values have to match the shader logic in - /// `write_transform_vertex()` function. - #[derive(Deserialize, Serialize)] - pub struct EdgeAaSegmentMask: u8 { - const LEFT = 0x1; - const TOP = 0x2; - const RIGHT = 0x4; - const BOTTOM = 0x8; - } -} - /// A tag that can be used to identify items during hit testing. If the tag /// is missing then the item doesn't take part in hit testing at all. This /// is composed of two numbers. In Servo, the first is an identifier while the @@ -75,7 +59,6 @@ pub struct DisplayItem { pub struct PrimitiveInfo { pub rect: TypedRect, pub local_clip: LocalClip, - pub edge_aa_segment_mask: EdgeAaSegmentMask, pub is_backface_visible: bool, pub tag: Option, } @@ -96,7 +79,6 @@ impl LayerPrimitiveInfo { PrimitiveInfo { rect: rect, local_clip: clip, - edge_aa_segment_mask: EdgeAaSegmentMask::empty(), is_backface_visible: true, tag: None, } @@ -346,7 +328,7 @@ pub enum BorderStyle { } #[repr(u32)] -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum BoxShadowClipMode { Outset = 0, Inset = 1, diff --git a/gfx/webrender_api/src/display_list.rs b/gfx/webrender_api/src/display_list.rs index 55df385846f3..f8ab4fb3a192 100644 --- a/gfx/webrender_api/src/display_list.rs +++ b/gfx/webrender_api/src/display_list.rs @@ -315,7 +315,6 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> { LayerPrimitiveInfo { rect: info.rect.translate(&offset), local_clip: info.local_clip.create_with_offset(offset), - edge_aa_segment_mask: info.edge_aa_segment_mask, is_backface_visible: info.is_backface_visible, tag: info.tag, } diff --git a/gfx/webrender_api/src/font.rs b/gfx/webrender_api/src/font.rs index 5dfbd4c2f339..0b87109f4e99 100644 --- a/gfx/webrender_api/src/font.rs +++ b/gfx/webrender_api/src/font.rs @@ -203,12 +203,53 @@ pub struct GlyphOptions { pub render_mode: FontRenderMode, } +bitflags! { + #[repr(C)] + #[derive(Deserialize, Serialize)] + pub struct FontInstanceFlags: u32 { + // Common flags + const SYNTHETIC_ITALICS = 1 << 0; + const SYNTHETIC_BOLD = 1 << 1; + const EMBEDDED_BITMAPS = 1 << 2; + const SUBPIXEL_BGR = 1 << 3; + + // Windows flags + const FORCE_GDI = 1 << 16; + + // Mac flags + const FONT_SMOOTHING = 1 << 16; + + // FreeType flags + const FORCE_AUTOHINT = 1 << 16; + const NO_AUTOHINT = 1 << 17; + const VERTICAL_LAYOUT = 1 << 18; + } +} + +impl Default for FontInstanceFlags { + #[cfg(target_os = "windows")] + fn default() -> FontInstanceFlags { + FontInstanceFlags::empty() + } + + #[cfg(target_os = "macos")] + fn default() -> FontInstanceFlags { + FontInstanceFlags::FONT_SMOOTHING + } + + #[cfg(not(any(target_os = "macos", target_os = "windows")))] + fn default() -> FontInstanceFlags { + FontInstanceFlags::empty() + } +} + + #[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct FontInstanceOptions { pub render_mode: FontRenderMode, pub subpx_dir: SubpixelDirection, - pub synthetic_italics: bool, + pub flags: FontInstanceFlags, /// When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel, /// the text will be rendered with bg_color.r/g/b as an opaque estimated /// background color. @@ -220,7 +261,7 @@ impl Default for FontInstanceOptions { FontInstanceOptions { render_mode: FontRenderMode::Subpixel, subpx_dir: SubpixelDirection::Horizontal, - synthetic_italics: false, + flags: Default::default(), bg_color: ColorU::new(0, 0, 0, 0), } } @@ -230,16 +271,14 @@ impl Default for FontInstanceOptions { #[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct FontInstancePlatformOptions { - pub use_embedded_bitmap: bool, - pub force_gdi_rendering: bool, + pub unused: u32, } #[cfg(target_os = "windows")] impl Default for FontInstancePlatformOptions { fn default() -> FontInstancePlatformOptions { FontInstancePlatformOptions { - use_embedded_bitmap: false, - force_gdi_rendering: false, + unused: 0, } } } @@ -248,25 +287,18 @@ impl Default for FontInstancePlatformOptions { #[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct FontInstancePlatformOptions { - pub font_smoothing: bool, + pub unused: u32, } #[cfg(target_os = "macos")] impl Default for FontInstancePlatformOptions { fn default() -> FontInstancePlatformOptions { FontInstancePlatformOptions { - font_smoothing: true, + unused: 0, } } } -pub const FONT_FORCE_AUTOHINT: u16 = 0b1; -pub const FONT_NO_AUTOHINT: u16 = 0b10; -pub const FONT_EMBEDDED_BITMAP: u16 = 0b100; -pub const FONT_EMBOLDEN: u16 = 0b1000; -pub const FONT_VERTICAL_LAYOUT: u16 = 0b10000; -pub const FONT_SUBPIXEL_BGR: u16 = 0b100000; - #[cfg(not(any(target_os = "macos", target_os = "windows")))] #[repr(u8)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)] @@ -292,7 +324,6 @@ pub enum FontHinting { #[repr(C)] #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct FontInstancePlatformOptions { - pub flags: u16, pub lcd_filter: FontLCDFilter, pub hinting: FontHinting, } @@ -301,7 +332,6 @@ pub struct FontInstancePlatformOptions { impl Default for FontInstancePlatformOptions { fn default() -> FontInstancePlatformOptions { FontInstancePlatformOptions { - flags: 0, lcd_filter: FontLCDFilter::Default, hinting: FontHinting::LCD, } diff --git a/gfx/webrender_bindings/Cargo.toml b/gfx/webrender_bindings/Cargo.toml index c80a89d4ed32..d72ab1a56251 100644 --- a/gfx/webrender_bindings/Cargo.toml +++ b/gfx/webrender_bindings/Cargo.toml @@ -9,16 +9,16 @@ rayon = "0.8" thread_profiler = "0.1.1" euclid = "0.15" app_units = "0.5.6" -gleam = "0.4.14" +gleam = "0.4.15" log = "0.3" [dependencies.webrender] path = "../webrender" -version = "0.54.0" +version = "0.55.0" default-features = false [target.'cfg(target_os = "windows")'.dependencies] -dwrote = "0.4" +dwrote = "0.4.1" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.4" diff --git a/gfx/webrender_bindings/webrender_ffi_generated.h b/gfx/webrender_bindings/webrender_ffi_generated.h index beb2a8735f2e..f1c8e9158717 100644 --- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -874,7 +874,7 @@ struct ColorU { struct FontInstanceOptions { FontRenderMode render_mode; SubpixelDirection subpx_dir; - bool synthetic_italics; + FontInstanceFlags flags; // When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel, // the text will be rendered with bg_color.r/g/b as an opaque estimated // background color. @@ -883,42 +883,38 @@ struct FontInstanceOptions { bool operator==(const FontInstanceOptions& aOther) const { return render_mode == aOther.render_mode && subpx_dir == aOther.subpx_dir && - synthetic_italics == aOther.synthetic_italics && + flags == aOther.flags && bg_color == aOther.bg_color; } }; #if defined(XP_WIN) struct FontInstancePlatformOptions { - bool use_embedded_bitmap; - bool force_gdi_rendering; + uint32_t unused; bool operator==(const FontInstancePlatformOptions& aOther) const { - return use_embedded_bitmap == aOther.use_embedded_bitmap && - force_gdi_rendering == aOther.force_gdi_rendering; + return unused == aOther.unused; } }; #endif #if defined(XP_MACOSX) struct FontInstancePlatformOptions { - bool font_smoothing; + uint32_t unused; bool operator==(const FontInstancePlatformOptions& aOther) const { - return font_smoothing == aOther.font_smoothing; + return unused == aOther.unused; } }; #endif #if !(defined(XP_MACOSX) || defined(XP_WIN)) struct FontInstancePlatformOptions { - uint16_t flags; FontLCDFilter lcd_filter; FontHinting hinting; bool operator==(const FontInstancePlatformOptions& aOther) const { - return flags == aOther.flags && - lcd_filter == aOther.lcd_filter && + return lcd_filter == aOther.lcd_filter && hinting == aOther.hinting; } }; From fee87ce9f1b69f7874c95c41afb4d79373c62ea0 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 1 Dec 2017 09:50:52 -0500 Subject: [PATCH 035/219] Bug 1421275 - Update Cargo lockfiles and re-vendor rust dependencies. r=jrmuizel MozReview-Commit-ID: Jaty3RGfffU --HG-- rename : third_party/rust/byteorder-1.0.0/COPYING => third_party/rust/byteorder-1.1.0/COPYING rename : third_party/rust/byteorder-1.0.0/LICENSE-MIT => third_party/rust/byteorder-1.1.0/LICENSE-MIT rename : third_party/rust/byteorder-1.0.0/README.md => third_party/rust/byteorder-1.1.0/README.md rename : third_party/rust/byteorder-1.0.0/UNLICENSE => third_party/rust/byteorder-1.1.0/UNLICENSE extra : rebase_source : 0668fbfe5d8ba288a0e530de3650bd013924b4ce --- .../rust/byteorder-1.0.0/.cargo-checksum.json | 1 - third_party/rust/byteorder-1.0.0/.travis.yml | 15 - third_party/rust/byteorder-1.0.0/Cargo.toml | 26 - .../rust/byteorder-1.0.0/benches/bench.rs | 148 - third_party/rust/byteorder-1.0.0/src/lib.rs | 820 ----- third_party/rust/byteorder-1.0.0/src/new.rs | 269 -- .../rust/byteorder-1.1.0/.cargo-checksum.json | 1 + third_party/rust/byteorder-1.1.0/.travis.yml | 22 + third_party/rust/byteorder-1.1.0/CHANGELOG.md | 36 + .../COPYING | 0 third_party/rust/byteorder-1.1.0/Cargo.toml | 44 + .../LICENSE-MIT | 0 .../README.md | 0 .../UNLICENSE | 0 .../rust/byteorder-1.1.0/benches/bench.rs | 320 ++ third_party/rust/byteorder-1.1.0/src/io.rs | 1189 ++++++ third_party/rust/byteorder-1.1.0/src/lib.rs | 3191 +++++++++++++++++ .../rust/byteorder/.cargo-checksum.json | 2 +- third_party/rust/byteorder/CHANGELOG.md | 22 + third_party/rust/byteorder/Cargo.toml | 11 +- third_party/rust/byteorder/README.md | 2 +- third_party/rust/byteorder/src/io.rs | 129 +- third_party/rust/byteorder/src/lib.rs | 135 +- third_party/rust/dwrote/.cargo-checksum.json | 2 +- third_party/rust/dwrote/Cargo.toml | 14 +- third_party/rust/dwrote/src/font_face.rs | 46 +- third_party/rust/dwrote/src/lib.rs | 1 + third_party/rust/gleam/.cargo-checksum.json | 2 +- third_party/rust/gleam/Cargo.toml | 2 +- third_party/rust/gleam/build.rs | 2 + third_party/rust/gleam/src/gl_fns.rs | 20 +- third_party/rust/gleam/src/gles_fns.rs | 100 +- toolkit/library/gtest/rust/Cargo.lock | 48 +- toolkit/library/rust/Cargo.lock | 48 +- 34 files changed, 5141 insertions(+), 1527 deletions(-) delete mode 100644 third_party/rust/byteorder-1.0.0/.cargo-checksum.json delete mode 100644 third_party/rust/byteorder-1.0.0/.travis.yml delete mode 100644 third_party/rust/byteorder-1.0.0/Cargo.toml delete mode 100644 third_party/rust/byteorder-1.0.0/benches/bench.rs delete mode 100644 third_party/rust/byteorder-1.0.0/src/lib.rs delete mode 100644 third_party/rust/byteorder-1.0.0/src/new.rs create mode 100644 third_party/rust/byteorder-1.1.0/.cargo-checksum.json create mode 100644 third_party/rust/byteorder-1.1.0/.travis.yml create mode 100644 third_party/rust/byteorder-1.1.0/CHANGELOG.md rename third_party/rust/{byteorder-1.0.0 => byteorder-1.1.0}/COPYING (100%) create mode 100644 third_party/rust/byteorder-1.1.0/Cargo.toml rename third_party/rust/{byteorder-1.0.0 => byteorder-1.1.0}/LICENSE-MIT (100%) rename third_party/rust/{byteorder-1.0.0 => byteorder-1.1.0}/README.md (100%) rename third_party/rust/{byteorder-1.0.0 => byteorder-1.1.0}/UNLICENSE (100%) create mode 100644 third_party/rust/byteorder-1.1.0/benches/bench.rs create mode 100644 third_party/rust/byteorder-1.1.0/src/io.rs create mode 100644 third_party/rust/byteorder-1.1.0/src/lib.rs diff --git a/third_party/rust/byteorder-1.0.0/.cargo-checksum.json b/third_party/rust/byteorder-1.0.0/.cargo-checksum.json deleted file mode 100644 index 86c8ac247ab7..000000000000 --- a/third_party/rust/byteorder-1.0.0/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{".travis.yml":"c8243fb884ca390f5a7b8cc45e1c0d5bbbdd7e4e82ada2dc1880b3a904c9ce12","COPYING":"01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f","Cargo.toml":"b6cd79e1f2a93cd8a5c6e6dd7985c46fc26e442ae5b8ed4a0ff37a4ad4708023","LICENSE-MIT":"0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f","README.md":"0559514b9d7488e96fb7a2f3c043a62fadf3495a1e10602d109ce79ee67da998","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","benches/bench.rs":"f583692d829c8dfe19b1d5b9e968ccf5c74d6733367ca183edff74041a6afedd","src/lib.rs":"b038b8a84b2b7b2143b2835185b3cbbacaf056fa8a2f03bec84bfd79c913c726","src/new.rs":"161c21b7ebb5668c7cc70b46b0eb37709e06bb9c854f2fdfc6ce3d3babcbf3de"},"package":"c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"} \ No newline at end of file diff --git a/third_party/rust/byteorder-1.0.0/.travis.yml b/third_party/rust/byteorder-1.0.0/.travis.yml deleted file mode 100644 index 0ebea27693b2..000000000000 --- a/third_party/rust/byteorder-1.0.0/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: rust -rust: - - 1.12.0 - - stable - - beta - - nightly -script: - - cargo build --verbose - - cargo doc - - cargo test --verbose - - cargo test --verbose --no-default-features --lib - - if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then - cargo bench --verbose; - cargo bench --verbose --no-default-features; - fi diff --git a/third_party/rust/byteorder-1.0.0/Cargo.toml b/third_party/rust/byteorder-1.0.0/Cargo.toml deleted file mode 100644 index d207d47babef..000000000000 --- a/third_party/rust/byteorder-1.0.0/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "byteorder" -version = "1.0.0" #:version -authors = ["Andrew Gallant "] -description = "Library for reading/writing numbers in big-endian and little-endian." -documentation = "https://docs.rs/byteorder" -homepage = "https://github.com/BurntSushi/byteorder" -repository = "https://github.com/BurntSushi/byteorder" -readme = "README.md" -keywords = ["byte", "endian", "big-endian", "little-endian", "binary"] -license = "Unlicense/MIT" - -[lib] -name = "byteorder" -bench = false - -[dev-dependencies] -quickcheck = "0.4" -rand = "0.3" - -[features] -default = ["std"] -std = [] - -[profile.bench] -opt-level = 3 diff --git a/third_party/rust/byteorder-1.0.0/benches/bench.rs b/third_party/rust/byteorder-1.0.0/benches/bench.rs deleted file mode 100644 index 51d57b3da7b1..000000000000 --- a/third_party/rust/byteorder-1.0.0/benches/bench.rs +++ /dev/null @@ -1,148 +0,0 @@ -#![feature(test)] - -extern crate byteorder; -extern crate test; - -macro_rules! bench_num { - ($name:ident, $read:ident, $bytes:expr, $data:expr) => ( - mod $name { - use byteorder::{ByteOrder, BigEndian, NativeEndian, LittleEndian}; - use super::test::Bencher; - use super::test::black_box as bb; - - const NITER: usize = 100_000; - - #[bench] - fn read_big_endian(b: &mut Bencher) { - let buf = $data; - b.iter(|| { - for _ in 0..NITER { - bb(BigEndian::$read(&buf, $bytes)); - } - }); - } - - #[bench] - fn read_little_endian(b: &mut Bencher) { - let buf = $data; - b.iter(|| { - for _ in 0..NITER { - bb(LittleEndian::$read(&buf, $bytes)); - } - }); - } - - #[bench] - fn read_native_endian(b: &mut Bencher) { - let buf = $data; - b.iter(|| { - for _ in 0..NITER { - bb(NativeEndian::$read(&buf, $bytes)); - } - }); - } - } - ); - ($ty:ident, $max:ident, - $read:ident, $write:ident, $size:expr, $data:expr) => ( - mod $ty { - use std::$ty; - use byteorder::{ByteOrder, BigEndian, NativeEndian, LittleEndian}; - use super::test::Bencher; - use super::test::black_box as bb; - - const NITER: usize = 100_000; - - #[bench] - fn read_big_endian(b: &mut Bencher) { - let buf = $data; - b.iter(|| { - for _ in 0..NITER { - bb(BigEndian::$read(&buf)); - } - }); - } - - #[bench] - fn read_little_endian(b: &mut Bencher) { - let buf = $data; - b.iter(|| { - for _ in 0..NITER { - bb(LittleEndian::$read(&buf)); - } - }); - } - - #[bench] - fn read_native_endian(b: &mut Bencher) { - let buf = $data; - b.iter(|| { - for _ in 0..NITER { - bb(NativeEndian::$read(&buf)); - } - }); - } - - #[bench] - fn write_big_endian(b: &mut Bencher) { - let mut buf = $data; - let n = $ty::$max; - b.iter(|| { - for _ in 0..NITER { - bb(BigEndian::$write(&mut buf, n)); - } - }); - } - - #[bench] - fn write_little_endian(b: &mut Bencher) { - let mut buf = $data; - let n = $ty::$max; - b.iter(|| { - for _ in 0..NITER { - bb(LittleEndian::$write(&mut buf, n)); - } - }); - } - - #[bench] - fn write_native_endian(b: &mut Bencher) { - let mut buf = $data; - let n = $ty::$max; - b.iter(|| { - for _ in 0..NITER { - bb(NativeEndian::$write(&mut buf, n)); - } - }); - } - } - ); -} - -bench_num!(u16, MAX, read_u16, write_u16, 2, [1, 2]); -bench_num!(i16, MAX, read_i16, write_i16, 2, [1, 2]); -bench_num!(u32, MAX, read_u32, write_u32, 4, [1, 2, 3, 4]); -bench_num!(i32, MAX, read_i32, write_i32, 4, [1, 2, 3, 4]); -bench_num!(u64, MAX, read_u64, write_u64, 8, [1, 2, 3, 4, 5, 6, 7, 8]); -bench_num!(i64, MAX, read_i64, write_i64, 8, [1, 2, 3, 4, 5, 6, 7, 8]); -bench_num!(f32, MAX, read_f32, write_f32, 4, [1, 2, 3, 4]); -bench_num!(f64, MAX, read_f64, write_f64, 8, - [1, 2, 3, 4, 5, 6, 7, 8]); - -bench_num!(uint_1, read_uint, 1, [1]); -bench_num!(uint_2, read_uint, 2, [1, 2]); -bench_num!(uint_3, read_uint, 3, [1, 2, 3]); -bench_num!(uint_4, read_uint, 4, [1, 2, 3, 4]); -bench_num!(uint_5, read_uint, 5, [1, 2, 3, 4, 5]); -bench_num!(uint_6, read_uint, 6, [1, 2, 3, 4, 5, 6]); -bench_num!(uint_7, read_uint, 7, [1, 2, 3, 4, 5, 6, 7]); -bench_num!(uint_8, read_uint, 8, [1, 2, 3, 4, 5, 6, 7, 8]); - -bench_num!(int_1, read_int, 1, [1]); -bench_num!(int_2, read_int, 2, [1, 2]); -bench_num!(int_3, read_int, 3, [1, 2, 3]); -bench_num!(int_4, read_int, 4, [1, 2, 3, 4]); -bench_num!(int_5, read_int, 5, [1, 2, 3, 4, 5]); -bench_num!(int_6, read_int, 6, [1, 2, 3, 4, 5, 6]); -bench_num!(int_7, read_int, 7, [1, 2, 3, 4, 5, 6, 7]); -bench_num!(int_8, read_int, 8, [1, 2, 3, 4, 5, 6, 7, 8]); diff --git a/third_party/rust/byteorder-1.0.0/src/lib.rs b/third_party/rust/byteorder-1.0.0/src/lib.rs deleted file mode 100644 index e524560505f6..000000000000 --- a/third_party/rust/byteorder-1.0.0/src/lib.rs +++ /dev/null @@ -1,820 +0,0 @@ -/*! -This crate provides convenience methods for encoding and decoding numbers -in either big-endian or little-endian order. - -The organization of the crate is pretty simple. A trait, `ByteOrder`, specifies -byte conversion methods for each type of number in Rust (sans numbers that have -a platform dependent size like `usize` and `isize`). Two types, `BigEndian` -and `LittleEndian` implement these methods. Finally, `ReadBytesExt` and -`WriteBytesExt` provide convenience methods available to all types that -implement `Read` and `Write`. - -# Examples - -Read unsigned 16 bit big-endian integers from a `Read` type: - -```rust -use std::io::Cursor; -use byteorder::{BigEndian, ReadBytesExt}; - -let mut rdr = Cursor::new(vec![2, 5, 3, 0]); -// Note that we use type parameters to indicate which kind of byte order -// we want! -assert_eq!(517, rdr.read_u16::().unwrap()); -assert_eq!(768, rdr.read_u16::().unwrap()); -``` - -Write unsigned 16 bit little-endian integers to a `Write` type: - -```rust -use byteorder::{LittleEndian, WriteBytesExt}; - -let mut wtr = vec![]; -wtr.write_u16::(517).unwrap(); -wtr.write_u16::(768).unwrap(); -assert_eq!(wtr, vec![5, 2, 0, 3]); -``` -*/ - -#![deny(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -extern crate core; - -use core::fmt::Debug; -use core::hash::Hash; -use core::mem::transmute; -use core::ptr::copy_nonoverlapping; - -#[cfg(feature = "std")] -pub use new::{ReadBytesExt, WriteBytesExt}; - -#[cfg(feature = "std")] -mod new; - -#[inline] -fn extend_sign(val: u64, nbytes: usize) -> i64 { - let shift = (8 - nbytes) * 8; - (val << shift) as i64 >> shift -} - -#[inline] -fn unextend_sign(val: i64, nbytes: usize) -> u64 { - let shift = (8 - nbytes) * 8; - (val << shift) as u64 >> shift -} - -#[inline] -fn pack_size(n: u64) -> usize { - if n < 1 << 8 { - 1 - } else if n < 1 << 16 { - 2 - } else if n < 1 << 24 { - 3 - } else if n < 1 << 32 { - 4 - } else if n < 1 << 40 { - 5 - } else if n < 1 << 48 { - 6 - } else if n < 1 << 56 { - 7 - } else { - 8 - } -} - -/// ByteOrder describes types that can serialize integers as bytes. -/// -/// Note that `Self` does not appear anywhere in this trait's definition! -/// Therefore, in order to use it, you'll need to use syntax like -/// `T::read_u16(&[0, 1])` where `T` implements `ByteOrder`. -/// -/// This crate provides two types that implement `ByteOrder`: `BigEndian` -/// and `LittleEndian`. -/// -/// # Examples -/// -/// Write and read `u32` numbers in little endian order: -/// -/// ```rust -/// use byteorder::{ByteOrder, LittleEndian}; -/// -/// let mut buf = [0; 4]; -/// LittleEndian::write_u32(&mut buf, 1_000_000); -/// assert_eq!(1_000_000, LittleEndian::read_u32(&buf)); -/// ``` -/// -/// Write and read `i16` numbers in big endian order: -/// -/// ```rust -/// use byteorder::{ByteOrder, BigEndian}; -/// -/// let mut buf = [0; 2]; -/// BigEndian::write_i16(&mut buf, -50_000); -/// assert_eq!(-50_000, BigEndian::read_i16(&buf)); -/// ``` -pub trait ByteOrder - : Clone + Copy + Debug + Default + Eq + Hash + Ord + PartialEq + PartialOrd { - /// Reads an unsigned 16 bit integer from `buf`. - /// - /// Panics when `buf.len() < 2`. - fn read_u16(buf: &[u8]) -> u16; - - /// Reads an unsigned 32 bit integer from `buf`. - /// - /// Panics when `buf.len() < 4`. - fn read_u32(buf: &[u8]) -> u32; - - /// Reads an unsigned 64 bit integer from `buf`. - /// - /// Panics when `buf.len() < 8`. - fn read_u64(buf: &[u8]) -> u64; - - /// Reads an unsigned n-bytes integer from `buf`. - /// - /// Panics when `nbytes < 1` or `nbytes > 8` or - /// `buf.len() < nbytes` - fn read_uint(buf: &[u8], nbytes: usize) -> u64; - - /// Writes an unsigned 16 bit integer `n` to `buf`. - /// - /// Panics when `buf.len() < 2`. - fn write_u16(buf: &mut [u8], n: u16); - - /// Writes an unsigned 32 bit integer `n` to `buf`. - /// - /// Panics when `buf.len() < 4`. - fn write_u32(buf: &mut [u8], n: u32); - - /// Writes an unsigned 64 bit integer `n` to `buf`. - /// - /// Panics when `buf.len() < 8`. - fn write_u64(buf: &mut [u8], n: u64); - - /// Writes an unsigned integer `n` to `buf` using only `nbytes`. - /// - /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 8`, then - /// this method panics. - fn write_uint(buf: &mut [u8], n: u64, nbytes: usize); - - /// Reads a signed 16 bit integer from `buf`. - /// - /// Panics when `buf.len() < 2`. - #[inline] - fn read_i16(buf: &[u8]) -> i16 { - Self::read_u16(buf) as i16 - } - - /// Reads a signed 32 bit integer from `buf`. - /// - /// Panics when `buf.len() < 4`. - #[inline] - fn read_i32(buf: &[u8]) -> i32 { - Self::read_u32(buf) as i32 - } - - /// Reads a signed 64 bit integer from `buf`. - /// - /// Panics when `buf.len() < 8`. - #[inline] - fn read_i64(buf: &[u8]) -> i64 { - Self::read_u64(buf) as i64 - } - - /// Reads a signed n-bytes integer from `buf`. - /// - /// Panics when `nbytes < 1` or `nbytes > 8` or - /// `buf.len() < nbytes` - #[inline] - fn read_int(buf: &[u8], nbytes: usize) -> i64 { - extend_sign(Self::read_uint(buf, nbytes), nbytes) - } - - /// Reads a IEEE754 single-precision (4 bytes) floating point number. - /// - /// Panics when `buf.len() < 4`. - #[inline] - fn read_f32(buf: &[u8]) -> f32 { - unsafe { transmute(Self::read_u32(buf)) } - } - - /// Reads a IEEE754 double-precision (8 bytes) floating point number. - /// - /// Panics when `buf.len() < 8`. - #[inline] - fn read_f64(buf: &[u8]) -> f64 { - unsafe { transmute(Self::read_u64(buf)) } - } - - /// Writes a signed 16 bit integer `n` to `buf`. - /// - /// Panics when `buf.len() < 2`. - #[inline] - fn write_i16(buf: &mut [u8], n: i16) { - Self::write_u16(buf, n as u16) - } - - /// Writes a signed 32 bit integer `n` to `buf`. - /// - /// Panics when `buf.len() < 4`. - #[inline] - fn write_i32(buf: &mut [u8], n: i32) { - Self::write_u32(buf, n as u32) - } - - /// Writes a signed 64 bit integer `n` to `buf`. - /// - /// Panics when `buf.len() < 8`. - #[inline] - fn write_i64(buf: &mut [u8], n: i64) { - Self::write_u64(buf, n as u64) - } - - /// Writes a signed integer `n` to `buf` using only `nbytes`. - /// - /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 8`, then - /// this method panics. - #[inline] - fn write_int(buf: &mut [u8], n: i64, nbytes: usize) { - Self::write_uint(buf, unextend_sign(n, nbytes), nbytes) - } - - /// Writes a IEEE754 single-precision (4 bytes) floating point number. - /// - /// Panics when `buf.len() < 4`. - #[inline] - fn write_f32(buf: &mut [u8], n: f32) { - Self::write_u32(buf, unsafe { transmute(n) }) - } - - /// Writes a IEEE754 double-precision (8 bytes) floating point number. - /// - /// Panics when `buf.len() < 8`. - #[inline] - fn write_f64(buf: &mut [u8], n: f64) { - Self::write_u64(buf, unsafe { transmute(n) }) - } -} - -/// Defines big-endian serialization. -/// -/// Note that this type has no value constructor. It is used purely at the -/// type level. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum BigEndian {} - -impl Default for BigEndian { - fn default() -> BigEndian { - unreachable!() - } -} - -/// Defines little-endian serialization. -/// -/// Note that this type has no value constructor. It is used purely at the -/// type level. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum LittleEndian {} - -impl Default for LittleEndian { - fn default() -> LittleEndian { - unreachable!() - } -} - -/// Defines network byte order serialization. -/// -/// Network byte order is defined by [RFC 1700][1] to be big-endian, and is -/// referred to in several protocol specifications. This type is an alias of -/// BigEndian. -/// -/// [1]: https://tools.ietf.org/html/rfc1700 -/// -/// Note that this type has no value constructor. It is used purely at the -/// type level. -pub type NetworkEndian = BigEndian; - -/// Defines system native-endian serialization. -/// -/// Note that this type has no value constructor. It is used purely at the -/// type level. -#[cfg(target_endian = "little")] -pub type NativeEndian = LittleEndian; - -/// Defines system native-endian serialization. -/// -/// Note that this type has no value constructor. It is used purely at the -/// type level. -#[cfg(target_endian = "big")] -pub type NativeEndian = BigEndian; - -macro_rules! read_num_bytes { - ($ty:ty, $size:expr, $src:expr, $which:ident) => ({ - assert!($size == ::core::mem::size_of::<$ty>()); - assert!($size <= $src.len()); - let mut data: $ty = 0; - unsafe { - copy_nonoverlapping( - $src.as_ptr(), - &mut data as *mut $ty as *mut u8, - $size); - } - data.$which() - }); -} - -macro_rules! write_num_bytes { - ($ty:ty, $size:expr, $n:expr, $dst:expr, $which:ident) => ({ - assert!($size <= $dst.len()); - unsafe { - // N.B. https://github.com/rust-lang/rust/issues/22776 - let bytes = transmute::<_, [u8; $size]>($n.$which()); - copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size); - } - }); -} - -impl ByteOrder for BigEndian { - #[inline] - fn read_u16(buf: &[u8]) -> u16 { - read_num_bytes!(u16, 2, buf, to_be) - } - - #[inline] - fn read_u32(buf: &[u8]) -> u32 { - read_num_bytes!(u32, 4, buf, to_be) - } - - #[inline] - fn read_u64(buf: &[u8]) -> u64 { - read_num_bytes!(u64, 8, buf, to_be) - } - - #[inline] - fn read_uint(buf: &[u8], nbytes: usize) -> u64 { - assert!(1 <= nbytes && nbytes <= 8 && nbytes <= buf.len()); - let mut out = [0u8; 8]; - let ptr_out = out.as_mut_ptr(); - unsafe { - copy_nonoverlapping( - buf.as_ptr(), ptr_out.offset((8 - nbytes) as isize), nbytes); - (*(ptr_out as *const u64)).to_be() - } - } - - #[inline] - fn write_u16(buf: &mut [u8], n: u16) { - write_num_bytes!(u16, 2, n, buf, to_be); - } - - #[inline] - fn write_u32(buf: &mut [u8], n: u32) { - write_num_bytes!(u32, 4, n, buf, to_be); - } - - #[inline] - fn write_u64(buf: &mut [u8], n: u64) { - write_num_bytes!(u64, 8, n, buf, to_be); - } - - #[inline] - fn write_uint(buf: &mut [u8], n: u64, nbytes: usize) { - assert!(pack_size(n) <= nbytes && nbytes <= 8); - assert!(nbytes <= buf.len()); - unsafe { - let bytes: [u8; 8] = transmute(n.to_be()); - copy_nonoverlapping( - bytes.as_ptr().offset((8 - nbytes) as isize), - buf.as_mut_ptr(), - nbytes); - } - } -} - -impl ByteOrder for LittleEndian { - #[inline] - fn read_u16(buf: &[u8]) -> u16 { - read_num_bytes!(u16, 2, buf, to_le) - } - - #[inline] - fn read_u32(buf: &[u8]) -> u32 { - read_num_bytes!(u32, 4, buf, to_le) - } - - #[inline] - fn read_u64(buf: &[u8]) -> u64 { - read_num_bytes!(u64, 8, buf, to_le) - } - - #[inline] - fn read_uint(buf: &[u8], nbytes: usize) -> u64 { - assert!(1 <= nbytes && nbytes <= 8 && nbytes <= buf.len()); - let mut out = [0u8; 8]; - let ptr_out = out.as_mut_ptr(); - unsafe { - copy_nonoverlapping(buf.as_ptr(), ptr_out, nbytes); - (*(ptr_out as *const u64)).to_le() - } - } - - #[inline] - fn write_u16(buf: &mut [u8], n: u16) { - write_num_bytes!(u16, 2, n, buf, to_le); - } - - #[inline] - fn write_u32(buf: &mut [u8], n: u32) { - write_num_bytes!(u32, 4, n, buf, to_le); - } - - #[inline] - fn write_u64(buf: &mut [u8], n: u64) { - write_num_bytes!(u64, 8, n, buf, to_le); - } - - #[inline] - fn write_uint(buf: &mut [u8], n: u64, nbytes: usize) { - assert!(pack_size(n as u64) <= nbytes && nbytes <= 8); - assert!(nbytes <= buf.len()); - unsafe { - let bytes: [u8; 8] = transmute(n.to_le()); - copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr(), nbytes); - } - } -} - -#[cfg(test)] -mod test { - extern crate quickcheck; - extern crate rand; - - use self::rand::thread_rng; - use self::quickcheck::{QuickCheck, StdGen, Testable}; - - pub const U64_MAX: u64 = ::core::u64::MAX; - pub const I64_MAX: u64 = ::core::i64::MAX as u64; - - pub fn qc_sized(f: A, size: u64) { - QuickCheck::new() - .gen(StdGen::new(thread_rng(), size as usize)) - .tests(1_00) - .max_tests(10_000) - .quickcheck(f); - } - - macro_rules! qc_byte_order { - ($name:ident, $ty_int:ident, $max:expr, - $bytes:expr, $read:ident, $write:ident) => ( - mod $name { - use {BigEndian, ByteOrder, NativeEndian, LittleEndian}; - use super::qc_sized; - - #[test] - fn big_endian() { - let max = ($max - 1) >> (8 * (8 - $bytes)); - fn prop(n: $ty_int) -> bool { - let mut buf = [0; 8]; - BigEndian::$write(&mut buf, n, $bytes); - n == BigEndian::$read(&mut buf[..$bytes], $bytes) - } - qc_sized(prop as fn($ty_int) -> bool, max); - } - - #[test] - fn little_endian() { - let max = ($max - 1) >> (8 * (8 - $bytes)); - fn prop(n: $ty_int) -> bool { - let mut buf = [0; 8]; - LittleEndian::$write(&mut buf, n, $bytes); - n == LittleEndian::$read(&mut buf[..$bytes], $bytes) - } - qc_sized(prop as fn($ty_int) -> bool, max); - } - - #[test] - fn native_endian() { - let max = ($max - 1) >> (8 * (8 - $bytes)); - fn prop(n: $ty_int) -> bool { - let mut buf = [0; 8]; - NativeEndian::$write(&mut buf, n, $bytes); - n == NativeEndian::$read(&mut buf[..$bytes], $bytes) - } - qc_sized(prop as fn($ty_int) -> bool, max); - } - } - ); - ($name:ident, $ty_int:ident, $max:expr, - $read:ident, $write:ident) => ( - mod $name { - use core::mem::size_of; - use {BigEndian, ByteOrder, NativeEndian, LittleEndian}; - use super::qc_sized; - - #[test] - fn big_endian() { - fn prop(n: $ty_int) -> bool { - let bytes = size_of::<$ty_int>(); - let mut buf = [0; 8]; - BigEndian::$write(&mut buf[8 - bytes..], n); - n == BigEndian::$read(&mut buf[8 - bytes..]) - } - qc_sized(prop as fn($ty_int) -> bool, $max - 1); - } - - #[test] - fn little_endian() { - fn prop(n: $ty_int) -> bool { - let bytes = size_of::<$ty_int>(); - let mut buf = [0; 8]; - LittleEndian::$write(&mut buf[..bytes], n); - n == LittleEndian::$read(&mut buf[..bytes]) - } - qc_sized(prop as fn($ty_int) -> bool, $max - 1); - } - - #[test] - fn native_endian() { - fn prop(n: $ty_int) -> bool { - let bytes = size_of::<$ty_int>(); - let mut buf = [0; 8]; - NativeEndian::$write(&mut buf[..bytes], n); - n == NativeEndian::$read(&mut buf[..bytes]) - } - qc_sized(prop as fn($ty_int) -> bool, $max - 1); - } - } - ); - } - - qc_byte_order!(prop_u16, u16, ::core::u16::MAX as u64, read_u16, write_u16); - qc_byte_order!(prop_i16, i16, ::core::i16::MAX as u64, read_i16, write_i16); - qc_byte_order!(prop_u32, u32, ::core::u32::MAX as u64, read_u32, write_u32); - qc_byte_order!(prop_i32, i32, ::core::i32::MAX as u64, read_i32, write_i32); - qc_byte_order!(prop_u64, u64, ::core::u64::MAX as u64, read_u64, write_u64); - qc_byte_order!(prop_i64, i64, ::core::i64::MAX as u64, read_i64, write_i64); - qc_byte_order!(prop_f32, f32, ::core::u64::MAX as u64, read_f32, write_f32); - qc_byte_order!(prop_f64, f64, ::core::i64::MAX as u64, read_f64, write_f64); - - qc_byte_order!(prop_uint_1, u64, super::U64_MAX, 1, read_uint, write_uint); - qc_byte_order!(prop_uint_2, u64, super::U64_MAX, 2, read_uint, write_uint); - qc_byte_order!(prop_uint_3, u64, super::U64_MAX, 3, read_uint, write_uint); - qc_byte_order!(prop_uint_4, u64, super::U64_MAX, 4, read_uint, write_uint); - qc_byte_order!(prop_uint_5, u64, super::U64_MAX, 5, read_uint, write_uint); - qc_byte_order!(prop_uint_6, u64, super::U64_MAX, 6, read_uint, write_uint); - qc_byte_order!(prop_uint_7, u64, super::U64_MAX, 7, read_uint, write_uint); - qc_byte_order!(prop_uint_8, u64, super::U64_MAX, 8, read_uint, write_uint); - - qc_byte_order!(prop_int_1, i64, super::I64_MAX, 1, read_int, write_int); - qc_byte_order!(prop_int_2, i64, super::I64_MAX, 2, read_int, write_int); - qc_byte_order!(prop_int_3, i64, super::I64_MAX, 3, read_int, write_int); - qc_byte_order!(prop_int_4, i64, super::I64_MAX, 4, read_int, write_int); - qc_byte_order!(prop_int_5, i64, super::I64_MAX, 5, read_int, write_int); - qc_byte_order!(prop_int_6, i64, super::I64_MAX, 6, read_int, write_int); - qc_byte_order!(prop_int_7, i64, super::I64_MAX, 7, read_int, write_int); - qc_byte_order!(prop_int_8, i64, super::I64_MAX, 8, read_int, write_int); - - // Test that all of the byte conversion functions panic when given a - // buffer that is too small. - // - // These tests are critical to ensure safety, otherwise we might end up - // with a buffer overflow. - macro_rules! too_small { - ($name:ident, $maximally_small:expr, $zero:expr, - $read:ident, $write:ident) => ( - mod $name { - use {BigEndian, ByteOrder, NativeEndian, LittleEndian}; - - #[test] - #[should_panic] - fn read_big_endian() { - let buf = [0; $maximally_small]; - BigEndian::$read(&buf); - } - - #[test] - #[should_panic] - fn read_little_endian() { - let buf = [0; $maximally_small]; - LittleEndian::$read(&buf); - } - - #[test] - #[should_panic] - fn read_native_endian() { - let buf = [0; $maximally_small]; - NativeEndian::$read(&buf); - } - - #[test] - #[should_panic] - fn write_big_endian() { - let mut buf = [0; $maximally_small]; - BigEndian::$write(&mut buf, $zero); - } - - #[test] - #[should_panic] - fn write_little_endian() { - let mut buf = [0; $maximally_small]; - LittleEndian::$write(&mut buf, $zero); - } - - #[test] - #[should_panic] - fn write_native_endian() { - let mut buf = [0; $maximally_small]; - NativeEndian::$write(&mut buf, $zero); - } - } - ); - ($name:ident, $maximally_small:expr, $read:ident) => ( - mod $name { - use {BigEndian, ByteOrder, NativeEndian, LittleEndian}; - - #[test] - #[should_panic] - fn read_big_endian() { - let buf = [0; $maximally_small]; - BigEndian::$read(&buf, $maximally_small + 1); - } - - #[test] - #[should_panic] - fn read_little_endian() { - let buf = [0; $maximally_small]; - LittleEndian::$read(&buf, $maximally_small + 1); - } - - #[test] - #[should_panic] - fn read_native_endian() { - let buf = [0; $maximally_small]; - NativeEndian::$read(&buf, $maximally_small + 1); - } - } - ); - } - - too_small!(small_u16, 1, 0, read_u16, write_u16); - too_small!(small_i16, 1, 0, read_i16, write_i16); - too_small!(small_u32, 3, 0, read_u32, write_u32); - too_small!(small_i32, 3, 0, read_i32, write_i32); - too_small!(small_u64, 7, 0, read_u64, write_u64); - too_small!(small_i64, 7, 0, read_i64, write_i64); - too_small!(small_f32, 3, 0.0, read_f32, write_f32); - too_small!(small_f64, 7, 0.0, read_f64, write_f64); - - too_small!(small_uint_1, 1, read_uint); - too_small!(small_uint_2, 2, read_uint); - too_small!(small_uint_3, 3, read_uint); - too_small!(small_uint_4, 4, read_uint); - too_small!(small_uint_5, 5, read_uint); - too_small!(small_uint_6, 6, read_uint); - too_small!(small_uint_7, 7, read_uint); - - too_small!(small_int_1, 1, read_int); - too_small!(small_int_2, 2, read_int); - too_small!(small_int_3, 3, read_int); - too_small!(small_int_4, 4, read_int); - too_small!(small_int_5, 5, read_int); - too_small!(small_int_6, 6, read_int); - too_small!(small_int_7, 7, read_int); - - #[test] - fn uint_bigger_buffer() { - use {ByteOrder, LittleEndian}; - let n = LittleEndian::read_uint(&[1, 2, 3, 4, 5, 6, 7, 8], 5); - assert_eq!(n, 0x0504030201); - } -} - -#[cfg(test)] -#[cfg(feature = "std")] -mod stdtests { - macro_rules! qc_bytes_ext { - ($name:ident, $ty_int:ident, $max:expr, - $bytes:expr, $read:ident, $write:ident) => ( - mod $name { - use std::io::Cursor; - use { - ReadBytesExt, WriteBytesExt, - BigEndian, NativeEndian, LittleEndian, - }; - use test::qc_sized; - - #[test] - fn big_endian() { - let max = ($max - 1) >> (8 * (8 - $bytes)); - fn prop(n: $ty_int) -> bool { - let mut wtr = vec![]; - wtr.$write::(n).unwrap(); - let mut rdr = Vec::new(); - rdr.extend(wtr[8 - $bytes..].iter().map(|&x|x)); - let mut rdr = Cursor::new(rdr); - n == rdr.$read::($bytes).unwrap() - } - qc_sized(prop as fn($ty_int) -> bool, max); - } - - #[test] - fn little_endian() { - let max = ($max - 1) >> (8 * (8 - $bytes)); - fn prop(n: $ty_int) -> bool { - let mut wtr = vec![]; - wtr.$write::(n).unwrap(); - let mut rdr = Cursor::new(wtr); - n == rdr.$read::($bytes).unwrap() - } - qc_sized(prop as fn($ty_int) -> bool, max); - } - - #[test] - fn native_endian() { - let max = ($max - 1) >> (8 * (8 - $bytes)); - fn prop(n: $ty_int) -> bool { - let mut wtr = vec![]; - wtr.$write::(n).unwrap(); - let mut rdr = Cursor::new(wtr); - n == rdr.$read::($bytes).unwrap() - } - qc_sized(prop as fn($ty_int) -> bool, max); - } - } - ); - ($name:ident, $ty_int:ident, $max:expr, $read:ident, $write:ident) => ( - mod $name { - use std::io::Cursor; - use { - ReadBytesExt, WriteBytesExt, - BigEndian, NativeEndian, LittleEndian, - }; - use test::qc_sized; - - #[test] - fn big_endian() { - fn prop(n: $ty_int) -> bool { - let mut wtr = vec![]; - wtr.$write::(n).unwrap(); - let mut rdr = Cursor::new(wtr); - n == rdr.$read::().unwrap() - } - qc_sized(prop as fn($ty_int) -> bool, $max - 1); - } - - #[test] - fn little_endian() { - fn prop(n: $ty_int) -> bool { - let mut wtr = vec![]; - wtr.$write::(n).unwrap(); - let mut rdr = Cursor::new(wtr); - n == rdr.$read::().unwrap() - } - qc_sized(prop as fn($ty_int) -> bool, $max - 1); - } - - #[test] - fn native_endian() { - fn prop(n: $ty_int) -> bool { - let mut wtr = vec![]; - wtr.$write::(n).unwrap(); - let mut rdr = Cursor::new(wtr); - n == rdr.$read::().unwrap() - } - qc_sized(prop as fn($ty_int) -> bool, $max - 1); - } - } - ); - } - - qc_bytes_ext!(prop_ext_u16, u16, ::std::u16::MAX as u64, read_u16, write_u16); - qc_bytes_ext!(prop_ext_i16, i16, ::std::i16::MAX as u64, read_i16, write_i16); - qc_bytes_ext!(prop_ext_u32, u32, ::std::u32::MAX as u64, read_u32, write_u32); - qc_bytes_ext!(prop_ext_i32, i32, ::std::i32::MAX as u64, read_i32, write_i32); - qc_bytes_ext!(prop_ext_u64, u64, ::std::u64::MAX as u64, read_u64, write_u64); - qc_bytes_ext!(prop_ext_i64, i64, ::std::i64::MAX as u64, read_i64, write_i64); - qc_bytes_ext!(prop_ext_f32, f32, ::std::u64::MAX as u64, read_f32, write_f32); - qc_bytes_ext!(prop_ext_f64, f64, ::std::i64::MAX as u64, read_f64, write_f64); - - qc_bytes_ext!(prop_ext_uint_1, u64, ::test::U64_MAX, 1, read_uint, write_u64); - qc_bytes_ext!(prop_ext_uint_2, u64, ::test::U64_MAX, 2, read_uint, write_u64); - qc_bytes_ext!(prop_ext_uint_3, u64, ::test::U64_MAX, 3, read_uint, write_u64); - qc_bytes_ext!(prop_ext_uint_4, u64, ::test::U64_MAX, 4, read_uint, write_u64); - qc_bytes_ext!(prop_ext_uint_5, u64, ::test::U64_MAX, 5, read_uint, write_u64); - qc_bytes_ext!(prop_ext_uint_6, u64, ::test::U64_MAX, 6, read_uint, write_u64); - qc_bytes_ext!(prop_ext_uint_7, u64, ::test::U64_MAX, 7, read_uint, write_u64); - qc_bytes_ext!(prop_ext_uint_8, u64, ::test::U64_MAX, 8, read_uint, write_u64); - - qc_bytes_ext!(prop_ext_int_1, i64, ::test::I64_MAX, 1, read_int, write_i64); - qc_bytes_ext!(prop_ext_int_2, i64, ::test::I64_MAX, 2, read_int, write_i64); - qc_bytes_ext!(prop_ext_int_3, i64, ::test::I64_MAX, 3, read_int, write_i64); - qc_bytes_ext!(prop_ext_int_4, i64, ::test::I64_MAX, 4, read_int, write_i64); - qc_bytes_ext!(prop_ext_int_5, i64, ::test::I64_MAX, 5, read_int, write_i64); - qc_bytes_ext!(prop_ext_int_6, i64, ::test::I64_MAX, 6, read_int, write_i64); - qc_bytes_ext!(prop_ext_int_7, i64, ::test::I64_MAX, 7, read_int, write_i64); - qc_bytes_ext!(prop_ext_int_8, i64, ::test::I64_MAX, 8, read_int, write_i64); -} - diff --git a/third_party/rust/byteorder-1.0.0/src/new.rs b/third_party/rust/byteorder-1.0.0/src/new.rs deleted file mode 100644 index b5f37700f960..000000000000 --- a/third_party/rust/byteorder-1.0.0/src/new.rs +++ /dev/null @@ -1,269 +0,0 @@ -use std::io::{self, Result}; - -use ByteOrder; - -/// Extends `Read` with methods for reading numbers. (For `std::io`.) -/// -/// Most of the methods defined here have an unconstrained type parameter that -/// must be explicitly instantiated. Typically, it is instantiated with either -/// the `BigEndian` or `LittleEndian` types defined in this crate. -/// -/// # Examples -/// -/// Read unsigned 16 bit big-endian integers from a `Read`: -/// -/// ```rust -/// use std::io::Cursor; -/// use byteorder::{BigEndian, ReadBytesExt}; -/// -/// let mut rdr = Cursor::new(vec![2, 5, 3, 0]); -/// assert_eq!(517, rdr.read_u16::().unwrap()); -/// assert_eq!(768, rdr.read_u16::().unwrap()); -/// ``` -pub trait ReadBytesExt: io::Read { - /// Reads an unsigned 8 bit integer from the underlying reader. - /// - /// Note that since this reads a single byte, no byte order conversions - /// are used. It is included for completeness. - #[inline] - fn read_u8(&mut self) -> Result { - let mut buf = [0; 1]; - try!(self.read_exact(&mut buf)); - Ok(buf[0]) - } - - /// Reads a signed 8 bit integer from the underlying reader. - /// - /// Note that since this reads a single byte, no byte order conversions - /// are used. It is included for completeness. - #[inline] - fn read_i8(&mut self) -> Result { - let mut buf = [0; 1]; - try!(self.read_exact(&mut buf)); - Ok(buf[0] as i8) - } - - /// Reads an unsigned 16 bit integer from the underlying reader. - #[inline] - fn read_u16(&mut self) -> Result { - let mut buf = [0; 2]; - try!(self.read_exact(&mut buf)); - Ok(T::read_u16(&buf)) - } - - /// Reads a signed 16 bit integer from the underlying reader. - #[inline] - fn read_i16(&mut self) -> Result { - let mut buf = [0; 2]; - try!(self.read_exact(&mut buf)); - Ok(T::read_i16(&buf)) - } - - /// Reads an unsigned 32 bit integer from the underlying reader. - #[inline] - fn read_u32(&mut self) -> Result { - let mut buf = [0; 4]; - try!(self.read_exact(&mut buf)); - Ok(T::read_u32(&buf)) - } - - /// Reads a signed 32 bit integer from the underlying reader. - #[inline] - fn read_i32(&mut self) -> Result { - let mut buf = [0; 4]; - try!(self.read_exact(&mut buf)); - Ok(T::read_i32(&buf)) - } - - /// Reads an unsigned 64 bit integer from the underlying reader. - #[inline] - fn read_u64(&mut self) -> Result { - let mut buf = [0; 8]; - try!(self.read_exact(&mut buf)); - Ok(T::read_u64(&buf)) - } - - /// Reads a signed 64 bit integer from the underlying reader. - #[inline] - fn read_i64(&mut self) -> Result { - let mut buf = [0; 8]; - try!(self.read_exact(&mut buf)); - Ok(T::read_i64(&buf)) - } - - /// Reads an unsigned n-bytes integer from the underlying reader. - #[inline] - fn read_uint(&mut self, nbytes: usize) -> Result { - let mut buf = [0; 8]; - try!(self.read_exact(&mut buf[..nbytes])); - Ok(T::read_uint(&buf[..nbytes], nbytes)) - } - - /// Reads a signed n-bytes integer from the underlying reader. - #[inline] - fn read_int(&mut self, nbytes: usize) -> Result { - let mut buf = [0; 8]; - try!(self.read_exact(&mut buf[..nbytes])); - Ok(T::read_int(&buf[..nbytes], nbytes)) - } - - /// Reads a IEEE754 single-precision (4 bytes) floating point number from - /// the underlying reader. - #[inline] - fn read_f32(&mut self) -> Result { - let mut buf = [0; 4]; - try!(self.read_exact(&mut buf)); - Ok(T::read_f32(&buf)) - } - - /// Reads a IEEE754 double-precision (8 bytes) floating point number from - /// the underlying reader. - #[inline] - fn read_f64(&mut self) -> Result { - let mut buf = [0; 8]; - try!(self.read_exact(&mut buf)); - Ok(T::read_f64(&buf)) - } -} - -/// All types that implement `Read` get methods defined in `ReadBytesExt` -/// for free. -impl ReadBytesExt for R {} - -/// Extends `Write` with methods for writing numbers. (For `std::io`.) -/// -/// Most of the methods defined here have an unconstrained type parameter that -/// must be explicitly instantiated. Typically, it is instantiated with either -/// the `BigEndian` or `LittleEndian` types defined in this crate. -/// -/// # Examples -/// -/// Write unsigned 16 bit big-endian integers to a `Write`: -/// -/// ```rust -/// use byteorder::{BigEndian, WriteBytesExt}; -/// -/// let mut wtr = vec![]; -/// wtr.write_u16::(517).unwrap(); -/// wtr.write_u16::(768).unwrap(); -/// assert_eq!(wtr, vec![2, 5, 3, 0]); -/// ``` -pub trait WriteBytesExt: io::Write { - /// Writes an unsigned 8 bit integer to the underlying writer. - /// - /// Note that since this writes a single byte, no byte order conversions - /// are used. It is included for completeness. - #[inline] - fn write_u8(&mut self, n: u8) -> Result<()> { - self.write_all(&[n]) - } - - /// Writes a signed 8 bit integer to the underlying writer. - /// - /// Note that since this writes a single byte, no byte order conversions - /// are used. It is included for completeness. - #[inline] - fn write_i8(&mut self, n: i8) -> Result<()> { - self.write_all(&[n as u8]) - } - - /// Writes an unsigned 16 bit integer to the underlying writer. - #[inline] - fn write_u16(&mut self, n: u16) -> Result<()> { - let mut buf = [0; 2]; - T::write_u16(&mut buf, n); - self.write_all(&buf) - } - - /// Writes a signed 16 bit integer to the underlying writer. - #[inline] - fn write_i16(&mut self, n: i16) -> Result<()> { - let mut buf = [0; 2]; - T::write_i16(&mut buf, n); - self.write_all(&buf) - } - - /// Writes an unsigned 32 bit integer to the underlying writer. - #[inline] - fn write_u32(&mut self, n: u32) -> Result<()> { - let mut buf = [0; 4]; - T::write_u32(&mut buf, n); - self.write_all(&buf) - } - - /// Writes a signed 32 bit integer to the underlying writer. - #[inline] - fn write_i32(&mut self, n: i32) -> Result<()> { - let mut buf = [0; 4]; - T::write_i32(&mut buf, n); - self.write_all(&buf) - } - - /// Writes an unsigned 64 bit integer to the underlying writer. - #[inline] - fn write_u64(&mut self, n: u64) -> Result<()> { - let mut buf = [0; 8]; - T::write_u64(&mut buf, n); - self.write_all(&buf) - } - - /// Writes a signed 64 bit integer to the underlying writer. - #[inline] - fn write_i64(&mut self, n: i64) -> Result<()> { - let mut buf = [0; 8]; - T::write_i64(&mut buf, n); - self.write_all(&buf) - } - - /// Writes an unsigned n-bytes integer to the underlying writer. - /// - /// If the given integer is not representable in the given number of bytes, - /// this method panics. If `nbytes > 8`, this method panics. - #[inline] - fn write_uint( - &mut self, - n: u64, - nbytes: usize, - ) -> Result<()> { - let mut buf = [0; 8]; - T::write_uint(&mut buf, n, nbytes); - self.write_all(&buf[0..nbytes]) - } - - /// Writes a signed n-bytes integer to the underlying writer. - /// - /// If the given integer is not representable in the given number of bytes, - /// this method panics. If `nbytes > 8`, this method panics. - #[inline] - fn write_int( - &mut self, - n: i64, - nbytes: usize, - ) -> Result<()> { - let mut buf = [0; 8]; - T::write_int(&mut buf, n, nbytes); - self.write_all(&buf[0..nbytes]) - } - - /// Writes a IEEE754 single-precision (4 bytes) floating point number to - /// the underlying writer. - #[inline] - fn write_f32(&mut self, n: f32) -> Result<()> { - let mut buf = [0; 4]; - T::write_f32(&mut buf, n); - self.write_all(&buf) - } - - /// Writes a IEEE754 double-precision (8 bytes) floating point number to - /// the underlying writer. - #[inline] - fn write_f64(&mut self, n: f64) -> Result<()> { - let mut buf = [0; 8]; - T::write_f64(&mut buf, n); - self.write_all(&buf) - } -} - -/// All types that implement `Write` get methods defined in `WriteBytesExt` -/// for free. -impl WriteBytesExt for W {} diff --git a/third_party/rust/byteorder-1.1.0/.cargo-checksum.json b/third_party/rust/byteorder-1.1.0/.cargo-checksum.json new file mode 100644 index 000000000000..666d7025c22e --- /dev/null +++ b/third_party/rust/byteorder-1.1.0/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".travis.yml":"a798200a7a2a7b499b4c44f0e516cd8975dc5f4b933144d1e2b1523087330b95","CHANGELOG.md":"caa17ca58eafcd282ed7b8bd47ef0670ce30a20aad339bf2758469f09fa4559d","COPYING":"01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f","Cargo.toml":"0e636678a02b111d1ebdfdc8ac5a835a2a41370028eeadf6d5fbfd3239f531f8","LICENSE-MIT":"0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f","README.md":"0559514b9d7488e96fb7a2f3c043a62fadf3495a1e10602d109ce79ee67da998","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","benches/bench.rs":"250d46461a0529a856d76a6421fd45c499bccba2c532e05dbf438c94582a8eac","src/io.rs":"5429f522221b3cce8a5c90fba7ad98b0ddad4fab44acc39763e6adabd511c021","src/lib.rs":"d31f218ca1892bea2812dd575ceb700779de704dc8bf1b9a778315ab75343c5d"},"package":"ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"} \ No newline at end of file diff --git a/third_party/rust/byteorder-1.1.0/.travis.yml b/third_party/rust/byteorder-1.1.0/.travis.yml new file mode 100644 index 000000000000..b8bb203a42f8 --- /dev/null +++ b/third_party/rust/byteorder-1.1.0/.travis.yml @@ -0,0 +1,22 @@ +language: rust +rust: + - 1.12.0 + - stable + - beta + - nightly +script: + - cargo build --verbose + - cargo doc + - cargo test --verbose + - cargo test --verbose --no-default-features --lib + - if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then + cargo test --verbose --features i128; + cargo test --verbose --no-default-features --features i128 --lib; + cargo bench --verbose --no-run; + cargo bench --verbose --no-run --no-default-features; + cargo bench --verbose --no-run --features i128; + cargo bench --verbose --no-run --no-default-features --features i128; + fi +branches: + only: + - master diff --git a/third_party/rust/byteorder-1.1.0/CHANGELOG.md b/third_party/rust/byteorder-1.1.0/CHANGELOG.md new file mode 100644 index 000000000000..0909791e782b --- /dev/null +++ b/third_party/rust/byteorder-1.1.0/CHANGELOG.md @@ -0,0 +1,36 @@ +1.1.0 +===== +This release of `byteorder` features a number of fixes and improvements, mostly +as a result of the +[Litz Blitz evaluation](https://public.etherpad-mozilla.org/p/rust-crate-eval-byteorder). + +Feature enhancements: + +* [FEATURE #63](https://github.com/BurntSushi/byteorder/issues/63): + Add methods for reading/writing slices of numbers for a specific + endianness. +* [FEATURE #65](https://github.com/BurntSushi/byteorder/issues/65): + Add support for `u128`/`i128` types. (Behind the nightly only `i128` + feature.) +* [FEATURE #72](https://github.com/BurntSushi/byteorder/issues/72): + Add "panics" and "errors" sections for each relevant public API item. +* [FEATURE #74](https://github.com/BurntSushi/byteorder/issues/74): + Add CI badges to Cargo.toml. +* [FEATURE #75](https://github.com/BurntSushi/byteorder/issues/75): + Add more examples to public API items. +* Add 24-bit read/write methods. +* Add `BE` and `LE` type aliases for `BigEndian` and `LittleEndian`, + respectively. + +Bug fixes: + +* [BUG #68](https://github.com/BurntSushi/byteorder/issues/68): + Panic in {BigEndian,LittleEndian}::default. +* [BUG #69](https://github.com/BurntSushi/byteorder/issues/69): + Seal the `ByteOrder` trait to prevent out-of-crate implementations. +* [BUG #71](https://github.com/BurntSushi/byteorder/issues/71): + Guarantee that the results of `read_f32`/`read_f64` are always defined. +* [BUG #73](https://github.com/BurntSushi/byteorder/issues/73): + Add crates.io categories. +* [BUG #77](https://github.com/BurntSushi/byteorder/issues/77): + Add `html_root` doc attribute. diff --git a/third_party/rust/byteorder-1.0.0/COPYING b/third_party/rust/byteorder-1.1.0/COPYING similarity index 100% rename from third_party/rust/byteorder-1.0.0/COPYING rename to third_party/rust/byteorder-1.1.0/COPYING diff --git a/third_party/rust/byteorder-1.1.0/Cargo.toml b/third_party/rust/byteorder-1.1.0/Cargo.toml new file mode 100644 index 000000000000..424fad8bff62 --- /dev/null +++ b/third_party/rust/byteorder-1.1.0/Cargo.toml @@ -0,0 +1,44 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g. crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "byteorder" +version = "1.1.0" +authors = ["Andrew Gallant "] +description = "Library for reading/writing numbers in big-endian and little-endian." +homepage = "https://github.com/BurntSushi/byteorder" +documentation = "https://docs.rs/byteorder" +readme = "README.md" +keywords = ["byte", "endian", "big-endian", "little-endian", "binary"] +categories = ["encoding", "parsing"] +license = "Unlicense/MIT" +repository = "https://github.com/BurntSushi/byteorder" +[profile.bench] +opt-level = 3 + +[lib] +name = "byteorder" +bench = false +[dev-dependencies.rand] +version = "0.3" + +[dev-dependencies.quickcheck] +version = "0.4" +default-features = false + +[features] +default = ["std"] +i128 = [] +std = [] +[badges.travis-ci] +branch = "master" +repository = "https://github.com/BurntSushi/byteorder" diff --git a/third_party/rust/byteorder-1.0.0/LICENSE-MIT b/third_party/rust/byteorder-1.1.0/LICENSE-MIT similarity index 100% rename from third_party/rust/byteorder-1.0.0/LICENSE-MIT rename to third_party/rust/byteorder-1.1.0/LICENSE-MIT diff --git a/third_party/rust/byteorder-1.0.0/README.md b/third_party/rust/byteorder-1.1.0/README.md similarity index 100% rename from third_party/rust/byteorder-1.0.0/README.md rename to third_party/rust/byteorder-1.1.0/README.md diff --git a/third_party/rust/byteorder-1.0.0/UNLICENSE b/third_party/rust/byteorder-1.1.0/UNLICENSE similarity index 100% rename from third_party/rust/byteorder-1.0.0/UNLICENSE rename to third_party/rust/byteorder-1.1.0/UNLICENSE diff --git a/third_party/rust/byteorder-1.1.0/benches/bench.rs b/third_party/rust/byteorder-1.1.0/benches/bench.rs new file mode 100644 index 000000000000..845e489f8869 --- /dev/null +++ b/third_party/rust/byteorder-1.1.0/benches/bench.rs @@ -0,0 +1,320 @@ +#![cfg_attr(feature = "i128", feature(i128))] +#![feature(test)] + +extern crate byteorder; +extern crate rand; +extern crate test; + +macro_rules! bench_num { + ($name:ident, $read:ident, $bytes:expr, $data:expr) => ( + mod $name { + use byteorder::{ByteOrder, BigEndian, NativeEndian, LittleEndian}; + use super::test::Bencher; + use super::test::black_box as bb; + + const NITER: usize = 100_000; + + #[bench] + fn read_big_endian(b: &mut Bencher) { + let buf = $data; + b.iter(|| { + for _ in 0..NITER { + bb(BigEndian::$read(&buf, $bytes)); + } + }); + } + + #[bench] + fn read_little_endian(b: &mut Bencher) { + let buf = $data; + b.iter(|| { + for _ in 0..NITER { + bb(LittleEndian::$read(&buf, $bytes)); + } + }); + } + + #[bench] + fn read_native_endian(b: &mut Bencher) { + let buf = $data; + b.iter(|| { + for _ in 0..NITER { + bb(NativeEndian::$read(&buf, $bytes)); + } + }); + } + } + ); + ($ty:ident, $max:ident, + $read:ident, $write:ident, $size:expr, $data:expr) => ( + mod $ty { + use std::$ty; + use byteorder::{ByteOrder, BigEndian, NativeEndian, LittleEndian}; + use super::test::Bencher; + use super::test::black_box as bb; + + const NITER: usize = 100_000; + + #[bench] + fn read_big_endian(b: &mut Bencher) { + let buf = $data; + b.iter(|| { + for _ in 0..NITER { + bb(BigEndian::$read(&buf)); + } + }); + } + + #[bench] + fn read_little_endian(b: &mut Bencher) { + let buf = $data; + b.iter(|| { + for _ in 0..NITER { + bb(LittleEndian::$read(&buf)); + } + }); + } + + #[bench] + fn read_native_endian(b: &mut Bencher) { + let buf = $data; + b.iter(|| { + for _ in 0..NITER { + bb(NativeEndian::$read(&buf)); + } + }); + } + + #[bench] + fn write_big_endian(b: &mut Bencher) { + let mut buf = $data; + let n = $ty::$max; + b.iter(|| { + for _ in 0..NITER { + bb(BigEndian::$write(&mut buf, n)); + } + }); + } + + #[bench] + fn write_little_endian(b: &mut Bencher) { + let mut buf = $data; + let n = $ty::$max; + b.iter(|| { + for _ in 0..NITER { + bb(LittleEndian::$write(&mut buf, n)); + } + }); + } + + #[bench] + fn write_native_endian(b: &mut Bencher) { + let mut buf = $data; + let n = $ty::$max; + b.iter(|| { + for _ in 0..NITER { + bb(NativeEndian::$write(&mut buf, n)); + } + }); + } + } + ); +} + +bench_num!(u16, MAX, read_u16, write_u16, 2, [1, 2]); +bench_num!(i16, MAX, read_i16, write_i16, 2, [1, 2]); +bench_num!(u32, MAX, read_u32, write_u32, 4, [1, 2, 3, 4]); +bench_num!(i32, MAX, read_i32, write_i32, 4, [1, 2, 3, 4]); +bench_num!(u64, MAX, read_u64, write_u64, 8, [1, 2, 3, 4, 5, 6, 7, 8]); +bench_num!(i64, MAX, read_i64, write_i64, 8, [1, 2, 3, 4, 5, 6, 7, 8]); +bench_num!(f32, MAX, read_f32, write_f32, 4, [1, 2, 3, 4]); +bench_num!(f64, MAX, read_f64, write_f64, 8, + [1, 2, 3, 4, 5, 6, 7, 8]); + +bench_num!(uint_1, read_uint, 1, [1]); +bench_num!(uint_2, read_uint, 2, [1, 2]); +bench_num!(uint_3, read_uint, 3, [1, 2, 3]); +bench_num!(uint_4, read_uint, 4, [1, 2, 3, 4]); +bench_num!(uint_5, read_uint, 5, [1, 2, 3, 4, 5]); +bench_num!(uint_6, read_uint, 6, [1, 2, 3, 4, 5, 6]); +bench_num!(uint_7, read_uint, 7, [1, 2, 3, 4, 5, 6, 7]); +bench_num!(uint_8, read_uint, 8, [1, 2, 3, 4, 5, 6, 7, 8]); + +bench_num!(int_1, read_int, 1, [1]); +bench_num!(int_2, read_int, 2, [1, 2]); +bench_num!(int_3, read_int, 3, [1, 2, 3]); +bench_num!(int_4, read_int, 4, [1, 2, 3, 4]); +bench_num!(int_5, read_int, 5, [1, 2, 3, 4, 5]); +bench_num!(int_6, read_int, 6, [1, 2, 3, 4, 5, 6]); +bench_num!(int_7, read_int, 7, [1, 2, 3, 4, 5, 6, 7]); +bench_num!(int_8, read_int, 8, [1, 2, 3, 4, 5, 6, 7, 8]); + +#[cfg(feature = "i128")] +bench_num!(u128, MAX, read_u128, write_u128, + 16, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); +#[cfg(feature = "i128")] +bench_num!(i128, MAX, read_i128, write_i128, + 16, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + +#[cfg(feature = "i128")] +bench_num!(uint128_1, read_uint128, + 1, [1]); +#[cfg(feature = "i128")] +bench_num!(uint128_2, read_uint128, + 2, [1, 2]); +#[cfg(feature = "i128")] +bench_num!(uint128_3, read_uint128, + 3, [1, 2, 3]); +#[cfg(feature = "i128")] +bench_num!(uint128_4, read_uint128, + 4, [1, 2, 3, 4]); +#[cfg(feature = "i128")] +bench_num!(uint128_5, read_uint128, + 5, [1, 2, 3, 4, 5]); +#[cfg(feature = "i128")] +bench_num!(uint128_6, read_uint128, + 6, [1, 2, 3, 4, 5, 6]); +#[cfg(feature = "i128")] +bench_num!(uint128_7, read_uint128, + 7, [1, 2, 3, 4, 5, 6, 7]); +#[cfg(feature = "i128")] +bench_num!(uint128_8, read_uint128, + 8, [1, 2, 3, 4, 5, 6, 7, 8]); +#[cfg(feature = "i128")] +bench_num!(uint128_9, read_uint128, + 9, [1, 2, 3, 4, 5, 6, 7, 8, 9]); +#[cfg(feature = "i128")] +bench_num!(uint128_10, read_uint128, + 10, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +#[cfg(feature = "i128")] +bench_num!(uint128_11, read_uint128, + 11, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); +#[cfg(feature = "i128")] +bench_num!(uint128_12, read_uint128, + 12, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); +#[cfg(feature = "i128")] +bench_num!(uint128_13, read_uint128, + 13, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); +#[cfg(feature = "i128")] +bench_num!(uint128_14, read_uint128, + 14, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]); +#[cfg(feature = "i128")] +bench_num!(uint128_15, read_uint128, + 15, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); +#[cfg(feature = "i128")] +bench_num!(uint128_16, read_uint128, + 16, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + +#[cfg(feature = "i128")] +bench_num!(int128_1, read_int128, + 1, [1]); +#[cfg(feature = "i128")] +bench_num!(int128_2, read_int128, + 2, [1, 2]); +#[cfg(feature = "i128")] +bench_num!(int128_3, read_int128, + 3, [1, 2, 3]); +#[cfg(feature = "i128")] +bench_num!(int128_4, read_int128, + 4, [1, 2, 3, 4]); +#[cfg(feature = "i128")] +bench_num!(int128_5, read_int128, + 5, [1, 2, 3, 4, 5]); +#[cfg(feature = "i128")] +bench_num!(int128_6, read_int128, + 6, [1, 2, 3, 4, 5, 6]); +#[cfg(feature = "i128")] +bench_num!(int128_7, read_int128, + 7, [1, 2, 3, 4, 5, 6, 7]); +#[cfg(feature = "i128")] +bench_num!(int128_8, read_int128, + 8, [1, 2, 3, 4, 5, 6, 7, 8]); +#[cfg(feature = "i128")] +bench_num!(int128_9, read_int128, + 9, [1, 2, 3, 4, 5, 6, 7, 8, 9]); +#[cfg(feature = "i128")] +bench_num!(int128_10, read_int128, + 10, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +#[cfg(feature = "i128")] +bench_num!(int128_11, read_int128, + 11, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); +#[cfg(feature = "i128")] +bench_num!(int128_12, read_int128, + 12, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); +#[cfg(feature = "i128")] +bench_num!(int128_13, read_int128, + 13, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); +#[cfg(feature = "i128")] +bench_num!(int128_14, read_int128, + 14, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]); +#[cfg(feature = "i128")] +bench_num!(int128_15, read_int128, + 15, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); +#[cfg(feature = "i128")] +bench_num!(int128_16, read_int128, + 16, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + + +macro_rules! bench_slice { + ($name:ident, $numty:ty, $read:ident, $write:ident) => { + mod $name { + use std::mem::size_of; + + use byteorder::{ByteOrder, BigEndian, LittleEndian}; + use rand::{self, Rng}; + use test::Bencher; + + #[bench] + fn read_big_endian(b: &mut Bencher) { + let mut numbers: Vec<$numty> = + rand::thread_rng().gen_iter().take(100000).collect(); + let mut bytes = vec![0; numbers.len() * size_of::<$numty>()]; + BigEndian::$write(&numbers, &mut bytes); + + b.bytes = bytes.len() as u64; + b.iter(|| { + BigEndian::$read(&bytes, &mut numbers); + }); + } + + #[bench] + fn read_little_endian(b: &mut Bencher) { + let mut numbers: Vec<$numty> = + rand::thread_rng().gen_iter().take(100000).collect(); + let mut bytes = vec![0; numbers.len() * size_of::<$numty>()]; + LittleEndian::$write(&numbers, &mut bytes); + + b.bytes = bytes.len() as u64; + b.iter(|| { + LittleEndian::$read(&bytes, &mut numbers); + }); + } + + #[bench] + fn write_big_endian(b: &mut Bencher) { + let numbers: Vec<$numty> = + rand::thread_rng().gen_iter().take(100000).collect(); + let mut bytes = vec![0; numbers.len() * size_of::<$numty>()]; + + b.bytes = bytes.len() as u64; + b.iter(|| { + BigEndian::$write(&numbers, &mut bytes); + }); + } + + #[bench] + fn write_little_endian(b: &mut Bencher) { + let numbers: Vec<$numty> = + rand::thread_rng().gen_iter().take(100000).collect(); + let mut bytes = vec![0; numbers.len() * size_of::<$numty>()]; + + b.bytes = bytes.len() as u64; + b.iter(|| { + LittleEndian::$write(&numbers, &mut bytes); + }); + } + } + } +} + +bench_slice!(slice_u64, u64, read_u64_into, write_u64_into); diff --git a/third_party/rust/byteorder-1.1.0/src/io.rs b/third_party/rust/byteorder-1.1.0/src/io.rs new file mode 100644 index 000000000000..208596ab18dd --- /dev/null +++ b/third_party/rust/byteorder-1.1.0/src/io.rs @@ -0,0 +1,1189 @@ +use std::io::{self, Result}; +use std::slice; +// use std::mem::transmute; + +use ByteOrder; + +/// Extends `Read` with methods for reading numbers. (For `std::io`.) +/// +/// Most of the methods defined here have an unconstrained type parameter that +/// must be explicitly instantiated. Typically, it is instantiated with either +/// the `BigEndian` or `LittleEndian` types defined in this crate. +/// +/// # Examples +/// +/// Read unsigned 16 bit big-endian integers from a `Read`: +/// +/// ```rust +/// use std::io::Cursor; +/// use byteorder::{BigEndian, ReadBytesExt}; +/// +/// let mut rdr = Cursor::new(vec![2, 5, 3, 0]); +/// assert_eq!(517, rdr.read_u16::().unwrap()); +/// assert_eq!(768, rdr.read_u16::().unwrap()); +/// ``` +pub trait ReadBytesExt: io::Read { + /// Reads an unsigned 8 bit integer from the underlying reader. + /// + /// Note that since this reads a single byte, no byte order conversions + /// are used. It is included for completeness. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read unsigned 8 bit integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![2, 5]); + /// assert_eq!(2, rdr.read_u8().unwrap()); + /// assert_eq!(5, rdr.read_u8().unwrap()); + /// ``` + #[inline] + fn read_u8(&mut self) -> Result { + let mut buf = [0; 1]; + try!(self.read_exact(&mut buf)); + Ok(buf[0]) + } + + /// Reads a signed 8 bit integer from the underlying reader. + /// + /// Note that since this reads a single byte, no byte order conversions + /// are used. It is included for completeness. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read unsigned 8 bit integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0x02, 0xfb]); + /// assert_eq!(2, rdr.read_i8().unwrap()); + /// assert_eq!(-5, rdr.read_i8().unwrap()); + /// ``` + #[inline] + fn read_i8(&mut self) -> Result { + let mut buf = [0; 1]; + try!(self.read_exact(&mut buf)); + Ok(buf[0] as i8) + } + + /// Reads an unsigned 16 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read unsigned 16 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![2, 5, 3, 0]); + /// assert_eq!(517, rdr.read_u16::().unwrap()); + /// assert_eq!(768, rdr.read_u16::().unwrap()); + /// ``` + #[inline] + fn read_u16(&mut self) -> Result { + let mut buf = [0; 2]; + try!(self.read_exact(&mut buf)); + Ok(T::read_u16(&buf)) + } + + /// Reads a signed 16 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read signed 16 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0x00, 0xc1, 0xff, 0x7c]); + /// assert_eq!(193, rdr.read_i16::().unwrap()); + /// assert_eq!(-132, rdr.read_i16::().unwrap()); + /// ``` + #[inline] + fn read_i16(&mut self) -> Result { + let mut buf = [0; 2]; + try!(self.read_exact(&mut buf)); + Ok(T::read_i16(&buf)) + } + + /// Reads an unsigned 24 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read unsigned 24 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0x00, 0x01, 0x0b]); + /// assert_eq!(267, rdr.read_u24::().unwrap()); + /// ``` + #[inline] + fn read_u24(&mut self) -> Result { + let mut buf = [0; 3]; + try!(self.read_exact(&mut buf)); + Ok(T::read_u24(&buf)) + } + + /// Reads a signed 24 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read signed 24 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0xff, 0x7a, 0x33]); + /// assert_eq!(-34253, rdr.read_i24::().unwrap()); + /// ``` + #[inline] + fn read_i24(&mut self) -> Result { + let mut buf = [0; 3]; + try!(self.read_exact(&mut buf)); + Ok(T::read_i24(&buf)) + } + + /// Reads an unsigned 32 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read unsigned 32 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0x00, 0x00, 0x01, 0x0b]); + /// assert_eq!(267, rdr.read_u32::().unwrap()); + /// ``` + #[inline] + fn read_u32(&mut self) -> Result { + let mut buf = [0; 4]; + try!(self.read_exact(&mut buf)); + Ok(T::read_u32(&buf)) + } + + /// Reads a signed 32 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read signed 32 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0xff, 0xff, 0x7a, 0x33]); + /// assert_eq!(-34253, rdr.read_i32::().unwrap()); + /// ``` + #[inline] + fn read_i32(&mut self) -> Result { + let mut buf = [0; 4]; + try!(self.read_exact(&mut buf)); + Ok(T::read_i32(&buf)) + } + + /// Reads an unsigned 64 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read an unsigned 64 bit big-endian integer from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83]); + /// assert_eq!(918733457491587, rdr.read_u64::().unwrap()); + /// ``` + #[inline] + fn read_u64(&mut self) -> Result { + let mut buf = [0; 8]; + try!(self.read_exact(&mut buf)); + Ok(T::read_u64(&buf)) + } + + /// Reads a signed 64 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a signed 64 bit big-endian integer from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0x80, 0, 0, 0, 0, 0, 0, 0]); + /// assert_eq!(i64::min_value(), rdr.read_i64::().unwrap()); + /// ``` + #[inline] + fn read_i64(&mut self) -> Result { + let mut buf = [0; 8]; + try!(self.read_exact(&mut buf)); + Ok(T::read_i64(&buf)) + } + + /// Reads an unsigned 128 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read an unsigned 128 bit big-endian integer from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83, + /// 0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83 + /// ]); + /// assert_eq!(16947640962301618749969007319746179, rdr.read_u128::().unwrap()); + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn read_u128(&mut self) -> Result { + let mut buf = [0; 16]; + try!(self.read_exact(&mut buf)); + Ok(T::read_u128(&buf)) + } + + /// Reads a signed 128 bit integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a signed 128 bit big-endian integer from a `Read`: + /// + /// ```rust + /// #![feature(i128_type)] + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + /// assert_eq!(i128::min_value(), rdr.read_i128::().unwrap()); + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn read_i128(&mut self) -> Result { + let mut buf = [0; 16]; + try!(self.read_exact(&mut buf)); + Ok(T::read_i128(&buf)) + } + + /// Reads an unsigned n-bytes integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read an unsigned n-byte big-endian integer from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0x80, 0x74, 0xfa]); + /// assert_eq!(8418554, rdr.read_uint::(3).unwrap()); + #[inline] + fn read_uint(&mut self, nbytes: usize) -> Result { + let mut buf = [0; 8]; + try!(self.read_exact(&mut buf[..nbytes])); + Ok(T::read_uint(&buf[..nbytes], nbytes)) + } + + /// Reads a signed n-bytes integer from the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read an unsigned n-byte big-endian integer from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0xc1, 0xff, 0x7c]); + /// assert_eq!(-4063364, rdr.read_int::(3).unwrap()); + #[inline] + fn read_int(&mut self, nbytes: usize) -> Result { + let mut buf = [0; 8]; + try!(self.read_exact(&mut buf[..nbytes])); + Ok(T::read_int(&buf[..nbytes], nbytes)) + } + + /// Reads an unsigned n-bytes integer from the underlying reader. + #[cfg(feature = "i128")] + #[inline] + fn read_uint128(&mut self, nbytes: usize) -> Result { + let mut buf = [0; 16]; + try!(self.read_exact(&mut buf[..nbytes])); + Ok(T::read_uint128(&buf[..nbytes], nbytes)) + } + + /// Reads a signed n-bytes integer from the underlying reader. + #[cfg(feature = "i128")] + #[inline] + fn read_int128(&mut self, nbytes: usize) -> Result { + let mut buf = [0; 16]; + try!(self.read_exact(&mut buf[..nbytes])); + Ok(T::read_int128(&buf[..nbytes], nbytes)) + } + + /// Reads a IEEE754 single-precision (4 bytes) floating point number from + /// the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a big-endian single-precision floating point number from a `Read`: + /// + /// ```rust + /// use std::f32; + /// use std::io::Cursor; + /// + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0x40, 0x49, 0x0f, 0xdb, + /// ]); + /// assert_eq!(f32::consts::PI, rdr.read_f32::().unwrap()); + /// ``` + #[inline] + fn read_f32(&mut self) -> Result { + let mut buf = [0; 4]; + try!(self.read_exact(&mut buf)); + Ok(T::read_f32(&buf)) + } + + /// Reads a IEEE754 double-precision (8 bytes) floating point number from + /// the underlying reader. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a big-endian double-precision floating point number from a `Read`: + /// + /// ```rust + /// use std::f64; + /// use std::io::Cursor; + /// + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18, + /// ]); + /// assert_eq!(f64::consts::PI, rdr.read_f64::().unwrap()); + /// ``` + #[inline] + fn read_f64(&mut self) -> Result { + let mut buf = [0; 8]; + try!(self.read_exact(&mut buf)); + Ok(T::read_f64(&buf)) + } + + /// Reads a sequence of unsigned 16 bit integers from the underlying + /// reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of unsigned 16 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![2, 5, 3, 0]); + /// let mut dst = [0; 2]; + /// rdr.read_u16_into::(&mut dst).unwrap(); + /// assert_eq!([517, 768], dst); + /// ``` + #[inline] + fn read_u16_into(&mut self, dst: &mut [u16]) -> Result<()> { + { + let mut buf = unsafe { slice_to_u8_mut(dst) }; + try!(self.read_exact(buf)); + } + T::from_slice_u16(dst); + Ok(()) + } + + /// Reads a sequence of unsigned 32 bit integers from the underlying + /// reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of unsigned 32 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0, 0, 2, 5, 0, 0, 3, 0]); + /// let mut dst = [0; 2]; + /// rdr.read_u32_into::(&mut dst).unwrap(); + /// assert_eq!([517, 768], dst); + /// ``` + #[inline] + fn read_u32_into(&mut self, dst: &mut [u32]) -> Result<()> { + { + let mut buf = unsafe { slice_to_u8_mut(dst) }; + try!(self.read_exact(buf)); + } + T::from_slice_u32(dst); + Ok(()) + } + + /// Reads a sequence of unsigned 64 bit integers from the underlying + /// reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of unsigned 64 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0, 0, 0, 0, 0, 0, 2, 5, + /// 0, 0, 0, 0, 0, 0, 3, 0, + /// ]); + /// let mut dst = [0; 2]; + /// rdr.read_u64_into::(&mut dst).unwrap(); + /// assert_eq!([517, 768], dst); + /// ``` + #[inline] + fn read_u64_into(&mut self, dst: &mut [u64]) -> Result<()> { + { + let mut buf = unsafe { slice_to_u8_mut(dst) }; + try!(self.read_exact(buf)); + } + T::from_slice_u64(dst); + Ok(()) + } + + /// Reads a sequence of unsigned 128 bit integers from the underlying + /// reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of unsigned 128 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, + /// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, + /// ]); + /// let mut dst = [0; 2]; + /// rdr.read_u128_into::(&mut dst).unwrap(); + /// assert_eq!([517, 768], dst); + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn read_u128_into( + &mut self, + dst: &mut [u128], + ) -> Result<()> { + { + let mut buf = unsafe { slice_to_u8_mut(dst) }; + try!(self.read_exact(buf)); + } + T::from_slice_u128(dst); + Ok(()) + } + + /// Reads a sequence of signed 16 bit integers from the underlying + /// reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of signed 16 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![2, 5, 3, 0]); + /// let mut dst = [0; 2]; + /// rdr.read_i16_into::(&mut dst).unwrap(); + /// assert_eq!([517, 768], dst); + /// ``` + #[inline] + fn read_i16_into(&mut self, dst: &mut [i16]) -> Result<()> { + { + let mut buf = unsafe { slice_to_u8_mut(dst) }; + try!(self.read_exact(buf)); + } + T::from_slice_i16(dst); + Ok(()) + } + + /// Reads a sequence of signed 32 bit integers from the underlying + /// reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of signed 32 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![0, 0, 2, 5, 0, 0, 3, 0]); + /// let mut dst = [0; 2]; + /// rdr.read_i32_into::(&mut dst).unwrap(); + /// assert_eq!([517, 768], dst); + /// ``` + #[inline] + fn read_i32_into(&mut self, dst: &mut [i32]) -> Result<()> { + { + let mut buf = unsafe { slice_to_u8_mut(dst) }; + try!(self.read_exact(buf)); + } + T::from_slice_i32(dst); + Ok(()) + } + + /// Reads a sequence of signed 64 bit integers from the underlying + /// reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of signed 64 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0, 0, 0, 0, 0, 0, 2, 5, + /// 0, 0, 0, 0, 0, 0, 3, 0, + /// ]); + /// let mut dst = [0; 2]; + /// rdr.read_i64_into::(&mut dst).unwrap(); + /// assert_eq!([517, 768], dst); + /// ``` + #[inline] + fn read_i64_into(&mut self, dst: &mut [i64]) -> Result<()> { + { + let mut buf = unsafe { slice_to_u8_mut(dst) }; + try!(self.read_exact(buf)); + } + T::from_slice_i64(dst); + Ok(()) + } + + /// Reads a sequence of signed 128 bit integers from the underlying + /// reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of signed 128 bit big-endian integers from a `Read`: + /// + /// ```rust + /// use std::io::Cursor; + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, + /// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, + /// ]); + /// let mut dst = [0; 2]; + /// rdr.read_i128_into::(&mut dst).unwrap(); + /// assert_eq!([517, 768], dst); + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn read_i128_into( + &mut self, + dst: &mut [i128], + ) -> Result<()> { + { + let mut buf = unsafe { slice_to_u8_mut(dst) }; + try!(self.read_exact(buf)); + } + T::from_slice_i128(dst); + Ok(()) + } + + /// Reads a sequence of IEEE754 single-precision (4 bytes) floating + /// point numbers from the underlying reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Safety + /// + /// This method is unsafe because there are no guarantees made about the + /// floating point values. In particular, this method does not check for + /// signaling NaNs, which may result in undefined behavior. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of big-endian single-precision floating point number + /// from a `Read`: + /// + /// ```rust + /// use std::f32; + /// use std::io::Cursor; + /// + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0x40, 0x49, 0x0f, 0xdb, + /// 0x3f, 0x80, 0x00, 0x00, + /// ]); + /// let mut dst = [0.0; 2]; + /// unsafe { + /// rdr.read_f32_into_unchecked::(&mut dst).unwrap(); + /// } + /// assert_eq!([f32::consts::PI, 1.0], dst); + /// ``` + #[inline] + unsafe fn read_f32_into_unchecked( + &mut self, + dst: &mut [f32], + ) -> Result<()> { + { + let mut buf = slice_to_u8_mut(dst); + try!(self.read_exact(buf)); + } + T::from_slice_f32(dst); + Ok(()) + } + + /// Reads a sequence of IEEE754 double-precision (8 bytes) floating + /// point numbers from the underlying reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Safety + /// + /// This method is unsafe because there are no guarantees made about the + /// floating point values. In particular, this method does not check for + /// signaling NaNs, which may result in undefined behavior. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of big-endian single-precision floating point number + /// from a `Read`: + /// + /// ```rust + /// use std::f64; + /// use std::io::Cursor; + /// + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18, + /// 0x3f, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// ]); + /// let mut dst = [0.0; 2]; + /// unsafe { + /// rdr.read_f64_into_unchecked::(&mut dst).unwrap(); + /// } + /// assert_eq!([f64::consts::PI, 1.0], dst); + /// ``` + #[inline] + unsafe fn read_f64_into_unchecked( + &mut self, + dst: &mut [f64], + ) -> Result<()> { + { + let mut buf = slice_to_u8_mut(dst); + try!(self.read_exact(buf)); + } + T::from_slice_f64(dst); + Ok(()) + } +} + +/// All types that implement `Read` get methods defined in `ReadBytesExt` +/// for free. +impl ReadBytesExt for R {} + +/// Extends `Write` with methods for writing numbers. (For `std::io`.) +/// +/// Most of the methods defined here have an unconstrained type parameter that +/// must be explicitly instantiated. Typically, it is instantiated with either +/// the `BigEndian` or `LittleEndian` types defined in this crate. +/// +/// # Examples +/// +/// Write unsigned 16 bit big-endian integers to a `Write`: +/// +/// ```rust +/// use byteorder::{BigEndian, WriteBytesExt}; +/// +/// let mut wtr = vec![]; +/// wtr.write_u16::(517).unwrap(); +/// wtr.write_u16::(768).unwrap(); +/// assert_eq!(wtr, vec![2, 5, 3, 0]); +/// ``` +pub trait WriteBytesExt: io::Write { + /// Writes an unsigned 8 bit integer to the underlying writer. + /// + /// Note that since this writes a single byte, no byte order conversions + /// are used. It is included for completeness. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_u8(&mut self, n: u8) -> Result<()> { + self.write_all(&[n]) + } + + /// Writes a signed 8 bit integer to the underlying writer. + /// + /// Note that since this writes a single byte, no byte order conversions + /// are used. It is included for completeness. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_i8(&mut self, n: i8) -> Result<()> { + self.write_all(&[n as u8]) + } + + /// Writes an unsigned 16 bit integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_u16(&mut self, n: u16) -> Result<()> { + let mut buf = [0; 2]; + T::write_u16(&mut buf, n); + self.write_all(&buf) + } + + /// Writes a signed 16 bit integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_i16(&mut self, n: i16) -> Result<()> { + let mut buf = [0; 2]; + T::write_i16(&mut buf, n); + self.write_all(&buf) + } + + /// Writes an unsigned 24 bit integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_u24(&mut self, n: u32) -> Result<()> { + let mut buf = [0; 3]; + T::write_u24(&mut buf, n); + self.write_all(&buf) + } + + /// Writes a signed 24 bit integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_i24(&mut self, n: i32) -> Result<()> { + let mut buf = [0; 3]; + T::write_i24(&mut buf, n); + self.write_all(&buf) + } + + /// Writes an unsigned 32 bit integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_u32(&mut self, n: u32) -> Result<()> { + let mut buf = [0; 4]; + T::write_u32(&mut buf, n); + self.write_all(&buf) + } + + /// Writes a signed 32 bit integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_i32(&mut self, n: i32) -> Result<()> { + let mut buf = [0; 4]; + T::write_i32(&mut buf, n); + self.write_all(&buf) + } + + /// Writes an unsigned 64 bit integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_u64(&mut self, n: u64) -> Result<()> { + let mut buf = [0; 8]; + T::write_u64(&mut buf, n); + self.write_all(&buf) + } + + /// Writes a signed 64 bit integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_i64(&mut self, n: i64) -> Result<()> { + let mut buf = [0; 8]; + T::write_i64(&mut buf, n); + self.write_all(&buf) + } + + /// Writes an unsigned 128 bit integer to the underlying writer. + #[cfg(feature = "i128")] + #[inline] + fn write_u128(&mut self, n: u128) -> Result<()> { + let mut buf = [0; 16]; + T::write_u128(&mut buf, n); + self.write_all(&buf) + } + + /// Writes a signed 128 bit integer to the underlying writer. + #[cfg(feature = "i128")] + #[inline] + fn write_i128(&mut self, n: i128) -> Result<()> { + let mut buf = [0; 16]; + T::write_i128(&mut buf, n); + self.write_all(&buf) + } + + /// Writes an unsigned n-bytes integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + /// + /// # Panics + /// + /// If the given integer is not representable in the given number of bytes, + /// this method panics. If `nbytes > 8`, this method panics. + #[inline] + fn write_uint( + &mut self, + n: u64, + nbytes: usize, + ) -> Result<()> { + let mut buf = [0; 8]; + T::write_uint(&mut buf, n, nbytes); + self.write_all(&buf[0..nbytes]) + } + + /// Writes a signed n-bytes integer to the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + /// + /// # Panics + /// + /// If the given integer is not representable in the given number of bytes, + /// this method panics. If `nbytes > 8`, this method panics. + #[inline] + fn write_int( + &mut self, + n: i64, + nbytes: usize, + ) -> Result<()> { + let mut buf = [0; 8]; + T::write_int(&mut buf, n, nbytes); + self.write_all(&buf[0..nbytes]) + } + + /// Writes an unsigned n-bytes integer to the underlying writer. + /// + /// If the given integer is not representable in the given number of bytes, + /// this method panics. If `nbytes > 16`, this method panics. + #[cfg(feature = "i128")] + #[inline] + fn write_uint128( + &mut self, + n: u128, + nbytes: usize, + ) -> Result<()> { + let mut buf = [0; 16]; + T::write_uint128(&mut buf, n, nbytes); + self.write_all(&buf[0..nbytes]) + } + + /// Writes a signed n-bytes integer to the underlying writer. + /// + /// If the given integer is not representable in the given number of bytes, + /// this method panics. If `nbytes > 16`, this method panics. + #[cfg(feature = "i128")] + #[inline] + fn write_int128( + &mut self, + n: i128, + nbytes: usize, + ) -> Result<()> { + let mut buf = [0; 16]; + T::write_int128(&mut buf, n, nbytes); + self.write_all(&buf[0..nbytes]) + } + + /// Writes a IEEE754 single-precision (4 bytes) floating point number to + /// the underlying writer. + /// + /// # Errors + /// + /// This method returns the same errors as [`Write::write_all`]. + /// + /// [`Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all + #[inline] + fn write_f32(&mut self, n: f32) -> Result<()> { + let mut buf = [0; 4]; + T::write_f32(&mut buf, n); + self.write_all(&buf) + } + + /// Writes a IEEE754 double-precision (8 bytes) floating point number to + /// the underlying writer. + #[inline] + fn write_f64(&mut self, n: f64) -> Result<()> { + let mut buf = [0; 8]; + T::write_f64(&mut buf, n); + self.write_all(&buf) + } +} + +/// All types that implement `Write` get methods defined in `WriteBytesExt` +/// for free. +impl WriteBytesExt for W {} + +/// Convert a slice of T (where T is plain old data) to its mutable binary +/// representation. +/// +/// This function is wildly unsafe because it permits arbitrary modification of +/// the binary representation of any `Copy` type. Use with care. +unsafe fn slice_to_u8_mut(slice: &mut [T]) -> &mut [u8] { + use std::mem::size_of; + + let len = size_of::() * slice.len(); + slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, len) +} diff --git a/third_party/rust/byteorder-1.1.0/src/lib.rs b/third_party/rust/byteorder-1.1.0/src/lib.rs new file mode 100644 index 000000000000..b60fe7ca4c0f --- /dev/null +++ b/third_party/rust/byteorder-1.1.0/src/lib.rs @@ -0,0 +1,3191 @@ +/*! +This crate provides convenience methods for encoding and decoding numbers +in either big-endian or little-endian order. + +The organization of the crate is pretty simple. A trait, `ByteOrder`, specifies +byte conversion methods for each type of number in Rust (sans numbers that have +a platform dependent size like `usize` and `isize`). Two types, `BigEndian` +and `LittleEndian` implement these methods. Finally, `ReadBytesExt` and +`WriteBytesExt` provide convenience methods available to all types that +implement `Read` and `Write`. + +# Examples + +Read unsigned 16 bit big-endian integers from a `Read` type: + +```rust +use std::io::Cursor; +use byteorder::{BigEndian, ReadBytesExt}; + +let mut rdr = Cursor::new(vec![2, 5, 3, 0]); +// Note that we use type parameters to indicate which kind of byte order +// we want! +assert_eq!(517, rdr.read_u16::().unwrap()); +assert_eq!(768, rdr.read_u16::().unwrap()); +``` + +Write unsigned 16 bit little-endian integers to a `Write` type: + +```rust +use byteorder::{LittleEndian, WriteBytesExt}; + +let mut wtr = vec![]; +wtr.write_u16::(517).unwrap(); +wtr.write_u16::(768).unwrap(); +assert_eq!(wtr, vec![5, 2, 0, 3]); +``` +*/ + +#![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(feature = "i128", feature(i128_type))] +#![cfg_attr(all(feature = "i128", test), feature(i128))] +#![doc(html_root_url = "https://docs.rs/byteorder/1.0.0")] + +#[cfg(feature = "std")] +extern crate core; + +use core::fmt::Debug; +use core::hash::Hash; +use core::mem::transmute; +use core::ptr::copy_nonoverlapping; + +#[cfg(feature = "std")] +pub use io::{ReadBytesExt, WriteBytesExt}; + +#[cfg(feature = "std")] +mod io; + +#[inline] +fn extend_sign(val: u64, nbytes: usize) -> i64 { + let shift = (8 - nbytes) * 8; + (val << shift) as i64 >> shift +} + +#[cfg(feature = "i128")] +#[inline] +fn extend_sign128(val: u128, nbytes: usize) -> i128 { + let shift = (16 - nbytes) * 8; + (val << shift) as i128 >> shift +} + +#[inline] +fn unextend_sign(val: i64, nbytes: usize) -> u64 { + let shift = (8 - nbytes) * 8; + (val << shift) as u64 >> shift +} + +#[cfg(feature = "i128")] +#[inline] +fn unextend_sign128(val: i128, nbytes: usize) -> u128 { + let shift = (16 - nbytes) * 8; + (val << shift) as u128 >> shift +} + +#[inline] +fn pack_size(n: u64) -> usize { + if n < 1 << 8 { + 1 + } else if n < 1 << 16 { + 2 + } else if n < 1 << 24 { + 3 + } else if n < 1 << 32 { + 4 + } else if n < 1 << 40 { + 5 + } else if n < 1 << 48 { + 6 + } else if n < 1 << 56 { + 7 + } else { + 8 + } +} + +#[cfg(feature = "i128")] +#[inline] +fn pack_size128(n: u128) -> usize { + if n < 1 << 8 { + 1 + } else if n < 1 << 16 { + 2 + } else if n < 1 << 24 { + 3 + } else if n < 1 << 32 { + 4 + } else if n < 1 << 40 { + 5 + } else if n < 1 << 48 { + 6 + } else if n < 1 << 56 { + 7 + } else if n < 1 << 64 { + 8 + } else if n < 1 << 72 { + 9 + } else if n < 1 << 80 { + 10 + } else if n < 1 << 88 { + 11 + } else if n < 1 << 96 { + 12 + } else if n < 1 << 104 { + 13 + } else if n < 1 << 112 { + 14 + } else if n < 1 << 120 { + 15 + } else { + 16 + } +} + +mod private { + /// Sealed stops crates other than byteorder from implementing any traits + /// that use it. + pub trait Sealed{} + impl Sealed for super::LittleEndian {} + impl Sealed for super::BigEndian {} +} + +/// ByteOrder describes types that can serialize integers as bytes. +/// +/// Note that `Self` does not appear anywhere in this trait's definition! +/// Therefore, in order to use it, you'll need to use syntax like +/// `T::read_u16(&[0, 1])` where `T` implements `ByteOrder`. +/// +/// This crate provides two types that implement `ByteOrder`: `BigEndian` +/// and `LittleEndian`. +/// This trait is sealed and cannot be implemented for callers to avoid +/// breaking backwards compatibility when adding new derived traits. +/// +/// # Examples +/// +/// Write and read `u32` numbers in little endian order: +/// +/// ```rust +/// use byteorder::{ByteOrder, LittleEndian}; +/// +/// let mut buf = [0; 4]; +/// LittleEndian::write_u32(&mut buf, 1_000_000); +/// assert_eq!(1_000_000, LittleEndian::read_u32(&buf)); +/// ``` +/// +/// Write and read `i16` numbers in big endian order: +/// +/// ```rust +/// use byteorder::{ByteOrder, BigEndian}; +/// +/// let mut buf = [0; 2]; +/// BigEndian::write_i16(&mut buf, -50_000); +/// assert_eq!(-50_000, BigEndian::read_i16(&buf)); +/// ``` +pub trait ByteOrder + : Clone + Copy + Debug + Default + Eq + Hash + Ord + PartialEq + PartialOrd + + private::Sealed +{ + /// Reads an unsigned 16 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 2`. + fn read_u16(buf: &[u8]) -> u16; + + /// Reads an unsigned 24 bit integer from `buf`, stored in u32. + /// + /// # Panics + /// + /// Panics when `buf.len() < 3`. + /// + /// # Examples + /// + /// Write and read 24 bit `u32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_u24(&mut buf, 1_000_000); + /// assert_eq!(1_000_000, LittleEndian::read_u24(&buf)); + /// ``` + fn read_u24(buf: &[u8]) -> u32 { + Self::read_uint(buf, 3) as u32 + } + + /// Reads an unsigned 32 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 4`. + /// + /// # Examples + /// + /// Write and read `u32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 4]; + /// LittleEndian::write_u32(&mut buf, 1_000_000); + /// assert_eq!(1_000_000, LittleEndian::read_u32(&buf)); + /// ``` + fn read_u32(buf: &[u8]) -> u32; + + /// Reads an unsigned 64 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + /// + /// # Examples + /// + /// Write and read `u64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 8]; + /// LittleEndian::write_u64(&mut buf, 1_000_000); + /// assert_eq!(1_000_000, LittleEndian::read_u64(&buf)); + /// ``` + fn read_u64(buf: &[u8]) -> u64; + + /// Reads an unsigned 128 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 16`. + /// + /// # Examples + /// + /// Write and read `u128` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 16]; + /// LittleEndian::write_u128(&mut buf, 1_000_000); + /// assert_eq!(1_000_000, LittleEndian::read_u128(&buf)); + /// ``` + #[cfg(feature = "i128")] + fn read_u128(buf: &[u8]) -> u128; + + /// Reads an unsigned n-bytes integer from `buf`. + /// + /// # Panics + /// + /// Panics when `nbytes < 1` or `nbytes > 8` or + /// `buf.len() < nbytes` + /// + /// # Examples + /// + /// Write and read an n-byte number in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_uint(&mut buf, 1_000_000, 3); + /// assert_eq!(1_000_000, LittleEndian::read_uint(&buf, 3)); + /// ``` + fn read_uint(buf: &[u8], nbytes: usize) -> u64; + + /// Reads an unsigned n-bytes integer from `buf`. + /// + /// # Panics + /// + /// Panics when `nbytes < 1` or `nbytes > 16` or + /// `buf.len() < nbytes` + /// + /// # Examples + /// + /// Write and read an n-byte number in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_uint128(&mut buf, 1_000_000, 3); + /// assert_eq!(1_000_000, LittleEndian::read_uint128(&buf, 3)); + /// ``` + #[cfg(feature = "i128")] + fn read_uint128(buf: &[u8], nbytes: usize) -> u128; + + /// Writes an unsigned 16 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 2`. + /// + /// # Examples + /// + /// Write and read `u16` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 2]; + /// LittleEndian::write_u16(&mut buf, 1_000_000); + /// assert_eq!(1_000_000, LittleEndian::read_u16(&buf)); + /// ``` + fn write_u16(buf: &mut [u8], n: u16); + + /// Writes an unsigned 24 bit integer `n` to `buf`, stored in u32. + /// + /// # Panics + /// + /// Panics when `buf.len() < 3`. + /// + /// # Examples + /// + /// Write and read 24 bit `u32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_u24(&mut buf, 1_000_000); + /// assert_eq!(1_000_000, LittleEndian::read_u24(&buf)); + /// ``` + fn write_u24(buf: &mut [u8], n: u32) { + Self::write_uint(buf, n as u64, 3) + } + + /// Writes an unsigned 32 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 4`. + /// + /// # Examples + /// + /// Write and read `u32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 4]; + /// LittleEndian::write_u32(&mut buf, 1_000_000); + /// assert_eq!(1_000_000, LittleEndian::read_u32(&buf)); + /// ``` + fn write_u32(buf: &mut [u8], n: u32); + + /// Writes an unsigned 64 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + /// + /// # Examples + /// + /// Write and read `u64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 8]; + /// LittleEndian::write_u64(&mut buf, 1_000_000); + /// assert_eq!(1_000_000, LittleEndian::read_u64(&buf)); + /// ``` + fn write_u64(buf: &mut [u8], n: u64); + + /// Writes an unsigned 128 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 16`. + /// + /// # Examples + /// + /// Write and read `u128` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 16]; + /// LittleEndian::write_u128(&mut buf, 1_000_000); + /// assert_eq!(1_000_000, LittleEndian::read_u128(&buf)); + /// ``` + #[cfg(feature = "i128")] + fn write_u128(buf: &mut [u8], n: u128); + + /// Writes an unsigned integer `n` to `buf` using only `nbytes`. + /// + /// # Panics + /// + /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 8`, then + /// this method panics. + /// + /// # Examples + /// + /// Write and read an n-byte number in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_uint(&mut buf, 1_000_000, 3); + /// assert_eq!(1_000_000, LittleEndian::read_uint(&buf, 3)); + /// ``` + fn write_uint(buf: &mut [u8], n: u64, nbytes: usize); + + /// Writes an unsigned integer `n` to `buf` using only `nbytes`. + /// + /// # Panics + /// + /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 16`, then + /// this method panics. + /// + /// # Examples + /// + /// Write and read an n-byte number in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_uint128(&mut buf, 1_000_000, 3); + /// assert_eq!(1_000_000, LittleEndian::read_uint128(&buf, 3)); + /// ``` + #[cfg(feature = "i128")] + fn write_uint128(buf: &mut [u8], n: u128, nbytes: usize); + + /// Reads a signed 16 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 2`. + /// + /// # Examples + /// + /// Write and read `i16` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 2]; + /// LittleEndian::write_i16(&mut buf, -1_000); + /// assert_eq!(-1_000, LittleEndian::read_i16(&buf)); + /// ``` + #[inline] + fn read_i16(buf: &[u8]) -> i16 { + Self::read_u16(buf) as i16 + } + + /// Reads a signed 24 bit integer from `buf`, stored in i32. + /// + /// # Panics + /// + /// Panics when `buf.len() < 3`. + /// + /// # Examples + /// + /// Write and read 24 bit `i32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_i24(&mut buf, -1_000_000); + /// assert_eq!(-1_000_000, LittleEndian::read_i24(&buf)); + /// ``` + #[inline] + fn read_i24(buf: &[u8]) -> i32 { + Self::read_int(buf, 3) as i32 + } + + /// Reads a signed 32 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 4`. + /// + /// # Examples + /// + /// Write and read `i32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 4]; + /// LittleEndian::write_i32(&mut buf, -1_000_000); + /// assert_eq!(-1_000_000, LittleEndian::read_i32(&buf)); + /// ``` + #[inline] + fn read_i32(buf: &[u8]) -> i32 { + Self::read_u32(buf) as i32 + } + + /// Reads a signed 64 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + /// + /// # Examples + /// + /// Write and read `i64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 8]; + /// LittleEndian::write_i64(&mut buf, -1_000_000_000); + /// assert_eq!(-1_000_000_000, LittleEndian::read_i64(&buf)); + /// ``` + #[inline] + fn read_i64(buf: &[u8]) -> i64 { + Self::read_u64(buf) as i64 + } + + /// Reads a signed 128 bit integer from `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 16`. + /// + /// # Examples + /// + /// Write and read `i128` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 16]; + /// LittleEndian::write_i128(&mut buf, -1_000_000_000); + /// assert_eq!(-1_000_000_000, LittleEndian::read_i128(&buf)); + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn read_i128(buf: &[u8]) -> i128 { + Self::read_u128(buf) as i128 + } + + /// Reads a signed n-bytes integer from `buf`. + /// + /// # Panics + /// + /// Panics when `nbytes < 1` or `nbytes > 8` or + /// `buf.len() < nbytes` + /// + /// # Examples + /// + /// Write and read n-length signed numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_int(&mut buf, -1_000, 3); + /// assert_eq!(-1_000, LittleEndian::read_int(&buf, 3)); + /// ``` + #[inline] + fn read_int(buf: &[u8], nbytes: usize) -> i64 { + extend_sign(Self::read_uint(buf, nbytes), nbytes) + } + + /// Reads a signed n-bytes integer from `buf`. + /// + /// # Panics + /// + /// Panics when `nbytes < 1` or `nbytes > 16` or + /// `buf.len() < nbytes` + /// + /// # Examples + /// + /// Write and read n-length signed numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_int128(&mut buf, -1_000, 3); + /// assert_eq!(-1_000, LittleEndian::read_int128(&buf, 3)); + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn read_int128(buf: &[u8], nbytes: usize) -> i128 { + extend_sign128(Self::read_uint128(buf, nbytes), nbytes) + } + + /// Reads a IEEE754 single-precision (4 bytes) floating point number. + /// + /// The return value is always defined; signaling NaN's may be turned into + /// quiet NaN's. + /// + /// # Panics + /// + /// Panics when `buf.len() < 4`. + /// + /// # Examples + /// + /// Write and read `f32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let e = 2.71828; + /// let mut buf = [0; 4]; + /// LittleEndian::write_f32(&mut buf, e); + /// assert_eq!(e, LittleEndian::read_f32(&buf)); + /// ``` + #[inline] + fn read_f32(buf: &[u8]) -> f32 { + safe_u32_bits_to_f32(Self::read_u32(buf)) + } + + /// Reads a IEEE754 double-precision (8 bytes) floating point number. + /// + /// The return value is always defined; signaling NaN's may be turned into + /// quiet NaN's. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + /// + /// # Examples + /// + /// Write and read `f64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let phi = 1.6180339887; + /// let mut buf = [0; 8]; + /// LittleEndian::write_f64(&mut buf, phi); + /// assert_eq!(phi, LittleEndian::read_f64(&buf)); + /// ``` + #[inline] + fn read_f64(buf: &[u8]) -> f64 { + safe_u64_bits_to_f64(Self::read_u64(buf)) + } + + /// Writes a signed 16 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 2`. + /// + /// # Examples + /// + /// Write and read `i16` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 2]; + /// LittleEndian::write_i16(&mut buf, -1_000); + /// assert_eq!(-1_000, LittleEndian::read_i16(&buf)); + /// ``` + #[inline] + fn write_i16(buf: &mut [u8], n: i16) { + Self::write_u16(buf, n as u16) + } + + /// Writes a signed 24 bit integer `n` to `buf`, stored in i32. + /// + /// # Panics + /// + /// Panics when `buf.len() < 3`. + /// + /// # Examples + /// + /// Write and read 24 bit `i32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_i24(&mut buf, -1_000_000); + /// assert_eq!(-1_000_000, LittleEndian::read_i24(&buf)); + /// ``` + #[inline] + fn write_i24(buf: &mut [u8], n: i32) { + Self::write_int(buf, n as i64, 3) + } + + /// Writes a signed 32 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 4`. + /// + /// # Examples + /// + /// Write and read `i32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 4]; + /// LittleEndian::write_i32(&mut buf, -1_000_000); + /// assert_eq!(-1_000_000, LittleEndian::read_i32(&buf)); + /// ``` + #[inline] + fn write_i32(buf: &mut [u8], n: i32) { + Self::write_u32(buf, n as u32) + } + + /// Writes a signed 64 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + /// + /// # Examples + /// + /// Write and read `i64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 8]; + /// LittleEndian::write_i64(&mut buf, -1_000_000_000); + /// assert_eq!(-1_000_000_000, LittleEndian::read_i64(&buf)); + /// ``` + #[inline] + fn write_i64(buf: &mut [u8], n: i64) { + Self::write_u64(buf, n as u64) + } + + /// Writes a signed 128 bit integer `n` to `buf`. + /// + /// # Panics + /// + /// Panics when `buf.len() < 16`. + /// + /// # Examples + /// + /// Write and read n-byte `i128` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 16]; + /// LittleEndian::write_i128(&mut buf, -1_000_000_000); + /// assert_eq!(-1_000_000_000, LittleEndian::read_i128(&buf)); + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn write_i128(buf: &mut [u8], n: i128) { + Self::write_u128(buf, n as u128) + } + + /// Writes a signed integer `n` to `buf` using only `nbytes`. + /// + /// # Panics + /// + /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 8`, then + /// this method panics. + /// + /// # Examples + /// + /// Write and read an n-byte number in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_int(&mut buf, -1_000, 3); + /// assert_eq!(-1_000, LittleEndian::read_int(&buf, 3)); + /// ``` + #[inline] + fn write_int(buf: &mut [u8], n: i64, nbytes: usize) { + Self::write_uint(buf, unextend_sign(n, nbytes), nbytes) + } + + /// Writes a signed integer `n` to `buf` using only `nbytes`. + /// + /// # Panics + /// + /// If `n` is not representable in `nbytes`, or if `nbytes` is `> 16`, then + /// this method panics. + /// + /// # Examples + /// + /// Write and read n-length signed numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut buf = [0; 3]; + /// LittleEndian::write_int128(&mut buf, -1_000, 3); + /// assert_eq!(-1_000, LittleEndian::read_int128(&buf, 3)); + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn write_int128(buf: &mut [u8], n: i128, nbytes: usize) { + Self::write_uint128(buf, unextend_sign128(n, nbytes), nbytes) + } + + /// Writes a IEEE754 single-precision (4 bytes) floating point number. + /// + /// # Panics + /// + /// Panics when `buf.len() < 4`. + /// + /// # Examples + /// + /// Write and read `f32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let e = 2.71828; + /// let mut buf = [0; 4]; + /// LittleEndian::write_f32(&mut buf, e); + /// assert_eq!(e, LittleEndian::read_f32(&buf)); + /// ``` + #[inline] + fn write_f32(buf: &mut [u8], n: f32) { + Self::write_u32(buf, unsafe { transmute(n) }) + } + + /// Writes a IEEE754 double-precision (8 bytes) floating point number. + /// + /// # Panics + /// + /// Panics when `buf.len() < 8`. + /// + /// # Examples + /// + /// Write and read `f64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let phi = 1.6180339887; + /// let mut buf = [0; 8]; + /// LittleEndian::write_f64(&mut buf, phi); + /// assert_eq!(phi, LittleEndian::read_f64(&buf)); + /// ``` + #[inline] + fn write_f64(buf: &mut [u8], n: f64) { + Self::write_u64(buf, unsafe { transmute(n) }) + } + + /// Reads unsigned 16 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `src.len() != 2*dst.len()`. + /// + /// # Examples + /// + /// Write and read `u16` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 8]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_u16_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_u16_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn read_u16_into(src: &[u8], dst: &mut [u16]); + + /// Reads unsigned 32 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `src.len() != 4*dst.len()`. + /// + /// # Examples + /// + /// Write and read `u32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 16]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_u32_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_u32_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn read_u32_into(src: &[u8], dst: &mut [u32]); + + /// Reads unsigned 64 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `src.len() != 8*dst.len()`. + /// + /// # Examples + /// + /// Write and read `u64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 32]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_u64_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_u64_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn read_u64_into(src: &[u8], dst: &mut [u64]); + + /// Reads unsigned 128 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `src.len() != 16*dst.len()`. + /// + /// # Examples + /// + /// Write and read `u128` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 64]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_u128_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_u128_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + #[cfg(feature = "i128")] + fn read_u128_into(src: &[u8], dst: &mut [u128]); + + /// Reads signed 16 bit integers from `src` to `dst`. + /// + /// # Panics + /// + /// Panics when `buf.len() != 2*dst.len()`. + /// + /// # Examples + /// + /// Write and read `i16` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 8]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_i16_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_i16_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + #[inline] + fn read_i16_into(src: &[u8], dst: &mut [i16]) { + Self::read_u16_into(src, unsafe { transmute(dst) }); + } + + /// Reads signed 32 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `src.len() != 4*dst.len()`. + /// + /// # Examples + /// + /// Write and read `i32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 16]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_i32_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_i32_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + #[inline] + fn read_i32_into(src: &[u8], dst: &mut [i32]) { + Self::read_u32_into(src, unsafe { transmute(dst) }); + } + + /// Reads signed 64 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `src.len() != 8*dst.len()`. + /// + /// # Examples + /// + /// Write and read `i64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 32]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_i64_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_i64_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + #[inline] + fn read_i64_into(src: &[u8], dst: &mut [i64]) { + Self::read_u64_into(src, unsafe { transmute(dst) }); + } + + /// Reads signed 128 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `src.len() != 16*dst.len()`. + /// + /// # Examples + /// + /// Write and read `i128` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 64]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_i128_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_i128_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn read_i128_into(src: &[u8], dst: &mut [i128]) { + Self::read_u128_into(src, unsafe { transmute(dst) }); + } + + /// Reads IEEE754 single-precision (4 bytes) floating point numbers from + /// `src` into `dst`. + /// + /// Note that this does not perform any checks on the floating point + /// conversion. In particular, if the `src` data encodes an undefined + /// floating point value for your environment, then the result may be + /// undefined behavior. For example, this function may produce signaling + /// NaN floating point values. + /// + /// # Panics + /// + /// Panics when `src.len() != 4*dst.len()`. + /// + /// # Examples + /// + /// Write and read `f32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 16]; + /// let numbers_given = [1.0, 2.0, 31.312e311, -11.32e91]; + /// LittleEndian::write_f32_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0.0; 4]; + /// unsafe { + /// LittleEndian::read_f32_into_unchecked(&bytes, &mut numbers_got); + /// } + /// assert_eq!(numbers_given, numbers_got); + /// ``` + #[inline] + unsafe fn read_f32_into_unchecked(src: &[u8], dst: &mut [f32]) { + Self::read_u32_into(src, transmute(dst)); + } + + /// Reads IEEE754 single-precision (4 bytes) floating point numbers from + /// `src` into `dst`. + /// + /// Note that this does not perform any checks on the floating point + /// conversion. In particular, if the `src` data encodes an undefined + /// floating point value for your environment, then the result may be + /// undefined behavior. For example, this function may produce signaling + /// NaN floating point values. + /// + /// # Panics + /// + /// Panics when `src.len() != 8*dst.len()`. + /// + /// # Examples + /// + /// Write and read `f64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 32]; + /// let numbers_given = [1.0, 2.0, 31.312e311, -11.32e91]; + /// LittleEndian::write_f64_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0.0; 4]; + /// unsafe { + /// LittleEndian::read_f64_into_unchecked(&bytes, &mut numbers_got); + /// } + /// assert_eq!(numbers_given, numbers_got); + /// ``` + #[inline] + unsafe fn read_f64_into_unchecked(src: &[u8], dst: &mut [f64]) { + Self::read_u64_into(src, transmute(dst)); + } + + /// Writes unsigned 16 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `dst.len() != 2*src.len()`. + /// + /// # Examples + /// + /// Write and read `u16` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 8]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_u16_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_u16_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn write_u16_into(src: &[u16], dst: &mut [u8]); + + /// Writes unsigned 32 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `dst.len() != 4*src.len()`. + /// + /// # Examples + /// + /// Write and read `u32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 16]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_u32_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_u32_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn write_u32_into(src: &[u32], dst: &mut [u8]); + + /// Writes unsigned 64 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `dst.len() != 8*src.len()`. + /// + /// # Examples + /// + /// Write and read `u64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 32]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_u64_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_u64_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn write_u64_into(src: &[u64], dst: &mut [u8]); + + /// Writes unsigned 128 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `dst.len() != 16*src.len()`. + /// + /// # Examples + /// + /// Write and read `u128` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 64]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_u128_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_u128_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + #[cfg(feature = "i128")] + fn write_u128_into(src: &[u128], dst: &mut [u8]); + + /// Writes signed 16 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `buf.len() != 2*src.len()`. + /// + /// # Examples + /// + /// Write and read `i16` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 8]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_i16_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_i16_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn write_i16_into(src: &[i16], dst: &mut [u8]) { + Self::write_u16_into(unsafe { transmute(src) }, dst); + } + + /// Writes signed 32 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `dst.len() != 4*src.len()`. + /// + /// # Examples + /// + /// Write and read `i32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 16]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_i32_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_i32_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn write_i32_into(src: &[i32], dst: &mut [u8]) { + Self::write_u32_into(unsafe { transmute(src) }, dst); + } + + /// Writes signed 64 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `dst.len() != 8*src.len()`. + /// + /// # Examples + /// + /// Write and read `i64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 32]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_i64_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_i64_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn write_i64_into(src: &[i64], dst: &mut [u8]) { + Self::write_u64_into(unsafe { transmute(src) }, dst); + } + + /// Writes signed 128 bit integers from `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `dst.len() != 16*src.len()`. + /// + /// # Examples + /// + /// Write and read `i128` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 64]; + /// let numbers_given = [1, 2, 0xf00f, 0xffee]; + /// LittleEndian::write_i128_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0; 4]; + /// LittleEndian::read_i128_into(&bytes, &mut numbers_got); + /// assert_eq!(numbers_given, numbers_got); + /// ``` + #[cfg(feature = "i128")] + fn write_i128_into(src: &[i128], dst: &mut [u8]) { + Self::write_u128_into(unsafe { transmute(src) }, dst); + } + + /// Writes IEEE754 single-precision (4 bytes) floating point numbers from + /// `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `src.len() != 4*dst.len()`. + /// + /// # Examples + /// + /// Write and read `f32` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 16]; + /// let numbers_given = [1.0, 2.0, 31.312e311, -11.32e91]; + /// LittleEndian::write_f32_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0.0; 4]; + /// unsafe { + /// LittleEndian::read_f32_into_unchecked(&bytes, &mut numbers_got); + /// } + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn write_f32_into(src: &[f32], dst: &mut [u8]) { + Self::write_u32_into(unsafe { transmute(src) }, dst); + } + + /// Writes IEEE754 double-precision (8 bytes) floating point numbers from + /// `src` into `dst`. + /// + /// # Panics + /// + /// Panics when `src.len() != 8*dst.len()`. + /// + /// # Examples + /// + /// Write and read `f64` numbers in little endian order: + /// + /// ```rust + /// use byteorder::{ByteOrder, LittleEndian}; + /// + /// let mut bytes = [0; 32]; + /// let numbers_given = [1.0, 2.0, 31.312e311, -11.32e91]; + /// LittleEndian::write_f64_into(&numbers_given, &mut bytes); + /// + /// let mut numbers_got = [0.0; 4]; + /// unsafe { + /// LittleEndian::read_f64_into_unchecked(&bytes, &mut numbers_got); + /// } + /// assert_eq!(numbers_given, numbers_got); + /// ``` + fn write_f64_into(src: &[f64], dst: &mut [u8]) { + Self::write_u64_into(unsafe { transmute(src) }, dst); + } + + /// Converts the given slice of unsigned 16 bit integers to a particular + /// endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// # Examples + /// + /// Convert the host platform's endianness to big-endian: + /// + /// ```rust + /// use byteorder::{ByteOrder, BigEndian}; + /// + /// let mut numbers = [5, 65000]; + /// BigEndian::from_slice_u16(&mut numbers); + /// if cfg!(target_endian = "little") { + /// assert_eq!(numbers, [5u16.swap_bytes(), 65000u16.swap_bytes()]); + /// } else { + /// assert_eq!(numbers, [5, 65000]); + /// } + /// ``` + fn from_slice_u16(numbers: &mut [u16]); + + /// Converts the given slice of unsigned 32 bit integers to a particular + /// endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// # Examples + /// + /// Convert the host platform's endianness to big-endian: + /// + /// ```rust + /// use byteorder::{ByteOrder, BigEndian}; + /// + /// let mut numbers = [5, 65000]; + /// BigEndian::from_slice_u32(&mut numbers); + /// if cfg!(target_endian = "little") { + /// assert_eq!(numbers, [5u32.swap_bytes(), 65000u32.swap_bytes()]); + /// } else { + /// assert_eq!(numbers, [5, 65000]); + /// } + /// ``` + fn from_slice_u32(numbers: &mut [u32]); + + /// Converts the given slice of unsigned 64 bit integers to a particular + /// endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// # Examples + /// + /// Convert the host platform's endianness to big-endian: + /// + /// ```rust + /// use byteorder::{ByteOrder, BigEndian}; + /// + /// let mut numbers = [5, 65000]; + /// BigEndian::from_slice_u64(&mut numbers); + /// if cfg!(target_endian = "little") { + /// assert_eq!(numbers, [5u64.swap_bytes(), 65000u64.swap_bytes()]); + /// } else { + /// assert_eq!(numbers, [5, 65000]); + /// } + /// ``` + fn from_slice_u64(numbers: &mut [u64]); + + /// Converts the given slice of unsigned 128 bit integers to a particular + /// endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// # Examples + /// + /// Convert the host platform's endianness to big-endian: + /// + /// ```rust + /// #![feature(i128_type)] + /// + /// use byteorder::{ByteOrder, BigEndian}; + /// + /// let mut numbers = [5, 65000]; + /// BigEndian::from_slice_u128(&mut numbers); + /// if cfg!(target_endian = "little") { + /// assert_eq!(numbers, [5u128.swap_bytes(), 65000u128.swap_bytes()]); + /// } else { + /// assert_eq!(numbers, [5, 65000]); + /// } + /// ``` + #[cfg(feature = "i128")] + fn from_slice_u128(numbers: &mut [u128]); + + /// Converts the given slice of signed 16 bit integers to a particular + /// endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// # Examples + /// + /// Convert the host platform's endianness to big-endian: + /// + /// ```rust + /// use byteorder::{ByteOrder, BigEndian}; + /// + /// let mut numbers = [5, 65000]; + /// BigEndian::from_slice_i16(&mut numbers); + /// if cfg!(target_endian = "little") { + /// assert_eq!(numbers, [5i16.swap_bytes(), 65000i16.swap_bytes()]); + /// } else { + /// assert_eq!(numbers, [5, 65000]); + /// } + /// ``` + #[inline] + fn from_slice_i16(numbers: &mut [i16]) { + Self::from_slice_u16(unsafe { transmute(numbers) }); + } + + /// Converts the given slice of signed 32 bit integers to a particular + /// endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// # Examples + /// + /// Convert the host platform's endianness to big-endian: + /// + /// ```rust + /// use byteorder::{ByteOrder, BigEndian}; + /// + /// let mut numbers = [5, 65000]; + /// BigEndian::from_slice_i32(&mut numbers); + /// if cfg!(target_endian = "little") { + /// assert_eq!(numbers, [5i32.swap_bytes(), 65000i32.swap_bytes()]); + /// } else { + /// assert_eq!(numbers, [5, 65000]); + /// } + /// ``` + #[inline] + fn from_slice_i32(numbers: &mut [i32]) { + Self::from_slice_u32(unsafe { transmute(numbers) }); + } + + /// Converts the given slice of signed 64 bit integers to a particular + /// endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// # Examples + /// + /// Convert the host platform's endianness to big-endian: + /// + /// ```rust + /// use byteorder::{ByteOrder, BigEndian}; + /// + /// let mut numbers = [5, 65000]; + /// BigEndian::from_slice_i64(&mut numbers); + /// if cfg!(target_endian = "little") { + /// assert_eq!(numbers, [5i64.swap_bytes(), 65000i64.swap_bytes()]); + /// } else { + /// assert_eq!(numbers, [5, 65000]); + /// } + /// ``` + #[inline] + fn from_slice_i64(numbers: &mut [i64]) { + Self::from_slice_u64(unsafe { transmute(numbers) }); + } + + /// Converts the given slice of signed 128 bit integers to a particular + /// endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// # Examples + /// + /// Convert the host platform's endianness to big-endian: + /// + /// ```rust + /// #![feature(i128_type)] + /// + /// use byteorder::{ByteOrder, BigEndian}; + /// + /// let mut numbers = [5, 65000]; + /// BigEndian::from_slice_i128(&mut numbers); + /// if cfg!(target_endian = "little") { + /// assert_eq!(numbers, [5i128.swap_bytes(), 65000i128.swap_bytes()]); + /// } else { + /// assert_eq!(numbers, [5, 65000]); + /// } + /// ``` + #[cfg(feature = "i128")] + #[inline] + fn from_slice_i128(numbers: &mut [i128]) { + Self::from_slice_u128(unsafe { transmute(numbers) }); + } + + /// Converts the given slice of IEEE754 single-precision (4 bytes) floating + /// point numbers to a particular endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// Note that the results of this operation are guaranteed to be defined. + /// In particular, this method may replace signaling NaN values with + /// quiet NaN values. + fn from_slice_f32(numbers: &mut [f32]); + + /// Converts the given slice of IEEE754 double-precision (8 bytes) floating + /// point numbers to a particular endianness. + /// + /// If the endianness matches the endianness of the host platform, then + /// this is a no-op. + /// + /// Note that the results of this operation are guaranteed to be defined. + /// In particular, this method may replace signaling NaN values with + /// quiet NaN values. + fn from_slice_f64(numbers: &mut [f64]); +} + +/// Defines big-endian serialization. +/// +/// Note that this type has no value constructor. It is used purely at the +/// type level. +/// +/// # Examples +/// +/// Write and read `u32` numbers in big endian order: +/// +/// ```rust +/// use byteorder::{ByteOrder, BigEndian}; +/// +/// let mut buf = [0; 4]; +/// BigEndian::write_u32(&mut buf, 1_000_000); +/// assert_eq!(1_000_000, BigEndian::read_u32(&buf)); +/// ``` +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum BigEndian {} + +impl Default for BigEndian { + fn default() -> BigEndian { + panic!("BigEndian default") + } +} + +/// A type alias for `BigEndian`. +pub type BE = BigEndian; + +/// Defines little-endian serialization. +/// +/// Note that this type has no value constructor. It is used purely at the +/// type level. +/// +/// # Examples +/// +/// Write and read `u32` numbers in little endian order: +/// +/// ```rust +/// use byteorder::{ByteOrder, LittleEndian}; +/// +/// let mut buf = [0; 4]; +/// LittleEndian::write_u32(&mut buf, 1_000_000); +/// assert_eq!(1_000_000, LittleEndian::read_u32(&buf)); +/// ``` +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum LittleEndian {} + +impl Default for LittleEndian { + fn default() -> LittleEndian { + panic!("LittleEndian default") + } +} + +/// A type alias for `LittleEndian`. +pub type LE = LittleEndian; + +/// Defines network byte order serialization. +/// +/// Network byte order is defined by [RFC 1700][1] to be big-endian, and is +/// referred to in several protocol specifications. This type is an alias of +/// BigEndian. +/// +/// [1]: https://tools.ietf.org/html/rfc1700 +/// +/// Note that this type has no value constructor. It is used purely at the +/// type level. +/// +/// # Examples +/// +/// Write and read `i16` numbers in big endian order: +/// +/// ```rust +/// use byteorder::{ByteOrder, NetworkEndian, BigEndian}; +/// +/// let mut buf = [0; 2]; +/// BigEndian::write_i16(&mut buf, -50_000); +/// assert_eq!(-50_000, NetworkEndian::read_i16(&buf)); +/// ``` +pub type NetworkEndian = BigEndian; + +/// Defines system native-endian serialization. +/// +/// Note that this type has no value constructor. It is used purely at the +/// type level. +#[cfg(target_endian = "little")] +pub type NativeEndian = LittleEndian; + +/// Defines system native-endian serialization. +/// +/// Note that this type has no value constructor. It is used purely at the +/// type level. +#[cfg(target_endian = "big")] +pub type NativeEndian = BigEndian; + +macro_rules! read_num_bytes { + ($ty:ty, $size:expr, $src:expr, $which:ident) => ({ + assert!($size == ::core::mem::size_of::<$ty>()); + assert!($size <= $src.len()); + let mut data: $ty = 0; + unsafe { + copy_nonoverlapping( + $src.as_ptr(), + &mut data as *mut $ty as *mut u8, + $size); + } + data.$which() + }); +} + +macro_rules! write_num_bytes { + ($ty:ty, $size:expr, $n:expr, $dst:expr, $which:ident) => ({ + assert!($size <= $dst.len()); + unsafe { + // N.B. https://github.com/rust-lang/rust/issues/22776 + let bytes = transmute::<_, [u8; $size]>($n.$which()); + copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size); + } + }); +} + +macro_rules! read_slice { + ($src:expr, $dst:expr, $size:expr, $which:ident) => {{ + assert_eq!($src.len(), $size * $dst.len()); + + unsafe { + copy_nonoverlapping( + $src.as_ptr(), + $dst.as_mut_ptr() as *mut u8, + $src.len()); + } + for v in $dst.iter_mut() { + *v = v.$which(); + } + }}; +} + +macro_rules! write_slice_native { + ($src:expr, $dst:expr, $ty:ty, $size:expr) => {{ + assert!($size == ::core::mem::size_of::<$ty>()); + assert_eq!($size * $src.len(), $dst.len()); + + unsafe { + copy_nonoverlapping( + $src.as_ptr() as *const u8, + $dst.as_mut_ptr(), + $dst.len()); + } + }}; +} + +macro_rules! write_slice { + ($src:expr, $dst:expr, $ty:ty, $size:expr, $write:expr) => ({ + assert!($size == ::core::mem::size_of::<$ty>()); + assert_eq!($size * $src.len(), $dst.len()); + + for (&n, chunk) in $src.iter().zip($dst.chunks_mut($size)) { + $write(chunk, n); + } + }); +} + +impl ByteOrder for BigEndian { + #[inline] + fn read_u16(buf: &[u8]) -> u16 { + read_num_bytes!(u16, 2, buf, to_be) + } + + #[inline] + fn read_u32(buf: &[u8]) -> u32 { + read_num_bytes!(u32, 4, buf, to_be) + } + + #[inline] + fn read_u64(buf: &[u8]) -> u64 { + read_num_bytes!(u64, 8, buf, to_be) + } + + #[cfg(feature = "i128")] + #[inline] + fn read_u128(buf: &[u8]) -> u128 { + read_num_bytes!(u128, 16, buf, to_be) + } + + #[inline] + fn read_uint(buf: &[u8], nbytes: usize) -> u64 { + assert!(1 <= nbytes && nbytes <= 8 && nbytes <= buf.len()); + let mut out = [0u8; 8]; + let ptr_out = out.as_mut_ptr(); + unsafe { + copy_nonoverlapping( + buf.as_ptr(), ptr_out.offset((8 - nbytes) as isize), nbytes); + (*(ptr_out as *const u64)).to_be() + } + } + + #[cfg(feature = "i128")] + #[inline] + fn read_uint128(buf: &[u8], nbytes: usize) -> u128 { + assert!(1 <= nbytes && nbytes <= 16 && nbytes <= buf.len()); + let mut out = [0u8; 16]; + let ptr_out = out.as_mut_ptr(); + unsafe { + copy_nonoverlapping( + buf.as_ptr(), ptr_out.offset((16 - nbytes) as isize), nbytes); + (*(ptr_out as *const u128)).to_be() + } + } + + #[inline] + fn write_u16(buf: &mut [u8], n: u16) { + write_num_bytes!(u16, 2, n, buf, to_be); + } + + #[inline] + fn write_u32(buf: &mut [u8], n: u32) { + write_num_bytes!(u32, 4, n, buf, to_be); + } + + #[inline] + fn write_u64(buf: &mut [u8], n: u64) { + write_num_bytes!(u64, 8, n, buf, to_be); + } + + #[cfg(feature = "i128")] + #[inline] + fn write_u128(buf: &mut [u8], n: u128) { + write_num_bytes!(u128, 16, n, buf, to_be); + } + + #[inline] + fn write_uint(buf: &mut [u8], n: u64, nbytes: usize) { + assert!(pack_size(n) <= nbytes && nbytes <= 8); + assert!(nbytes <= buf.len()); + unsafe { + let bytes: [u8; 8] = transmute(n.to_be()); + copy_nonoverlapping( + bytes.as_ptr().offset((8 - nbytes) as isize), + buf.as_mut_ptr(), + nbytes); + } + } + + #[cfg(feature = "i128")] + #[inline] + fn write_uint128(buf: &mut [u8], n: u128, nbytes: usize) { + assert!(pack_size128(n) <= nbytes && nbytes <= 16); + assert!(nbytes <= buf.len()); + unsafe { + let bytes: [u8; 16] = transmute(n.to_be()); + copy_nonoverlapping( + bytes.as_ptr().offset((16 - nbytes) as isize), + buf.as_mut_ptr(), + nbytes); + } + } + + #[inline] + fn read_u16_into(src: &[u8], dst: &mut [u16]) { + read_slice!(src, dst, 2, to_be); + } + + #[inline] + fn read_u32_into(src: &[u8], dst: &mut [u32]) { + read_slice!(src, dst, 4, to_be); + } + + #[inline] + fn read_u64_into(src: &[u8], dst: &mut [u64]) { + read_slice!(src, dst, 8, to_be); + } + + #[cfg(feature = "i128")] + #[inline] + fn read_u128_into(src: &[u8], dst: &mut [u128]) { + read_slice!(src, dst, 16, to_be); + } + + #[inline] + fn write_u16_into(src: &[u16], dst: &mut [u8]) { + if cfg!(target_endian = "big") { + write_slice_native!(src, dst, u16, 2); + } else { + write_slice!(src, dst, u16, 2, Self::write_u16); + } + } + + #[inline] + fn write_u32_into(src: &[u32], dst: &mut [u8]) { + if cfg!(target_endian = "big") { + write_slice_native!(src, dst, u32, 4); + } else { + write_slice!(src, dst, u32, 4, Self::write_u32); + } + } + + #[inline] + fn write_u64_into(src: &[u64], dst: &mut [u8]) { + if cfg!(target_endian = "big") { + write_slice_native!(src, dst, u64, 8); + } else { + write_slice!(src, dst, u64, 8, Self::write_u64); + } + } + + #[cfg(feature = "i128")] + #[inline] + fn write_u128_into(src: &[u128], dst: &mut [u8]) { + if cfg!(target_endian = "big") { + write_slice_native!(src, dst, u128, 16); + } else { + write_slice!(src, dst, u128, 16, Self::write_u128); + } + } + + #[inline] + fn from_slice_u16(numbers: &mut [u16]) { + if cfg!(target_endian = "little") { + for n in numbers { + *n = n.to_be(); + } + } + } + + #[inline] + fn from_slice_u32(numbers: &mut [u32]) { + if cfg!(target_endian = "little") { + for n in numbers { + *n = n.to_be(); + } + } + } + + #[inline] + fn from_slice_u64(numbers: &mut [u64]) { + if cfg!(target_endian = "little") { + for n in numbers { + *n = n.to_be(); + } + } + } + + #[cfg(feature = "i128")] + #[inline] + fn from_slice_u128(numbers: &mut [u128]) { + if cfg!(target_endian = "little") { + for n in numbers { + *n = n.to_be(); + } + } + } + + #[inline] + fn from_slice_f32(numbers: &mut [f32]) { + if cfg!(target_endian = "little") { + for n in numbers { + let int: u32 = unsafe { transmute(*n) }; + *n = safe_u32_bits_to_f32(int.to_be()); + } + } + } + + #[inline] + fn from_slice_f64(numbers: &mut [f64]) { + if cfg!(target_endian = "little") { + for n in numbers { + let int: u64 = unsafe { transmute(*n) }; + *n = safe_u64_bits_to_f64(int.to_be()); + } + } + } +} + +impl ByteOrder for LittleEndian { + #[inline] + fn read_u16(buf: &[u8]) -> u16 { + read_num_bytes!(u16, 2, buf, to_le) + } + + #[inline] + fn read_u32(buf: &[u8]) -> u32 { + read_num_bytes!(u32, 4, buf, to_le) + } + + #[inline] + fn read_u64(buf: &[u8]) -> u64 { + read_num_bytes!(u64, 8, buf, to_le) + } + + #[cfg(feature = "i128")] + #[inline] + fn read_u128(buf: &[u8]) -> u128 { + read_num_bytes!(u128, 16, buf, to_le) + } + + #[inline] + fn read_uint(buf: &[u8], nbytes: usize) -> u64 { + assert!(1 <= nbytes && nbytes <= 8 && nbytes <= buf.len()); + let mut out = [0u8; 8]; + let ptr_out = out.as_mut_ptr(); + unsafe { + copy_nonoverlapping(buf.as_ptr(), ptr_out, nbytes); + (*(ptr_out as *const u64)).to_le() + } + } + + #[cfg(feature = "i128")] + #[inline] + fn read_uint128(buf: &[u8], nbytes: usize) -> u128 { + assert!(1 <= nbytes && nbytes <= 16 && nbytes <= buf.len()); + let mut out = [0u8; 16]; + let ptr_out = out.as_mut_ptr(); + unsafe { + copy_nonoverlapping(buf.as_ptr(), ptr_out, nbytes); + (*(ptr_out as *const u128)).to_le() + } + } + + #[inline] + fn write_u16(buf: &mut [u8], n: u16) { + write_num_bytes!(u16, 2, n, buf, to_le); + } + + #[inline] + fn write_u32(buf: &mut [u8], n: u32) { + write_num_bytes!(u32, 4, n, buf, to_le); + } + + #[inline] + fn write_u64(buf: &mut [u8], n: u64) { + write_num_bytes!(u64, 8, n, buf, to_le); + } + + #[cfg(feature = "i128")] + #[inline] + fn write_u128(buf: &mut [u8], n: u128) { + write_num_bytes!(u128, 16, n, buf, to_le); + } + + #[inline] + fn write_uint(buf: &mut [u8], n: u64, nbytes: usize) { + assert!(pack_size(n as u64) <= nbytes && nbytes <= 8); + assert!(nbytes <= buf.len()); + unsafe { + let bytes: [u8; 8] = transmute(n.to_le()); + copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr(), nbytes); + } + } + + #[cfg(feature = "i128")] + #[inline] + fn write_uint128(buf: &mut [u8], n: u128, nbytes: usize) { + assert!(pack_size128(n as u128) <= nbytes && nbytes <= 16); + assert!(nbytes <= buf.len()); + unsafe { + let bytes: [u8; 16] = transmute(n.to_le()); + copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr(), nbytes); + } + } + + #[inline] + fn read_u16_into(src: &[u8], dst: &mut [u16]) { + read_slice!(src, dst, 2, to_le); + } + + #[inline] + fn read_u32_into(src: &[u8], dst: &mut [u32]) { + read_slice!(src, dst, 4, to_le); + } + + #[inline] + fn read_u64_into(src: &[u8], dst: &mut [u64]) { + read_slice!(src, dst, 8, to_le); + } + + #[cfg(feature = "i128")] + #[inline] + fn read_u128_into(src: &[u8], dst: &mut [u128]) { + read_slice!(src, dst, 16, to_le); + } + + #[inline] + fn write_u16_into(src: &[u16], dst: &mut [u8]) { + if cfg!(target_endian = "little") { + write_slice_native!(src, dst, u16, 2); + } else { + write_slice!(src, dst, u16, 2, Self::write_u16); + } + } + + #[inline] + fn write_u32_into(src: &[u32], dst: &mut [u8]) { + if cfg!(target_endian = "little") { + write_slice_native!(src, dst, u32, 4); + } else { + write_slice!(src, dst, u32, 4, Self::write_u32); + } + } + + #[inline] + fn write_u64_into(src: &[u64], dst: &mut [u8]) { + if cfg!(target_endian = "little") { + write_slice_native!(src, dst, u64, 8); + } else { + write_slice!(src, dst, u64, 8, Self::write_u64); + } + } + + #[cfg(feature = "i128")] + #[inline] + fn write_u128_into(src: &[u128], dst: &mut [u8]) { + if cfg!(target_endian = "little") { + write_slice_native!(src, dst, u128, 16); + } else { + write_slice!(src, dst, u128, 16, Self::write_u128); + } + } + + #[inline] + fn from_slice_u16(numbers: &mut [u16]) { + if cfg!(target_endian = "big") { + for n in numbers { + *n = n.to_le(); + } + } + } + + #[inline] + fn from_slice_u32(numbers: &mut [u32]) { + if cfg!(target_endian = "big") { + for n in numbers { + *n = n.to_le(); + } + } + } + + #[inline] + fn from_slice_u64(numbers: &mut [u64]) { + if cfg!(target_endian = "big") { + for n in numbers { + *n = n.to_le(); + } + } + } + + #[cfg(feature = "i128")] + #[inline] + fn from_slice_u128(numbers: &mut [u128]) { + if cfg!(target_endian = "big") { + for n in numbers { + *n = n.to_le(); + } + } + } + + #[inline] + fn from_slice_f32(numbers: &mut [f32]) { + if cfg!(target_endian = "big") { + for n in numbers { + let int: u32 = unsafe { transmute(*n) }; + *n = safe_u32_bits_to_f32(int.to_le()); + } + } + } + + #[inline] + fn from_slice_f64(numbers: &mut [f64]) { + if cfg!(target_endian = "big") { + for n in numbers { + let int: u64 = unsafe { transmute(*n) }; + *n = safe_u64_bits_to_f64(int.to_le()); + } + } + } +} + +#[inline] +fn safe_u32_bits_to_f32(u: u32) -> f32 { + use core::f32::NAN; + + const EXP_MASK: u32 = 0x7F800000; + const FRACT_MASK: u32 = 0x007FFFFF; + + if u & EXP_MASK == EXP_MASK && u & FRACT_MASK != 0 { + // While IEEE 754-2008 specifies encodings for quiet NaNs and + // signaling ones, certains MIPS and PA-RISC CPUs treat signaling + // NaNs differently. Therefore, to be safe, we pass a known quiet + // NaN if u is any kind of NaN. The check above only assumes + // IEEE 754-1985 to be valid. + NAN + } else { + unsafe { transmute(u) } + } +} + +#[inline] +fn safe_u64_bits_to_f64(u: u64) -> f64 { + use core::f64::NAN; + + const EXP_MASK: u64 = 0x7FF0000000000000; + const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF; + + if u & EXP_MASK == EXP_MASK && u & FRACT_MASK != 0 { + // While IEEE 754-2008 specifies encodings for quiet NaNs and + // signaling ones, certains MIPS and PA-RISC CPUs treat signaling + // NaNs differently. Therefore, to be safe, we pass a known quiet + // NaN if u is any kind of NaN. The check above only assumes + // IEEE 754-1985 to be valid. + NAN + } else { + unsafe { transmute(u) } + } +} + +#[cfg(test)] +mod test { + extern crate quickcheck; + extern crate rand; + + use self::quickcheck::{QuickCheck, StdGen, Testable}; + use self::rand::thread_rng; + #[cfg(feature = "i128")] use self::quickcheck::{Arbitrary, Gen}; + + pub const U24_MAX: u32 = 16_777_215; + pub const I24_MAX: i32 = 8_388_607; + + pub const U64_MAX: u64 = ::core::u64::MAX; + pub const I64_MAX: u64 = ::core::i64::MAX as u64; + + macro_rules! calc_max { + ($max:expr, $bytes:expr) => { calc_max!($max, $bytes, 8) }; + ($max:expr, $bytes:expr, $maxbytes:expr) => { + ($max - 1) >> (8 * ($maxbytes - $bytes)) + }; + } + + #[derive(Clone, Debug)] + pub struct Wi128(pub T); + + #[cfg(feature = "i128")] + impl Wi128 { + pub fn clone(&self) -> T { + self.0.clone() + } + } + + impl PartialEq for Wi128 { + fn eq(&self, other: &T) -> bool { + self.0.eq(other) + } + } + + #[cfg(feature = "i128")] + impl Arbitrary for Wi128 { + fn arbitrary(gen: &mut G) -> Wi128 { + let max = calc_max!(::core::u128::MAX, gen.size(), 16); + let output = + (gen.gen::() as u128) | + ((gen.gen::() as u128) << 64); + Wi128(output & (max - 1)) + } + } + + #[cfg(feature = "i128")] + impl Arbitrary for Wi128 { + fn arbitrary(gen: &mut G) -> Wi128 { + let max = calc_max!(::core::i128::MAX, gen.size(), 16); + let output = + (gen.gen::() as i128) | + ((gen.gen::() as i128) << 64); + Wi128(output & (max - 1)) + } + } + + pub fn qc_sized(f: A, size: u64) { + QuickCheck::new() + .gen(StdGen::new(thread_rng(), size as usize)) + .tests(1_00) + .max_tests(10_000) + .quickcheck(f); + } + + macro_rules! qc_byte_order { + ($name:ident, $ty_int:ty, $max:expr, + $bytes:expr, $read:ident, $write:ident) => ( + mod $name { + use {BigEndian, ByteOrder, NativeEndian, LittleEndian}; + #[allow(unused_imports)] use super::{ qc_sized, Wi128 }; + + #[test] + fn big_endian() { + fn prop(n: $ty_int) -> bool { + let mut buf = [0; 16]; + BigEndian::$write(&mut buf, n.clone(), $bytes); + n == BigEndian::$read(&mut buf[..$bytes], $bytes) + } + qc_sized(prop as fn($ty_int) -> bool, $max); + } + + #[test] + fn little_endian() { + fn prop(n: $ty_int) -> bool { + let mut buf = [0; 16]; + LittleEndian::$write(&mut buf, n.clone(), $bytes); + n == LittleEndian::$read(&mut buf[..$bytes], $bytes) + } + qc_sized(prop as fn($ty_int) -> bool, $max); + } + + #[test] + fn native_endian() { + fn prop(n: $ty_int) -> bool { + let mut buf = [0; 16]; + NativeEndian::$write(&mut buf, n.clone(), $bytes); + n == NativeEndian::$read(&mut buf[..$bytes], $bytes) + } + qc_sized(prop as fn($ty_int) -> bool, $max); + } + } + ); + ($name:ident, $ty_int:ty, $max:expr, + $read:ident, $write:ident) => ( + mod $name { + use core::mem::size_of; + use {BigEndian, ByteOrder, NativeEndian, LittleEndian}; + #[allow(unused_imports)] use super::{ qc_sized, Wi128 }; + + #[test] + fn big_endian() { + fn prop(n: $ty_int) -> bool { + let bytes = size_of::<$ty_int>(); + let mut buf = [0; 16]; + BigEndian::$write(&mut buf[16 - bytes..], n.clone()); + n == BigEndian::$read(&mut buf[16 - bytes..]) + } + qc_sized(prop as fn($ty_int) -> bool, $max - 1); + } + + #[test] + fn little_endian() { + fn prop(n: $ty_int) -> bool { + let bytes = size_of::<$ty_int>(); + let mut buf = [0; 16]; + LittleEndian::$write(&mut buf[..bytes], n.clone()); + n == LittleEndian::$read(&mut buf[..bytes]) + } + qc_sized(prop as fn($ty_int) -> bool, $max - 1); + } + + #[test] + fn native_endian() { + fn prop(n: $ty_int) -> bool { + let bytes = size_of::<$ty_int>(); + let mut buf = [0; 16]; + NativeEndian::$write(&mut buf[..bytes], n.clone()); + n == NativeEndian::$read(&mut buf[..bytes]) + } + qc_sized(prop as fn($ty_int) -> bool, $max - 1); + } + } + ); + } + + qc_byte_order!(prop_u16, u16, ::core::u16::MAX as u64, read_u16, write_u16); + qc_byte_order!(prop_i16, i16, ::core::i16::MAX as u64, read_i16, write_i16); + qc_byte_order!(prop_u24, u32, ::test::U24_MAX as u64, read_u24, write_u24); + qc_byte_order!(prop_i24, i32, ::test::I24_MAX as u64, read_i24, write_i24); + qc_byte_order!(prop_u32, u32, ::core::u32::MAX as u64, read_u32, write_u32); + qc_byte_order!(prop_i32, i32, ::core::i32::MAX as u64, read_i32, write_i32); + qc_byte_order!(prop_u64, u64, ::core::u64::MAX as u64, read_u64, write_u64); + qc_byte_order!(prop_i64, i64, ::core::i64::MAX as u64, read_i64, write_i64); + qc_byte_order!(prop_f32, f32, ::core::u64::MAX as u64, read_f32, write_f32); + qc_byte_order!(prop_f64, f64, ::core::i64::MAX as u64, read_f64, write_f64); + + #[cfg(feature = "i128")] + qc_byte_order!(prop_u128, Wi128, 16 + 1, read_u128, write_u128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_i128, Wi128, 16 + 1, read_i128, write_i128); + + qc_byte_order!(prop_uint_1, + u64, calc_max!(super::U64_MAX, 1), 1, read_uint, write_uint); + qc_byte_order!(prop_uint_2, + u64, calc_max!(super::U64_MAX, 2), 2, read_uint, write_uint); + qc_byte_order!(prop_uint_3, + u64, calc_max!(super::U64_MAX, 3), 3, read_uint, write_uint); + qc_byte_order!(prop_uint_4, + u64, calc_max!(super::U64_MAX, 4), 4, read_uint, write_uint); + qc_byte_order!(prop_uint_5, + u64, calc_max!(super::U64_MAX, 5), 5, read_uint, write_uint); + qc_byte_order!(prop_uint_6, + u64, calc_max!(super::U64_MAX, 6), 6, read_uint, write_uint); + qc_byte_order!(prop_uint_7, + u64, calc_max!(super::U64_MAX, 7), 7, read_uint, write_uint); + qc_byte_order!(prop_uint_8, + u64, calc_max!(super::U64_MAX, 8), 8, read_uint, write_uint); + + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_1, + Wi128, 1, 1, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_2, + Wi128, 2, 2, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_3, + Wi128, 3, 3, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_4, + Wi128, 4, 4, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_5, + Wi128, 5, 5, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_6, + Wi128, 6, 6, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_7, + Wi128, 7, 7, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_8, + Wi128, 8, 8, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_9, + Wi128, 9, 9, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_10, + Wi128, 10, 10, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_11, + Wi128, 11, 11, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_12, + Wi128, 12, 12, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_13, + Wi128, 13, 13, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_14, + Wi128, 14, 14, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_15, + Wi128, 15, 15, read_uint128, write_uint128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_uint128_16, + Wi128, 16, 16, read_uint128, write_uint128); + + qc_byte_order!(prop_int_1, + i64, calc_max!(super::I64_MAX, 1), 1, read_int, write_int); + qc_byte_order!(prop_int_2, + i64, calc_max!(super::I64_MAX, 2), 2, read_int, write_int); + qc_byte_order!(prop_int_3, + i64, calc_max!(super::I64_MAX, 3), 3, read_int, write_int); + qc_byte_order!(prop_int_4, + i64, calc_max!(super::I64_MAX, 4), 4, read_int, write_int); + qc_byte_order!(prop_int_5, + i64, calc_max!(super::I64_MAX, 5), 5, read_int, write_int); + qc_byte_order!(prop_int_6, + i64, calc_max!(super::I64_MAX, 6), 6, read_int, write_int); + qc_byte_order!(prop_int_7, + i64, calc_max!(super::I64_MAX, 7), 7, read_int, write_int); + qc_byte_order!(prop_int_8, + i64, calc_max!(super::I64_MAX, 8), 8, read_int, write_int); + + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_1, + Wi128, 1, 1, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_2, + Wi128, 2, 2, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_3, + Wi128, 3, 3, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_4, + Wi128, 4, 4, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_5, + Wi128, 5, 5, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_6, + Wi128, 6, 6, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_7, + Wi128, 7, 7, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_8, + Wi128, 8, 8, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_9, + Wi128, 9, 9, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_10, + Wi128, 10, 10, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_11, + Wi128, 11, 11, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_12, + Wi128, 12, 12, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_13, + Wi128, 13, 13, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_14, + Wi128, 14, 14, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_15, + Wi128, 15, 15, read_int128, write_int128); + #[cfg(feature = "i128")] + qc_byte_order!(prop_int128_16, + Wi128, 16, 16, read_int128, write_int128); + + + // Test that all of the byte conversion functions panic when given a + // buffer that is too small. + // + // These tests are critical to ensure safety, otherwise we might end up + // with a buffer overflow. + macro_rules! too_small { + ($name:ident, $maximally_small:expr, $zero:expr, + $read:ident, $write:ident) => ( + mod $name { + use {BigEndian, ByteOrder, NativeEndian, LittleEndian}; + + #[test] + #[should_panic] + fn read_big_endian() { + let buf = [0; $maximally_small]; + BigEndian::$read(&buf); + } + + #[test] + #[should_panic] + fn read_little_endian() { + let buf = [0; $maximally_small]; + LittleEndian::$read(&buf); + } + + #[test] + #[should_panic] + fn read_native_endian() { + let buf = [0; $maximally_small]; + NativeEndian::$read(&buf); + } + + #[test] + #[should_panic] + fn write_big_endian() { + let mut buf = [0; $maximally_small]; + BigEndian::$write(&mut buf, $zero); + } + + #[test] + #[should_panic] + fn write_little_endian() { + let mut buf = [0; $maximally_small]; + LittleEndian::$write(&mut buf, $zero); + } + + #[test] + #[should_panic] + fn write_native_endian() { + let mut buf = [0; $maximally_small]; + NativeEndian::$write(&mut buf, $zero); + } + } + ); + ($name:ident, $maximally_small:expr, $read:ident) => ( + mod $name { + use {BigEndian, ByteOrder, NativeEndian, LittleEndian}; + + #[test] + #[should_panic] + fn read_big_endian() { + let buf = [0; $maximally_small]; + BigEndian::$read(&buf, $maximally_small + 1); + } + + #[test] + #[should_panic] + fn read_little_endian() { + let buf = [0; $maximally_small]; + LittleEndian::$read(&buf, $maximally_small + 1); + } + + #[test] + #[should_panic] + fn read_native_endian() { + let buf = [0; $maximally_small]; + NativeEndian::$read(&buf, $maximally_small + 1); + } + } + ); + } + + too_small!(small_u16, 1, 0, read_u16, write_u16); + too_small!(small_i16, 1, 0, read_i16, write_i16); + too_small!(small_u32, 3, 0, read_u32, write_u32); + too_small!(small_i32, 3, 0, read_i32, write_i32); + too_small!(small_u64, 7, 0, read_u64, write_u64); + too_small!(small_i64, 7, 0, read_i64, write_i64); + too_small!(small_f32, 3, 0.0, read_f32, write_f32); + too_small!(small_f64, 7, 0.0, read_f64, write_f64); + #[cfg(feature = "i128")] + too_small!(small_u128, 15, 0, read_u128, write_u128); + #[cfg(feature = "i128")] + too_small!(small_i128, 15, 0, read_i128, write_i128); + + too_small!(small_uint_1, 1, read_uint); + too_small!(small_uint_2, 2, read_uint); + too_small!(small_uint_3, 3, read_uint); + too_small!(small_uint_4, 4, read_uint); + too_small!(small_uint_5, 5, read_uint); + too_small!(small_uint_6, 6, read_uint); + too_small!(small_uint_7, 7, read_uint); + + #[cfg(feature = "i128")] + too_small!(small_uint128_1, 1, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_2, 2, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_3, 3, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_4, 4, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_5, 5, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_6, 6, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_7, 7, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_8, 8, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_9, 9, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_10, 10, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_11, 11, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_12, 12, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_13, 13, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_14, 14, read_uint128); + #[cfg(feature = "i128")] + too_small!(small_uint128_15, 15, read_uint128); + + too_small!(small_int_1, 1, read_int); + too_small!(small_int_2, 2, read_int); + too_small!(small_int_3, 3, read_int); + too_small!(small_int_4, 4, read_int); + too_small!(small_int_5, 5, read_int); + too_small!(small_int_6, 6, read_int); + too_small!(small_int_7, 7, read_int); + + #[cfg(feature = "i128")] + too_small!(small_int128_1, 1, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_2, 2, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_3, 3, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_4, 4, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_5, 5, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_6, 6, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_7, 7, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_8, 8, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_9, 9, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_10, 10, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_11, 11, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_12, 12, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_13, 13, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_14, 14, read_int128); + #[cfg(feature = "i128")] + too_small!(small_int128_15, 15, read_int128); + + // Test that reading/writing slices enforces the correct lengths. + macro_rules! slice_lengths { + ($name:ident, $read:ident, $write:ident, + $num_bytes:expr, $numbers:expr) => { + mod $name { + use {ByteOrder, BigEndian, NativeEndian, LittleEndian}; + + #[test] + #[should_panic] + fn read_big_endian() { + let bytes = [0; $num_bytes]; + let mut numbers = $numbers; + BigEndian::$read(&bytes, &mut numbers); + } + + #[test] + #[should_panic] + fn read_little_endian() { + let bytes = [0; $num_bytes]; + let mut numbers = $numbers; + LittleEndian::$read(&bytes, &mut numbers); + } + + #[test] + #[should_panic] + fn read_native_endian() { + let bytes = [0; $num_bytes]; + let mut numbers = $numbers; + NativeEndian::$read(&bytes, &mut numbers); + } + + #[test] + #[should_panic] + fn write_big_endian() { + let mut bytes = [0; $num_bytes]; + let numbers = $numbers; + BigEndian::$write(&numbers, &mut bytes); + } + + #[test] + #[should_panic] + fn write_little_endian() { + let mut bytes = [0; $num_bytes]; + let numbers = $numbers; + LittleEndian::$write(&numbers, &mut bytes); + } + + #[test] + #[should_panic] + fn write_native_endian() { + let mut bytes = [0; $num_bytes]; + let numbers = $numbers; + NativeEndian::$write(&numbers, &mut bytes); + } + } + } + } + + slice_lengths!( + slice_len_too_small_u16, read_u16_into, write_u16_into, 3, [0, 0]); + slice_lengths!( + slice_len_too_big_u16, read_u16_into, write_u16_into, 5, [0, 0]); + slice_lengths!( + slice_len_too_small_i16, read_i16_into, write_i16_into, 3, [0, 0]); + slice_lengths!( + slice_len_too_big_i16, read_i16_into, write_i16_into, 5, [0, 0]); + + slice_lengths!( + slice_len_too_small_u32, read_u32_into, write_u32_into, 7, [0, 0]); + slice_lengths!( + slice_len_too_big_u32, read_u32_into, write_u32_into, 9, [0, 0]); + slice_lengths!( + slice_len_too_small_i32, read_i32_into, write_i32_into, 7, [0, 0]); + slice_lengths!( + slice_len_too_big_i32, read_i32_into, write_i32_into, 9, [0, 0]); + + slice_lengths!( + slice_len_too_small_u64, read_u64_into, write_u64_into, 15, [0, 0]); + slice_lengths!( + slice_len_too_big_u64, read_u64_into, write_u64_into, 17, [0, 0]); + slice_lengths!( + slice_len_too_small_i64, read_i64_into, write_i64_into, 15, [0, 0]); + slice_lengths!( + slice_len_too_big_i64, read_i64_into, write_i64_into, 17, [0, 0]); + + #[cfg(feature = "i128")] + slice_lengths!( + slice_len_too_small_u128, read_u128_into, write_u128_into, 31, [0, 0]); + #[cfg(feature = "i128")] + slice_lengths!( + slice_len_too_big_u128, read_u128_into, write_u128_into, 33, [0, 0]); + #[cfg(feature = "i128")] + slice_lengths!( + slice_len_too_small_i128, read_i128_into, write_i128_into, 31, [0, 0]); + #[cfg(feature = "i128")] + slice_lengths!( + slice_len_too_big_i128, read_i128_into, write_i128_into, 33, [0, 0]); + + #[test] + fn uint_bigger_buffer() { + use {ByteOrder, LittleEndian}; + let n = LittleEndian::read_uint(&[1, 2, 3, 4, 5, 6, 7, 8], 5); + assert_eq!(n, 0x0504030201); + } + + #[test] + fn read_snan() { + use core::f32; + use core::f64; + use core::mem::transmute; + + use {ByteOrder, BigEndian, LittleEndian}; + + let sf = BigEndian::read_f32(&[0xFF, 0x80, 0x00, 0x01]); + let sbits: u32 = unsafe { transmute(sf) }; + assert_eq!(sbits, unsafe { transmute(f32::NAN) }); + assert_eq!(sf.classify(), ::core::num::FpCategory::Nan); + + let df = BigEndian::read_f64(&[0x7F, 0xF0, 0, 0, 0, 0, 0, 0x01]); + let dbits: u64 = unsafe { ::core::mem::transmute(df) }; + assert_eq!(dbits, unsafe { transmute(f64::NAN) }); + assert_eq!(df.classify(), ::core::num::FpCategory::Nan); + + let sf = LittleEndian::read_f32(&[0x01, 0x00, 0x80, 0xFF]); + let sbits: u32 = unsafe { transmute(sf) }; + assert_eq!(sbits, unsafe { transmute(f32::NAN) }); + assert_eq!(sf.classify(), ::core::num::FpCategory::Nan); + + let df = LittleEndian::read_f64(&[0x01, 0, 0, 0, 0, 0, 0xF0, 0x7F]); + let dbits: u64 = unsafe { ::core::mem::transmute(df) }; + assert_eq!(dbits, unsafe { transmute(f64::NAN) }); + assert_eq!(df.classify(), ::core::num::FpCategory::Nan); + } +} + +#[cfg(test)] +#[cfg(feature = "std")] +mod stdtests { + extern crate quickcheck; + extern crate rand; + + use self::quickcheck::{QuickCheck, StdGen, Testable}; + use self::rand::thread_rng; + + fn qc_unsized(f: A) { + + QuickCheck::new() + .gen(StdGen::new(thread_rng(), 16)) + .tests(1_00) + .max_tests(10_000) + .quickcheck(f); + } + + macro_rules! calc_max { + ($max:expr, $bytes:expr) => { ($max - 1) >> (8 * (8 - $bytes)) }; + } + + macro_rules! qc_bytes_ext { + ($name:ident, $ty_int:ty, $max:expr, + $bytes:expr, $read:ident, $write:ident) => ( + mod $name { + use std::io::Cursor; + use { + ReadBytesExt, WriteBytesExt, + BigEndian, NativeEndian, LittleEndian, + }; + #[allow(unused_imports)] use test::{qc_sized, Wi128}; + + #[test] + fn big_endian() { + fn prop(n: $ty_int) -> bool { + let mut wtr = vec![]; + wtr.$write::(n.clone()).unwrap(); + let mut rdr = Vec::new(); + rdr.extend(wtr[wtr.len()-$bytes..].iter().map(|&x| x)); + let mut rdr = Cursor::new(rdr); + n == rdr.$read::($bytes).unwrap() + } + qc_sized(prop as fn($ty_int) -> bool, $max); + } + + #[test] + fn little_endian() { + fn prop(n: $ty_int) -> bool { + let mut wtr = vec![]; + wtr.$write::(n.clone()).unwrap(); + let mut rdr = Cursor::new(wtr); + n == rdr.$read::($bytes).unwrap() + } + qc_sized(prop as fn($ty_int) -> bool, $max); + } + + #[test] + fn native_endian() { + fn prop(n: $ty_int) -> bool { + let mut wtr = vec![]; + wtr.$write::(n.clone()).unwrap(); + let mut rdr = Cursor::new(wtr); + n == rdr.$read::($bytes).unwrap() + } + qc_sized(prop as fn($ty_int) -> bool, $max); + } + } + ); + ($name:ident, $ty_int:ty, $max:expr, $read:ident, $write:ident) => ( + mod $name { + use std::io::Cursor; + use { + ReadBytesExt, WriteBytesExt, + BigEndian, NativeEndian, LittleEndian, + }; + #[allow(unused_imports)] use test::{qc_sized, Wi128}; + + #[test] + fn big_endian() { + fn prop(n: $ty_int) -> bool { + let mut wtr = vec![]; + wtr.$write::(n.clone()).unwrap(); + let mut rdr = Cursor::new(wtr); + n == rdr.$read::().unwrap() + } + qc_sized(prop as fn($ty_int) -> bool, $max - 1); + } + + #[test] + fn little_endian() { + fn prop(n: $ty_int) -> bool { + let mut wtr = vec![]; + wtr.$write::(n.clone()).unwrap(); + let mut rdr = Cursor::new(wtr); + n == rdr.$read::().unwrap() + } + qc_sized(prop as fn($ty_int) -> bool, $max - 1); + } + + #[test] + fn native_endian() { + fn prop(n: $ty_int) -> bool { + let mut wtr = vec![]; + wtr.$write::(n.clone()).unwrap(); + let mut rdr = Cursor::new(wtr); + n == rdr.$read::().unwrap() + } + qc_sized(prop as fn($ty_int) -> bool, $max - 1); + } + } + ); + } + + qc_bytes_ext!(prop_ext_u16, + u16, ::std::u16::MAX as u64, read_u16, write_u16); + qc_bytes_ext!(prop_ext_i16, + i16, ::std::i16::MAX as u64, read_i16, write_i16); + qc_bytes_ext!(prop_ext_u32, + u32, ::std::u32::MAX as u64, read_u32, write_u32); + qc_bytes_ext!(prop_ext_i32, + i32, ::std::i32::MAX as u64, read_i32, write_i32); + qc_bytes_ext!(prop_ext_u64, + u64, ::std::u64::MAX as u64, read_u64, write_u64); + qc_bytes_ext!(prop_ext_i64, + i64, ::std::i64::MAX as u64, read_i64, write_i64); + qc_bytes_ext!(prop_ext_f32, + f32, ::std::u64::MAX as u64, read_f32, write_f32); + qc_bytes_ext!(prop_ext_f64, + f64, ::std::i64::MAX as u64, read_f64, write_f64); + + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_u128, Wi128, 16 + 1, read_u128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_i128, Wi128, 16 + 1, read_i128, write_i128); + + qc_bytes_ext!(prop_ext_uint_1, + u64, calc_max!(::test::U64_MAX, 1), 1, read_uint, write_u64); + qc_bytes_ext!(prop_ext_uint_2, + u64, calc_max!(::test::U64_MAX, 2), 2, read_uint, write_u64); + qc_bytes_ext!(prop_ext_uint_3, + u64, calc_max!(::test::U64_MAX, 3), 3, read_uint, write_u64); + qc_bytes_ext!(prop_ext_uint_4, + u64, calc_max!(::test::U64_MAX, 4), 4, read_uint, write_u64); + qc_bytes_ext!(prop_ext_uint_5, + u64, calc_max!(::test::U64_MAX, 5), 5, read_uint, write_u64); + qc_bytes_ext!(prop_ext_uint_6, + u64, calc_max!(::test::U64_MAX, 6), 6, read_uint, write_u64); + qc_bytes_ext!(prop_ext_uint_7, + u64, calc_max!(::test::U64_MAX, 7), 7, read_uint, write_u64); + qc_bytes_ext!(prop_ext_uint_8, + u64, calc_max!(::test::U64_MAX, 8), 8, read_uint, write_u64); + + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_1, + Wi128, 1, 1, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_2, + Wi128, 2, 2, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_3, + Wi128, 3, 3, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_4, + Wi128, 4, 4, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_5, + Wi128, 5, 5, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_6, + Wi128, 6, 6, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_7, + Wi128, 7, 7, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_8, + Wi128, 8, 8, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_9, + Wi128, 9, 9, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_10, + Wi128, 10, 10, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_11, + Wi128, 11, 11, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_12, + Wi128, 12, 12, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_13, + Wi128, 13, 13, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_14, + Wi128, 14, 14, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_15, + Wi128, 15, 15, read_uint128, write_u128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_uint128_16, + Wi128, 16, 16, read_uint128, write_u128); + + qc_bytes_ext!(prop_ext_int_1, + i64, calc_max!(::test::I64_MAX, 1), 1, read_int, write_i64); + qc_bytes_ext!(prop_ext_int_2, + i64, calc_max!(::test::I64_MAX, 2), 2, read_int, write_i64); + qc_bytes_ext!(prop_ext_int_3, + i64, calc_max!(::test::I64_MAX, 3), 3, read_int, write_i64); + qc_bytes_ext!(prop_ext_int_4, + i64, calc_max!(::test::I64_MAX, 4), 4, read_int, write_i64); + qc_bytes_ext!(prop_ext_int_5, + i64, calc_max!(::test::I64_MAX, 5), 5, read_int, write_i64); + qc_bytes_ext!(prop_ext_int_6, + i64, calc_max!(::test::I64_MAX, 6), 6, read_int, write_i64); + qc_bytes_ext!(prop_ext_int_7, + i64, calc_max!(::test::I64_MAX, 1), 7, read_int, write_i64); + qc_bytes_ext!(prop_ext_int_8, + i64, calc_max!(::test::I64_MAX, 8), 8, read_int, write_i64); + + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_1, + Wi128, 1, 1, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_2, + Wi128, 2, 2, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_3, + Wi128, 3, 3, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_4, + Wi128, 4, 4, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_5, + Wi128, 5, 5, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_6, + Wi128, 6, 6, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_7, + Wi128, 7, 7, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_8, + Wi128, 8, 8, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_9, + Wi128, 9, 9, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_10, + Wi128, 10, 10, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_11, + Wi128, 11, 11, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_12, + Wi128, 12, 12, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_13, + Wi128, 13, 13, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_14, + Wi128, 14, 14, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_15, + Wi128, 15, 15, read_int128, write_i128); + #[cfg(feature = "i128")] + qc_bytes_ext!(prop_ext_int128_16, + Wi128, 16, 16, read_int128, write_i128); + + // Test slice serialization/deserialization. + macro_rules! qc_slice { + ($name:ident, $ty_int:ty, $read:ident, $write:ident, $zero:expr) => { + mod $name { + use core::mem::size_of; + use {ByteOrder, BigEndian, NativeEndian, LittleEndian}; + use super::qc_unsized; + #[allow(unused_imports)] + use test::Wi128; + + #[test] + fn big_endian() { + #[allow(unused_unsafe)] + fn prop(numbers: Vec<$ty_int>) -> bool { + let numbers: Vec<_> = numbers + .into_iter() + .map(|x| x.clone()) + .collect(); + let num_bytes = size_of::<$ty_int>() * numbers.len(); + let mut bytes = vec![0; num_bytes]; + + BigEndian::$write(&numbers, &mut bytes); + + let mut got = vec![$zero; numbers.len()]; + unsafe { BigEndian::$read(&bytes, &mut got); } + + numbers == got + } + qc_unsized(prop as fn(_) -> bool); + } + + #[test] + fn little_endian() { + #[allow(unused_unsafe)] + fn prop(numbers: Vec<$ty_int>) -> bool { + let numbers: Vec<_> = numbers + .into_iter() + .map(|x| x.clone()) + .collect(); + let num_bytes = size_of::<$ty_int>() * numbers.len(); + let mut bytes = vec![0; num_bytes]; + + LittleEndian::$write(&numbers, &mut bytes); + + let mut got = vec![$zero; numbers.len()]; + unsafe { LittleEndian::$read(&bytes, &mut got); } + + numbers == got + } + qc_unsized(prop as fn(_) -> bool); + } + + #[test] + fn native_endian() { + #[allow(unused_unsafe)] + fn prop(numbers: Vec<$ty_int>) -> bool { + let numbers: Vec<_> = numbers + .into_iter() + .map(|x| x.clone()) + .collect(); + let num_bytes = size_of::<$ty_int>() * numbers.len(); + let mut bytes = vec![0; num_bytes]; + + NativeEndian::$write(&numbers, &mut bytes); + + let mut got = vec![$zero; numbers.len()]; + unsafe { NativeEndian::$read(&bytes, &mut got); } + + numbers == got + } + qc_unsized(prop as fn(_) -> bool); + } + } + } + } + + qc_slice!(prop_slice_u16, u16, read_u16_into, write_u16_into, 0); + qc_slice!(prop_slice_i16, i16, read_i16_into, write_i16_into, 0); + qc_slice!(prop_slice_u32, u32, read_u32_into, write_u32_into, 0); + qc_slice!(prop_slice_i32, i32, read_i32_into, write_i32_into, 0); + qc_slice!(prop_slice_u64, u64, read_u64_into, write_u64_into, 0); + qc_slice!(prop_slice_i64, i64, read_i64_into, write_i64_into, 0); + #[cfg(feature = "i128")] + qc_slice!( + prop_slice_u128, Wi128, read_u128_into, write_u128_into, 0); + #[cfg(feature = "i128")] + qc_slice!( + prop_slice_i128, Wi128, read_i128_into, write_i128_into, 0); + + qc_slice!( + prop_slice_f32, f32, read_f32_into_unchecked, write_f32_into, 0.0); + qc_slice!( + prop_slice_f64, f64, read_f64_into_unchecked, write_f64_into, 0.0); +} diff --git a/third_party/rust/byteorder/.cargo-checksum.json b/third_party/rust/byteorder/.cargo-checksum.json index 666d7025c22e..08bdcd8821dd 100644 --- a/third_party/rust/byteorder/.cargo-checksum.json +++ b/third_party/rust/byteorder/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".travis.yml":"a798200a7a2a7b499b4c44f0e516cd8975dc5f4b933144d1e2b1523087330b95","CHANGELOG.md":"caa17ca58eafcd282ed7b8bd47ef0670ce30a20aad339bf2758469f09fa4559d","COPYING":"01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f","Cargo.toml":"0e636678a02b111d1ebdfdc8ac5a835a2a41370028eeadf6d5fbfd3239f531f8","LICENSE-MIT":"0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f","README.md":"0559514b9d7488e96fb7a2f3c043a62fadf3495a1e10602d109ce79ee67da998","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","benches/bench.rs":"250d46461a0529a856d76a6421fd45c499bccba2c532e05dbf438c94582a8eac","src/io.rs":"5429f522221b3cce8a5c90fba7ad98b0ddad4fab44acc39763e6adabd511c021","src/lib.rs":"d31f218ca1892bea2812dd575ceb700779de704dc8bf1b9a778315ab75343c5d"},"package":"ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"} \ No newline at end of file +{"files":{".travis.yml":"a798200a7a2a7b499b4c44f0e516cd8975dc5f4b933144d1e2b1523087330b95","CHANGELOG.md":"750ff16a714caad77a272a62b4baed5ea113ead2846f8c3968342e592a99ecd5","COPYING":"01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f","Cargo.toml":"cd633b2d76dd5d57191388e7cc31340eb2d6b89e2f98f587999dc685d977c673","LICENSE-MIT":"0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f","README.md":"97c01a66dbff4615acd49a8c3a85d137bf29cc113fa514910195bb11aef445bc","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","benches/bench.rs":"250d46461a0529a856d76a6421fd45c499bccba2c532e05dbf438c94582a8eac","src/io.rs":"4221711399f9723ef21c5b07700b8e9319f6f665b1eaaceb49528f6e976a25a9","src/lib.rs":"07e5f53bf3b9e30f1da9f4cf37797b77ce3d16c9f2a3a3e56669dd323debd1b9"},"package":"652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"} \ No newline at end of file diff --git a/third_party/rust/byteorder/CHANGELOG.md b/third_party/rust/byteorder/CHANGELOG.md index 0909791e782b..d59eaa7c5a81 100644 --- a/third_party/rust/byteorder/CHANGELOG.md +++ b/third_party/rust/byteorder/CHANGELOG.md @@ -1,3 +1,25 @@ +1.2.1 +===== +This patch release removes more unnecessary uses of `unsafe` that +were overlooked in the prior `1.2.0` release. In particular, the +`ReadBytesExt::read_{f32,f64}_into_checked` methods have been deprecated and +replaced by more appropriately named `read_{f32,f64}_into` methods. + + +1.2.0 +===== +The most prominent change in this release of `byteorder` is the removal of +unnecessary signaling NaN masking, and in turn, the `unsafe` annotations +associated with methods that didn't do masking. See +[#103](https://github.com/BurntSushi/byteorder/issues/103) +for more details. + +* [BUG #102](https://github.com/BurntSushi/byteorder/issues/102): + Fix big endian tests. +* [BUG #103](https://github.com/BurntSushi/byteorder/issues/103): + Remove sNaN masking. + + 1.1.0 ===== This release of `byteorder` features a number of fixes and improvements, mostly diff --git a/third_party/rust/byteorder/Cargo.toml b/third_party/rust/byteorder/Cargo.toml index 424fad8bff62..06a18e205621 100644 --- a/third_party/rust/byteorder/Cargo.toml +++ b/third_party/rust/byteorder/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "byteorder" -version = "1.1.0" +version = "1.2.1" authors = ["Andrew Gallant "] description = "Library for reading/writing numbers in big-endian and little-endian." homepage = "https://github.com/BurntSushi/byteorder" @@ -28,17 +28,16 @@ opt-level = 3 [lib] name = "byteorder" bench = false -[dev-dependencies.rand] -version = "0.3" - [dev-dependencies.quickcheck] version = "0.4" default-features = false +[dev-dependencies.rand] +version = "0.3" + [features] default = ["std"] i128 = [] std = [] [badges.travis-ci] -branch = "master" -repository = "https://github.com/BurntSushi/byteorder" +repository = "BurntSushi/byteorder" diff --git a/third_party/rust/byteorder/README.md b/third_party/rust/byteorder/README.md index 298a99d3468a..f282ab72e0b3 100644 --- a/third_party/rust/byteorder/README.md +++ b/third_party/rust/byteorder/README.md @@ -1,7 +1,7 @@ This crate provides convenience methods for encoding and decoding numbers in either big-endian or little-endian order. -[![Build status](https://api.travis-ci.org/BurntSushi/byteorder.png)](https://travis-ci.org/BurntSushi/byteorder) +[![Build status](https://api.travis-ci.org/BurntSushi/byteorder.svg)](https://travis-ci.org/BurntSushi/byteorder) [![](http://meritbadge.herokuapp.com/byteorder)](https://crates.io/crates/byteorder) Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org). diff --git a/third_party/rust/byteorder/src/io.rs b/third_party/rust/byteorder/src/io.rs index 208596ab18dd..c5645fa17f27 100644 --- a/third_party/rust/byteorder/src/io.rs +++ b/third_party/rust/byteorder/src/io.rs @@ -1,6 +1,5 @@ use std::io::{self, Result}; use std::slice; -// use std::mem::transmute; use ByteOrder; @@ -509,7 +508,7 @@ pub trait ReadBytesExt: io::Read { #[inline] fn read_u16_into(&mut self, dst: &mut [u16]) -> Result<()> { { - let mut buf = unsafe { slice_to_u8_mut(dst) }; + let buf = unsafe { slice_to_u8_mut(dst) }; try!(self.read_exact(buf)); } T::from_slice_u16(dst); @@ -544,7 +543,7 @@ pub trait ReadBytesExt: io::Read { #[inline] fn read_u32_into(&mut self, dst: &mut [u32]) -> Result<()> { { - let mut buf = unsafe { slice_to_u8_mut(dst) }; + let buf = unsafe { slice_to_u8_mut(dst) }; try!(self.read_exact(buf)); } T::from_slice_u32(dst); @@ -582,7 +581,7 @@ pub trait ReadBytesExt: io::Read { #[inline] fn read_u64_into(&mut self, dst: &mut [u64]) -> Result<()> { { - let mut buf = unsafe { slice_to_u8_mut(dst) }; + let buf = unsafe { slice_to_u8_mut(dst) }; try!(self.read_exact(buf)); } T::from_slice_u64(dst); @@ -659,7 +658,7 @@ pub trait ReadBytesExt: io::Read { #[inline] fn read_i16_into(&mut self, dst: &mut [i16]) -> Result<()> { { - let mut buf = unsafe { slice_to_u8_mut(dst) }; + let buf = unsafe { slice_to_u8_mut(dst) }; try!(self.read_exact(buf)); } T::from_slice_i16(dst); @@ -694,7 +693,7 @@ pub trait ReadBytesExt: io::Read { #[inline] fn read_i32_into(&mut self, dst: &mut [i32]) -> Result<()> { { - let mut buf = unsafe { slice_to_u8_mut(dst) }; + let buf = unsafe { slice_to_u8_mut(dst) }; try!(self.read_exact(buf)); } T::from_slice_i32(dst); @@ -732,7 +731,7 @@ pub trait ReadBytesExt: io::Read { #[inline] fn read_i64_into(&mut self, dst: &mut [i64]) -> Result<()> { { - let mut buf = unsafe { slice_to_u8_mut(dst) }; + let buf = unsafe { slice_to_u8_mut(dst) }; try!(self.read_exact(buf)); } T::from_slice_i64(dst); @@ -787,11 +786,53 @@ pub trait ReadBytesExt: io::Read { /// The given buffer is either filled completely or an error is returned. /// If an error is returned, the contents of `dst` are unspecified. /// - /// # Safety + /// # Errors /// - /// This method is unsafe because there are no guarantees made about the - /// floating point values. In particular, this method does not check for - /// signaling NaNs, which may result in undefined behavior. + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of big-endian single-precision floating point number + /// from a `Read`: + /// + /// ```rust + /// use std::f32; + /// use std::io::Cursor; + /// + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0x40, 0x49, 0x0f, 0xdb, + /// 0x3f, 0x80, 0x00, 0x00, + /// ]); + /// let mut dst = [0.0; 2]; + /// rdr.read_f32_into::(&mut dst).unwrap(); + /// assert_eq!([f32::consts::PI, 1.0], dst); + /// ``` + #[inline] + fn read_f32_into( + &mut self, + dst: &mut [f32], + ) -> Result<()> { + { + let buf = unsafe { slice_to_u8_mut(dst) }; + try!(self.read_exact(buf)); + } + T::from_slice_f32(dst); + Ok(()) + } + + /// **DEPRECATED**. + /// + /// This method is deprecated. Use `read_f32_into` instead. + /// + /// Reads a sequence of IEEE754 single-precision (4 bytes) floating + /// point numbers from the underlying reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. /// /// # Errors /// @@ -815,24 +856,65 @@ pub trait ReadBytesExt: io::Read { /// 0x3f, 0x80, 0x00, 0x00, /// ]); /// let mut dst = [0.0; 2]; - /// unsafe { - /// rdr.read_f32_into_unchecked::(&mut dst).unwrap(); - /// } + /// rdr.read_f32_into_unchecked::(&mut dst).unwrap(); /// assert_eq!([f32::consts::PI, 1.0], dst); /// ``` #[inline] - unsafe fn read_f32_into_unchecked( + fn read_f32_into_unchecked( &mut self, dst: &mut [f32], + ) -> Result<()> { + self.read_f32_into::(dst) + } + + /// Reads a sequence of IEEE754 double-precision (8 bytes) floating + /// point numbers from the underlying reader. + /// + /// The given buffer is either filled completely or an error is returned. + /// If an error is returned, the contents of `dst` are unspecified. + /// + /// # Errors + /// + /// This method returns the same errors as [`Read::read_exact`]. + /// + /// [`Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact + /// + /// # Examples + /// + /// Read a sequence of big-endian single-precision floating point number + /// from a `Read`: + /// + /// ```rust + /// use std::f64; + /// use std::io::Cursor; + /// + /// use byteorder::{BigEndian, ReadBytesExt}; + /// + /// let mut rdr = Cursor::new(vec![ + /// 0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18, + /// 0x3f, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// ]); + /// let mut dst = [0.0; 2]; + /// rdr.read_f64_into::(&mut dst).unwrap(); + /// assert_eq!([f64::consts::PI, 1.0], dst); + /// ``` + #[inline] + fn read_f64_into( + &mut self, + dst: &mut [f64], ) -> Result<()> { { - let mut buf = slice_to_u8_mut(dst); + let buf = unsafe { slice_to_u8_mut(dst) }; try!(self.read_exact(buf)); } - T::from_slice_f32(dst); + T::from_slice_f64(dst); Ok(()) } + /// **DEPRECATED**. + /// + /// This method is deprecated. Use `read_f64_into` instead. + /// /// Reads a sequence of IEEE754 double-precision (8 bytes) floating /// point numbers from the underlying reader. /// @@ -867,22 +949,15 @@ pub trait ReadBytesExt: io::Read { /// 0x3f, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// ]); /// let mut dst = [0.0; 2]; - /// unsafe { - /// rdr.read_f64_into_unchecked::(&mut dst).unwrap(); - /// } + /// rdr.read_f64_into_unchecked::(&mut dst).unwrap(); /// assert_eq!([f64::consts::PI, 1.0], dst); /// ``` #[inline] - unsafe fn read_f64_into_unchecked( + fn read_f64_into_unchecked( &mut self, dst: &mut [f64], ) -> Result<()> { - { - let mut buf = slice_to_u8_mut(dst); - try!(self.read_exact(buf)); - } - T::from_slice_f64(dst); - Ok(()) + self.read_f64_into::(dst) } } diff --git a/third_party/rust/byteorder/src/lib.rs b/third_party/rust/byteorder/src/lib.rs index b60fe7ca4c0f..0654e88d0c3d 100644 --- a/third_party/rust/byteorder/src/lib.rs +++ b/third_party/rust/byteorder/src/lib.rs @@ -40,7 +40,7 @@ assert_eq!(wtr, vec![5, 2, 0, 3]); #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "i128", feature(i128_type))] #![cfg_attr(all(feature = "i128", test), feature(i128))] -#![doc(html_root_url = "https://docs.rs/byteorder/1.0.0")] +#![doc(html_root_url = "https://docs.rs/byteorder/1.2.1")] #[cfg(feature = "std")] extern crate core; @@ -611,9 +611,6 @@ pub trait ByteOrder /// Reads a IEEE754 single-precision (4 bytes) floating point number. /// - /// The return value is always defined; signaling NaN's may be turned into - /// quiet NaN's. - /// /// # Panics /// /// Panics when `buf.len() < 4`. @@ -632,14 +629,11 @@ pub trait ByteOrder /// ``` #[inline] fn read_f32(buf: &[u8]) -> f32 { - safe_u32_bits_to_f32(Self::read_u32(buf)) + unsafe { transmute(Self::read_u32(buf)) } } /// Reads a IEEE754 double-precision (8 bytes) floating point number. /// - /// The return value is always defined; signaling NaN's may be turned into - /// quiet NaN's. - /// /// # Panics /// /// Panics when `buf.len() < 8`. @@ -658,7 +652,7 @@ pub trait ByteOrder /// ``` #[inline] fn read_f64(buf: &[u8]) -> f64 { - safe_u64_bits_to_f64(Self::read_u64(buf)) + unsafe { transmute(Self::read_u64(buf)) } } /// Writes a signed 16 bit integer `n` to `buf`. @@ -1066,12 +1060,6 @@ pub trait ByteOrder /// Reads IEEE754 single-precision (4 bytes) floating point numbers from /// `src` into `dst`. /// - /// Note that this does not perform any checks on the floating point - /// conversion. In particular, if the `src` data encodes an undefined - /// floating point value for your environment, then the result may be - /// undefined behavior. For example, this function may produce signaling - /// NaN floating point values. - /// /// # Panics /// /// Panics when `src.len() != 4*dst.len()`. @@ -1088,25 +1076,17 @@ pub trait ByteOrder /// LittleEndian::write_f32_into(&numbers_given, &mut bytes); /// /// let mut numbers_got = [0.0; 4]; - /// unsafe { - /// LittleEndian::read_f32_into_unchecked(&bytes, &mut numbers_got); - /// } + /// LittleEndian::read_f32_into_unchecked(&bytes, &mut numbers_got); /// assert_eq!(numbers_given, numbers_got); /// ``` #[inline] - unsafe fn read_f32_into_unchecked(src: &[u8], dst: &mut [f32]) { - Self::read_u32_into(src, transmute(dst)); + fn read_f32_into_unchecked(src: &[u8], dst: &mut [f32]) { + Self::read_u32_into(src, unsafe { transmute(dst) }); } /// Reads IEEE754 single-precision (4 bytes) floating point numbers from /// `src` into `dst`. /// - /// Note that this does not perform any checks on the floating point - /// conversion. In particular, if the `src` data encodes an undefined - /// floating point value for your environment, then the result may be - /// undefined behavior. For example, this function may produce signaling - /// NaN floating point values. - /// /// # Panics /// /// Panics when `src.len() != 8*dst.len()`. @@ -1123,14 +1103,12 @@ pub trait ByteOrder /// LittleEndian::write_f64_into(&numbers_given, &mut bytes); /// /// let mut numbers_got = [0.0; 4]; - /// unsafe { - /// LittleEndian::read_f64_into_unchecked(&bytes, &mut numbers_got); - /// } + /// LittleEndian::read_f64_into_unchecked(&bytes, &mut numbers_got); /// assert_eq!(numbers_given, numbers_got); /// ``` #[inline] - unsafe fn read_f64_into_unchecked(src: &[u8], dst: &mut [f64]) { - Self::read_u64_into(src, transmute(dst)); + fn read_f64_into_unchecked(src: &[u8], dst: &mut [f64]) { + Self::read_u64_into(src, unsafe { transmute(dst) }); } /// Writes unsigned 16 bit integers from `src` into `dst`. @@ -1590,10 +1568,6 @@ pub trait ByteOrder /// /// If the endianness matches the endianness of the host platform, then /// this is a no-op. - /// - /// Note that the results of this operation are guaranteed to be defined. - /// In particular, this method may replace signaling NaN values with - /// quiet NaN values. fn from_slice_f32(numbers: &mut [f32]); /// Converts the given slice of IEEE754 double-precision (8 bytes) floating @@ -1601,10 +1575,6 @@ pub trait ByteOrder /// /// If the endianness matches the endianness of the host platform, then /// this is a no-op. - /// - /// Note that the results of this operation are guaranteed to be defined. - /// In particular, this method may replace signaling NaN values with - /// quiet NaN values. fn from_slice_f64(numbers: &mut [f64]); } @@ -1964,7 +1934,7 @@ impl ByteOrder for BigEndian { if cfg!(target_endian = "little") { for n in numbers { let int: u32 = unsafe { transmute(*n) }; - *n = safe_u32_bits_to_f32(int.to_be()); + *n = unsafe { transmute(int.to_be()) }; } } } @@ -1974,7 +1944,7 @@ impl ByteOrder for BigEndian { if cfg!(target_endian = "little") { for n in numbers { let int: u64 = unsafe { transmute(*n) }; - *n = safe_u64_bits_to_f64(int.to_be()); + *n = unsafe { transmute(int.to_be()) }; } } } @@ -2167,7 +2137,7 @@ impl ByteOrder for LittleEndian { if cfg!(target_endian = "big") { for n in numbers { let int: u32 = unsafe { transmute(*n) }; - *n = safe_u32_bits_to_f32(int.to_le()); + *n = unsafe { transmute(int.to_le()) }; } } } @@ -2177,50 +2147,12 @@ impl ByteOrder for LittleEndian { if cfg!(target_endian = "big") { for n in numbers { let int: u64 = unsafe { transmute(*n) }; - *n = safe_u64_bits_to_f64(int.to_le()); + *n = unsafe { transmute(int.to_le()) }; } } } } -#[inline] -fn safe_u32_bits_to_f32(u: u32) -> f32 { - use core::f32::NAN; - - const EXP_MASK: u32 = 0x7F800000; - const FRACT_MASK: u32 = 0x007FFFFF; - - if u & EXP_MASK == EXP_MASK && u & FRACT_MASK != 0 { - // While IEEE 754-2008 specifies encodings for quiet NaNs and - // signaling ones, certains MIPS and PA-RISC CPUs treat signaling - // NaNs differently. Therefore, to be safe, we pass a known quiet - // NaN if u is any kind of NaN. The check above only assumes - // IEEE 754-1985 to be valid. - NAN - } else { - unsafe { transmute(u) } - } -} - -#[inline] -fn safe_u64_bits_to_f64(u: u64) -> f64 { - use core::f64::NAN; - - const EXP_MASK: u64 = 0x7FF0000000000000; - const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF; - - if u & EXP_MASK == EXP_MASK && u & FRACT_MASK != 0 { - // While IEEE 754-2008 specifies encodings for quiet NaNs and - // signaling ones, certains MIPS and PA-RISC CPUs treat signaling - // NaNs differently. Therefore, to be safe, we pass a known quiet - // NaN if u is any kind of NaN. The check above only assumes - // IEEE 754-1985 to be valid. - NAN - } else { - unsafe { transmute(u) } - } -} - #[cfg(test)] mod test { extern crate quickcheck; @@ -2796,35 +2728,6 @@ mod test { let n = LittleEndian::read_uint(&[1, 2, 3, 4, 5, 6, 7, 8], 5); assert_eq!(n, 0x0504030201); } - - #[test] - fn read_snan() { - use core::f32; - use core::f64; - use core::mem::transmute; - - use {ByteOrder, BigEndian, LittleEndian}; - - let sf = BigEndian::read_f32(&[0xFF, 0x80, 0x00, 0x01]); - let sbits: u32 = unsafe { transmute(sf) }; - assert_eq!(sbits, unsafe { transmute(f32::NAN) }); - assert_eq!(sf.classify(), ::core::num::FpCategory::Nan); - - let df = BigEndian::read_f64(&[0x7F, 0xF0, 0, 0, 0, 0, 0, 0x01]); - let dbits: u64 = unsafe { ::core::mem::transmute(df) }; - assert_eq!(dbits, unsafe { transmute(f64::NAN) }); - assert_eq!(df.classify(), ::core::num::FpCategory::Nan); - - let sf = LittleEndian::read_f32(&[0x01, 0x00, 0x80, 0xFF]); - let sbits: u32 = unsafe { transmute(sf) }; - assert_eq!(sbits, unsafe { transmute(f32::NAN) }); - assert_eq!(sf.classify(), ::core::num::FpCategory::Nan); - - let df = LittleEndian::read_f64(&[0x01, 0, 0, 0, 0, 0, 0xF0, 0x7F]); - let dbits: u64 = unsafe { ::core::mem::transmute(df) }; - assert_eq!(dbits, unsafe { transmute(f64::NAN) }); - assert_eq!(df.classify(), ::core::num::FpCategory::Nan); - } } #[cfg(test)] @@ -2865,9 +2768,8 @@ mod stdtests { fn prop(n: $ty_int) -> bool { let mut wtr = vec![]; wtr.$write::(n.clone()).unwrap(); - let mut rdr = Vec::new(); - rdr.extend(wtr[wtr.len()-$bytes..].iter().map(|&x| x)); - let mut rdr = Cursor::new(rdr); + let offset = wtr.len() - $bytes; + let mut rdr = Cursor::new(&mut wtr[offset..]); n == rdr.$read::($bytes).unwrap() } qc_sized(prop as fn($ty_int) -> bool, $max); @@ -2889,7 +2791,12 @@ mod stdtests { fn prop(n: $ty_int) -> bool { let mut wtr = vec![]; wtr.$write::(n.clone()).unwrap(); - let mut rdr = Cursor::new(wtr); + let offset = if cfg!(target_endian = "big") { + wtr.len() - $bytes + } else { + 0 + }; + let mut rdr = Cursor::new(&mut wtr[offset..]); n == rdr.$read::($bytes).unwrap() } qc_sized(prop as fn($ty_int) -> bool, $max); diff --git a/third_party/rust/dwrote/.cargo-checksum.json b/third_party/rust/dwrote/.cargo-checksum.json index c96d7485093d..fb8cfe8b9745 100644 --- a/third_party/rust/dwrote/.cargo-checksum.json +++ b/third_party/rust/dwrote/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"48059bb4b7efd1a6c2d659fa909888f2c8b5d34f0bd8027b4aefaae9b8d0586e","README.md":"d69d75705e2582721cbfb2d3b4b2af052c71679057a0b2ac53a22c03f1755bba","appveyor.yml":"2c7b2468dc69bef84860b8900024cb6e1a1c52f6fe1232e8ccd83caaf7c231ca","src/bitmap_render_target.rs":"d3b229f85a9804ac52976431657727b410e7d5253283df046e46d98c196f0a3a","src/com_helpers.rs":"fccb4b36379ae3454a88aa32a8e5c09e46ef5f5626266dde1fe5f40a992de39c","src/comptr.rs":"218435689f505769686e07cfc5428852dda90b849a0d48e670f632307f5edc7c","src/font.rs":"9bdf3134c6ad3639eab3da4419c9b43aad2673797f6fdc65841da2c82e1f3af4","src/font_collection.rs":"969fa3abf141dc3504774886f4783fda4a74cd5a198c643f8a77fc1af4e75258","src/font_face.rs":"9506ca579345ab2b6b5615fc75f8f431e2bb0dbd93123d1d2a21a73c851a5427","src/font_family.rs":"403da9f8f9903cbe7f9f79636497b273f9885e200f53af99f9d4e483f11d6889","src/font_file.rs":"60ad02fc25765a2c113175ea372e98a2be0d84aa65fef9246b6a0192e63ff708","src/font_file_loader_impl.rs":"0d304ad99ff1e6874510a1498223329d798ff75b417e3db7e823a695003dfe92","src/gdi_interop.rs":"98922996afc5b8c8304cb65e7c965419003825dfa172a3e11fe69bf3d768551c","src/glyph_run_analysis.rs":"d30d8b41b047815ab5770c730b7a6d09939f2347b4a4257b87bebec08a5794fe","src/helpers.rs":"5d6f164468234ca8806dc1cea117b42dbfae80cc4c9ae965cb0556efdb364682","src/lib.rs":"07dae7e9a6b8e2970917eade968490e2af90110047a0e16f539647269b12f439","src/rendering_params.rs":"be1d1c433f76926c285d8ecdb747c5d9cc6a6c10c1a1890c0760cd99755ed471","src/test.rs":"d77e45f8866abeea070cbbafd4cbde62d875292e8d191310a04c70091978547c","src/types.rs":"784235c15d61fb0d001373575169aa473c92af18dcbc1709a5b2bbaa3a7ceb22"},"package":"36e3b27cd0b8a68e00f07e8d8e1e4f4d8a6b8b873290a734f63bd56d792d23e1"} \ No newline at end of file +{"files":{"Cargo.toml":"0b4c8dcf0a46bb7a21d3872485dce2bd0a91fa3003b1a87ecee8e418a95b5f94","README.md":"d69d75705e2582721cbfb2d3b4b2af052c71679057a0b2ac53a22c03f1755bba","appveyor.yml":"2c7b2468dc69bef84860b8900024cb6e1a1c52f6fe1232e8ccd83caaf7c231ca","src/bitmap_render_target.rs":"d3b229f85a9804ac52976431657727b410e7d5253283df046e46d98c196f0a3a","src/com_helpers.rs":"fccb4b36379ae3454a88aa32a8e5c09e46ef5f5626266dde1fe5f40a992de39c","src/comptr.rs":"218435689f505769686e07cfc5428852dda90b849a0d48e670f632307f5edc7c","src/font.rs":"9bdf3134c6ad3639eab3da4419c9b43aad2673797f6fdc65841da2c82e1f3af4","src/font_collection.rs":"969fa3abf141dc3504774886f4783fda4a74cd5a198c643f8a77fc1af4e75258","src/font_face.rs":"97bb3f67516b766fb3522f6fb07836cd55020adecc814353d2da35f885c66a85","src/font_family.rs":"403da9f8f9903cbe7f9f79636497b273f9885e200f53af99f9d4e483f11d6889","src/font_file.rs":"60ad02fc25765a2c113175ea372e98a2be0d84aa65fef9246b6a0192e63ff708","src/font_file_loader_impl.rs":"0d304ad99ff1e6874510a1498223329d798ff75b417e3db7e823a695003dfe92","src/gdi_interop.rs":"98922996afc5b8c8304cb65e7c965419003825dfa172a3e11fe69bf3d768551c","src/glyph_run_analysis.rs":"d30d8b41b047815ab5770c730b7a6d09939f2347b4a4257b87bebec08a5794fe","src/helpers.rs":"5d6f164468234ca8806dc1cea117b42dbfae80cc4c9ae965cb0556efdb364682","src/lib.rs":"af8bae7d775a9ddeb956353e572b553aa2819f7183bb3aed8dde98016f6b4c97","src/rendering_params.rs":"be1d1c433f76926c285d8ecdb747c5d9cc6a6c10c1a1890c0760cd99755ed471","src/test.rs":"d77e45f8866abeea070cbbafd4cbde62d875292e8d191310a04c70091978547c","src/types.rs":"784235c15d61fb0d001373575169aa473c92af18dcbc1709a5b2bbaa3a7ceb22"},"package":"a207eb7b40e25d1d28dc679f451d321fb6954b73ceaa47986702575865469461"} \ No newline at end of file diff --git a/third_party/rust/dwrote/Cargo.toml b/third_party/rust/dwrote/Cargo.toml index b0b9eaf1c04c..4b2abe8eb906 100644 --- a/third_party/rust/dwrote/Cargo.toml +++ b/third_party/rust/dwrote/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "dwrote" -version = "0.4.0" +version = "0.4.1" authors = ["Vladimir Vukicevic "] description = "Lightweight binding to DirectWrite." license = "MPL-2.0" @@ -20,13 +20,13 @@ repository = "https://github.com/servo/dwrote-rs" [lib] name = "dwrote" -[dependencies.serde_derive] -version = "1.0" - [dependencies.kernel32-sys] version = "0.2" -[dependencies.serde] +[dependencies.lazy_static] +version = "0.2" + +[dependencies.serde_derive] version = "1.0" [dependencies.winapi] @@ -35,8 +35,8 @@ version = "0.2" [dependencies.gdi32-sys] version = "0.2" -[dependencies.lazy_static] -version = "0.2" +[dependencies.serde] +version = "1.0" [dependencies.libc] version = "0.2" diff --git a/third_party/rust/dwrote/src/font_face.rs b/third_party/rust/dwrote/src/font_face.rs index 8d1d02669721..7e5a1230d70a 100644 --- a/third_party/rust/dwrote/src/font_face.rs +++ b/third_party/rust/dwrote/src/font_face.rs @@ -8,7 +8,7 @@ use std::cell::UnsafeCell; use std::mem::zeroed; use comptr::ComPtr; -use super::{FontMetrics, FontFile, DefaultDWriteRenderParams}; +use super::{FontMetrics, FontFile, DefaultDWriteRenderParams, DWriteFactory}; use winapi; @@ -35,21 +35,47 @@ impl FontFace { (*self.native.get()).as_ptr() } + unsafe fn get_raw_files(&self) -> Vec<*mut winapi::IDWriteFontFile> { + let mut number_of_files: u32 = 0; + let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut()); + assert!(hr == 0); + + let mut file_ptrs: Vec<*mut winapi::IDWriteFontFile> = + vec![ptr::null_mut(); number_of_files as usize]; + let hr = (*self.native.get()).GetFiles(&mut number_of_files, file_ptrs.as_mut_ptr()); + assert!(hr == 0); + file_ptrs + } + pub fn get_files(&self) -> Vec { unsafe { - let mut number_of_files: u32 = 0; - let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut()); - assert!(hr == 0); - - let mut file_ptrs: Vec<*mut winapi::IDWriteFontFile> = - vec![ptr::null_mut(); number_of_files as usize]; - let hr = (*self.native.get()).GetFiles(&mut number_of_files, file_ptrs.as_mut_ptr()); - assert!(hr == 0); - + let file_ptrs = self.get_raw_files(); file_ptrs.iter().map(|p| FontFile::take(ComPtr::already_addrefed(*p))).collect() } } + pub fn create_font_face_with_simulations(&self, simulations: winapi::DWRITE_FONT_SIMULATIONS) -> FontFace { + unsafe { + let file_ptrs = self.get_raw_files(); + let face_type = (*self.native.get()).GetType(); + let face_index = (*self.native.get()).GetIndex(); + let mut face: ComPtr = ComPtr::new(); + let hr = (*DWriteFactory()).CreateFontFace( + face_type, + file_ptrs.len() as u32, + file_ptrs.as_ptr(), + face_index, + simulations, + face.getter_addrefs() + ); + for p in file_ptrs { + let _ = ComPtr::::already_addrefed(p); + } + assert!(hr == 0); + FontFace::take(face) + } + } + pub fn get_glyph_count(&self) -> u16 { unsafe { (*self.native.get()).GetGlyphCount() diff --git a/third_party/rust/dwrote/src/lib.rs b/third_party/rust/dwrote/src/lib.rs index dd028ae2f4d5..60a2fffa36f1 100644 --- a/third_party/rust/dwrote/src/lib.rs +++ b/third_party/rust/dwrote/src/lib.rs @@ -57,6 +57,7 @@ pub use winapi::{DWRITE_FONT_SIMULATIONS_NONE, DWRITE_FONT_SIMULATIONS_BOLD, DWRITE_FONT_SIMULATIONS_OBLIQUE}; pub use winapi::{DWRITE_TEXTURE_ALIASED_1x1, DWRITE_TEXTURE_CLEARTYPE_3x1}; +pub use winapi::{DWRITE_FONT_SIMULATIONS}; pub use winapi::{DWRITE_RENDERING_MODE}; pub use winapi::{DWRITE_MEASURING_MODE}; pub use winapi::{DWRITE_TEXTURE_TYPE}; diff --git a/third_party/rust/gleam/.cargo-checksum.json b/third_party/rust/gleam/.cargo-checksum.json index 32a34b83a118..5461ff99cc12 100644 --- a/third_party/rust/gleam/.cargo-checksum.json +++ b/third_party/rust/gleam/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".travis.yml":"29b74b95210896ce634c11a9037638668473b5a1b3b1716c505cb04dbb6341fa","COPYING":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"0417239a0f167b2c99eead895cdf7a46c39a84bbd9c172e31e99157e41f5a5d2","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"2de24b7458d6b88f20324303a48acf64a4f2bbfb83d2ec4d6ff2b4f4a1fd2275","build.rs":"d2a1c4b40ecbc29ddf005ec32f5b68f9242c8d54b703027bf3a1b605a0882d6a","src/gl.rs":"dea0447dd130a46d648f9526b927e0ba6d52b7ef5d1ec92471ac013119cbdee3","src/gl_fns.rs":"b9460d03ea3178672184bcb11e2075dffa8ce18b842be954d996bce9e701d62e","src/gles_fns.rs":"4344505df0aae7bded0048a2e4761920ddbd61acd0370d830d69c23a1f946fb1","src/lib.rs":"16610c19b45a3f26d56b379a3591aa2e4fc9477e7bd88f86b31c6ea32e834861"},"package":"8bd6aa276bc0bf40348728e916a82f8f9cc10b1922cabf9f2fe9cd5e8b98d55b"} \ No newline at end of file +{"files":{".travis.yml":"29b74b95210896ce634c11a9037638668473b5a1b3b1716c505cb04dbb6341fa","COPYING":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"0b1402356bfafe5636331294d4d6e65a9bdb953f8497d41f757f1c7b70e047ad","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"2de24b7458d6b88f20324303a48acf64a4f2bbfb83d2ec4d6ff2b4f4a1fd2275","build.rs":"6c391c6f2dd99a7d1c233a3c4b78a2c8355d5e59c74c15ad9d2ec4c5448ea67a","src/gl.rs":"dea0447dd130a46d648f9526b927e0ba6d52b7ef5d1ec92471ac013119cbdee3","src/gl_fns.rs":"b964929cc066751272cd79036683f525a67ee96fe021f8fdb99af4024fa799b6","src/gles_fns.rs":"45dd524107d20175fd1c4a420c91cc88820312d0a65b5b02dcb6082ca9b8a39a","src/lib.rs":"16610c19b45a3f26d56b379a3591aa2e4fc9477e7bd88f86b31c6ea32e834861"},"package":"dff613336334932baaa2759d001f14e06ea1a08a247c05962d1423aa0e89ee99"} \ No newline at end of file diff --git a/third_party/rust/gleam/Cargo.toml b/third_party/rust/gleam/Cargo.toml index 6c5896f46b58..919dff3fddc3 100644 --- a/third_party/rust/gleam/Cargo.toml +++ b/third_party/rust/gleam/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "gleam" -version = "0.4.14" +version = "0.4.15" authors = ["The Servo Project Developers"] build = "build.rs" description = "Generated OpenGL bindings and wrapper for Servo." diff --git a/third_party/rust/gleam/build.rs b/third_party/rust/gleam/build.rs index 6f62bd403845..c45a6a645eca 100644 --- a/third_party/rust/gleam/build.rs +++ b/third_party/rust/gleam/build.rs @@ -28,6 +28,8 @@ fn main() { "GL_EXT_texture_format_BGRA8888", "GL_OES_EGL_image", "GL_OES_EGL_image_external", + "GL_EXT_disjoint_timer_query", + "GL_EXT_debug_marker", ]; let gles_reg = Registry::new(Api::Gles2, (3, 0), Profile::Core, Fallbacks::All, gles_extensions); gles_reg.write_bindings(gl_generator::StructGenerator, &mut file_gles) diff --git a/third_party/rust/gleam/src/gl_fns.rs b/third_party/rust/gleam/src/gl_fns.rs index a3f7a18dbe41..b48df44bf3a3 100644 --- a/third_party/rust/gleam/src/gl_fns.rs +++ b/third_party/rust/gleam/src/gl_fns.rs @@ -149,11 +149,11 @@ impl Gl for GlFns { } fn gen_queries(&self, n: GLsizei) -> Vec { + let mut result = vec![0; n as usize]; unsafe { - let mut result = vec![0; n as usize]; self.ffi_gl_.GenQueries(n, result.as_mut_ptr()); - return result; } + result } fn begin_query(&self, target: GLenum, id: GLuint) { @@ -175,35 +175,35 @@ impl Gl for GlFns { } fn get_query_object_iv(&self, id: GLuint, pname: GLenum) -> i32 { + let mut result = 0; unsafe { - let mut result = 0; self.ffi_gl_.GetQueryObjectiv(id, pname, &mut result); - result } + result } fn get_query_object_uiv(&self, id: GLuint, pname: GLenum) -> u32 { + let mut result = 0; unsafe { - let mut result = 0; self.ffi_gl_.GetQueryObjectuiv(id, pname, &mut result); - result } + result } fn get_query_object_i64v(&self, id: GLuint, pname: GLenum) -> i64 { + let mut result = 0; unsafe { - let mut result = 0; self.ffi_gl_.GetQueryObjecti64v(id, pname, &mut result); - result } + result } fn get_query_object_ui64v(&self, id: GLuint, pname: GLenum) -> u64 { + let mut result = 0; unsafe { - let mut result = 0; self.ffi_gl_.GetQueryObjectui64v(id, pname, &mut result); - result } + result } fn delete_queries(&self, queries: &[GLuint]) { diff --git a/third_party/rust/gleam/src/gles_fns.rs b/third_party/rust/gleam/src/gles_fns.rs index 48f7a8021948..0e53d6aded30 100644 --- a/third_party/rust/gleam/src/gles_fns.rs +++ b/third_party/rust/gleam/src/gles_fns.rs @@ -145,49 +145,95 @@ impl Gl for GlesFns { } } - #[allow(unused_variables)] fn gen_queries(&self, n: GLsizei) -> Vec { - panic!("not supported") + if !self.ffi_gl_.GenQueriesEXT.is_loaded() { + return Vec::new(); + } + let mut result = vec![0; n as usize]; + unsafe { + self.ffi_gl_.GenQueriesEXT(n, result.as_mut_ptr()); + } + result } - #[allow(unused_variables)] fn begin_query(&self, target: GLenum, id: GLuint) { - panic!("not supported") + if !self.ffi_gl_.BeginQueryEXT.is_loaded() { + return; + } + unsafe { + self.ffi_gl_.BeginQueryEXT(target, id); + } } - #[allow(unused_variables)] fn end_query(&self, target: GLenum) { - panic!("not supported") + if !self.ffi_gl_.EndQueryEXT.is_loaded() { + return; + } + unsafe { + self.ffi_gl_.EndQueryEXT(target); + } } - #[allow(unused_variables)] fn query_counter(&self, id: GLuint, target: GLenum) { - panic!("not supported") + if !self.ffi_gl_.QueryCounterEXT.is_loaded() { + return; + } + unsafe { + self.ffi_gl_.QueryCounterEXT(id, target); + } } - #[allow(unused_variables)] fn get_query_object_iv(&self, id: GLuint, pname: GLenum) -> i32 { - panic!("not supported") + if !self.ffi_gl_.GetQueryObjectivEXT.is_loaded() { + return 0; + } + let mut result = 0; + unsafe { + self.ffi_gl_.GetQueryObjectivEXT(id, pname, &mut result); + } + result } - #[allow(unused_variables)] fn get_query_object_uiv(&self, id: GLuint, pname: GLenum) -> u32 { - panic!("not supported") + if !self.ffi_gl_.GetQueryObjectuivEXT.is_loaded() { + return 0; + } + let mut result = 0; + unsafe { + self.ffi_gl_.GetQueryObjectuivEXT(id, pname, &mut result); + } + result } - #[allow(unused_variables)] fn get_query_object_i64v(&self, id: GLuint, pname: GLenum) -> i64 { - panic!("not supported") + if !self.ffi_gl_.GetQueryObjecti64vEXT.is_loaded() { + return 0; + } + let mut result = 0; + unsafe { + self.ffi_gl_.GetQueryObjecti64vEXT(id, pname, &mut result); + } + result } - #[allow(unused_variables)] fn get_query_object_ui64v(&self, id: GLuint, pname: GLenum) -> u64 { - panic!("not supported") + if !self.ffi_gl_.GetQueryObjectui64vEXT.is_loaded() { + return 0; + } + let mut result = 0; + unsafe { + self.ffi_gl_.GetQueryObjectui64vEXT(id, pname, &mut result); + } + result } - #[allow(unused_variables)] fn delete_queries(&self, queries: &[GLuint]) { - panic!("not supported") + if !self.ffi_gl_.DeleteQueriesEXT.is_loaded() { + return; + } + unsafe { + self.ffi_gl_.DeleteQueriesEXT(queries.len() as GLsizei, queries.as_ptr()); + } } fn delete_vertex_arrays(&self, vertex_arrays: &[GLuint]) { @@ -1486,16 +1532,28 @@ impl Gl for GlesFns { } } - #[allow(unused_variables)] fn insert_event_marker_ext(&self, message: &str) { + if self.ffi_gl_.InsertEventMarkerEXT.is_loaded() { + unsafe { + self.ffi_gl_.InsertEventMarkerEXT(message.len() as GLsizei, message.as_ptr() as *const _); + } + } } - #[allow(unused_variables)] fn push_group_marker_ext(&self, message: &str) { + if self.ffi_gl_.PushGroupMarkerEXT.is_loaded() { + unsafe { + self.ffi_gl_.PushGroupMarkerEXT(message.len() as GLsizei, message.as_ptr() as *const _); + } + } } - #[allow(unused_variables)] fn pop_group_marker_ext(&self) { + if self.ffi_gl_.PopGroupMarkerEXT.is_loaded() { + unsafe { + self.ffi_gl_.PopGroupMarkerEXT(); + } + } } fn fence_sync(&self, condition: GLenum, flags: GLbitfield) -> GLsync { diff --git a/toolkit/library/gtest/rust/Cargo.lock b/toolkit/library/gtest/rust/Cargo.lock index 16e79f6382da..cdd25a252b35 100644 --- a/toolkit/library/gtest/rust/Cargo.lock +++ b/toolkit/library/gtest/rust/Cargo.lock @@ -101,7 +101,7 @@ name = "bincode" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -111,7 +111,7 @@ name = "bincode" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -160,7 +160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.0.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -168,7 +168,7 @@ name = "bytes" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -391,7 +391,7 @@ dependencies = [ [[package]] name = "dwrote" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -521,7 +521,7 @@ name = "fxhash" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -598,7 +598,7 @@ dependencies = [ [[package]] name = "gleam" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -823,7 +823,7 @@ name = "mp4parse" version = "0.9.0" dependencies = [ "bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "mp4parse_fallible 0.0.1", "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -836,7 +836,7 @@ version = "0.1.0" name = "mp4parse_capi" version = "0.9.0" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "mp4parse 0.9.0", "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1250,7 +1250,7 @@ dependencies = [ "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bindgen 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1495,20 +1495,20 @@ dependencies = [ [[package]] name = "webrender" -version = "0.54.0" +version = "0.55.0" dependencies = [ "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1516,20 +1516,20 @@ dependencies = [ "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender_api 0.54.0", + "webrender_api 0.55.0", ] [[package]] name = "webrender_api" -version = "0.54.0" +version = "0.55.0" dependencies = [ "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1542,14 +1542,14 @@ dependencies = [ "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender 0.54.0", + "webrender 0.55.0", ] [[package]] @@ -1603,7 +1603,7 @@ dependencies = [ "checksum bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5cde24d1b2e2216a726368b2363a273739c91f4e3eb4e0dd12d672d396ad989" "checksum bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80b13e2ab064ff3aa0bdbf1eff533f9822dc37899821f5f98c67f263eab51707" "checksum boxfnonce 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8380105befe91099e6f69206164072c05bc92427ff6aa8a5171388317346dd75" -"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" +"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6" "checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d" "checksum cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c47d456a36ebf0536a6705c83c1cbbcb9255fbc1d905a6ded104f479268a29" @@ -1624,7 +1624,7 @@ dependencies = [ "checksum darling_macro 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a86ec160aa0c3dd492dd4a14ec8104ad8f1a9400a820624db857998cc1f80f9" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d" -"checksum dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36e3b27cd0b8a68e00f07e8d8e1e4f4d8a6b8b873290a734f63bd56d792d23e1" +"checksum dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a207eb7b40e25d1d28dc679f451d321fb6954b73ceaa47986702575865469461" "checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a" "checksum encoding_c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "93ec52324ca72f423237a413ca0e1c60654c8b3d0934fcd5fd888508dfcc4ba7" "checksum encoding_rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f5215aabf22b83153be3ee44dfe3f940214541b2ce13d419c55e7a115c8c51a9" @@ -1641,7 +1641,7 @@ dependencies = [ "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" "checksum gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75d69f914b49d9ff32fdf394cbd798f8c716d74fd19f9cc29da3e99797b2a78d" -"checksum gleam 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8bd6aa276bc0bf40348728e916a82f8f9cc10b1922cabf9f2fe9cd5e8b98d55b" +"checksum gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "dff613336334932baaa2759d001f14e06ea1a08a247c05962d1423aa0e89ee99" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4" "checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa" diff --git a/toolkit/library/rust/Cargo.lock b/toolkit/library/rust/Cargo.lock index 8b2a3aae3218..c388c6f99ee0 100644 --- a/toolkit/library/rust/Cargo.lock +++ b/toolkit/library/rust/Cargo.lock @@ -101,7 +101,7 @@ name = "bincode" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -111,7 +111,7 @@ name = "bincode" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -160,7 +160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.0.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -168,7 +168,7 @@ name = "bytes" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -391,7 +391,7 @@ dependencies = [ [[package]] name = "dwrote" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -521,7 +521,7 @@ name = "fxhash" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -597,7 +597,7 @@ dependencies = [ [[package]] name = "gleam" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -822,7 +822,7 @@ name = "mp4parse" version = "0.9.0" dependencies = [ "bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "mp4parse_fallible 0.0.1", "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -831,7 +831,7 @@ dependencies = [ name = "mp4parse_capi" version = "0.9.0" dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "mp4parse 0.9.0", "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1242,7 +1242,7 @@ dependencies = [ "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bindgen 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1507,20 +1507,20 @@ dependencies = [ [[package]] name = "webrender" -version = "0.54.0" +version = "0.55.0" dependencies = [ "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1528,20 +1528,20 @@ dependencies = [ "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender_api 0.54.0", + "webrender_api 0.55.0", ] [[package]] name = "webrender_api" -version = "0.54.0" +version = "0.55.0" dependencies = [ "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1554,14 +1554,14 @@ dependencies = [ "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender 0.54.0", + "webrender 0.55.0", ] [[package]] @@ -1615,7 +1615,7 @@ dependencies = [ "checksum bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5cde24d1b2e2216a726368b2363a273739c91f4e3eb4e0dd12d672d396ad989" "checksum bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80b13e2ab064ff3aa0bdbf1eff533f9822dc37899821f5f98c67f263eab51707" "checksum boxfnonce 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8380105befe91099e6f69206164072c05bc92427ff6aa8a5171388317346dd75" -"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" +"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6" "checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d" "checksum cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c47d456a36ebf0536a6705c83c1cbbcb9255fbc1d905a6ded104f479268a29" @@ -1636,7 +1636,7 @@ dependencies = [ "checksum darling_macro 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a86ec160aa0c3dd492dd4a14ec8104ad8f1a9400a820624db857998cc1f80f9" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d" -"checksum dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36e3b27cd0b8a68e00f07e8d8e1e4f4d8a6b8b873290a734f63bd56d792d23e1" +"checksum dwrote 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a207eb7b40e25d1d28dc679f451d321fb6954b73ceaa47986702575865469461" "checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a" "checksum encoding_c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "93ec52324ca72f423237a413ca0e1c60654c8b3d0934fcd5fd888508dfcc4ba7" "checksum encoding_rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f5215aabf22b83153be3ee44dfe3f940214541b2ce13d419c55e7a115c8c51a9" @@ -1653,7 +1653,7 @@ dependencies = [ "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" "checksum gl_generator 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75d69f914b49d9ff32fdf394cbd798f8c716d74fd19f9cc29da3e99797b2a78d" -"checksum gleam 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8bd6aa276bc0bf40348728e916a82f8f9cc10b1922cabf9f2fe9cd5e8b98d55b" +"checksum gleam 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "dff613336334932baaa2759d001f14e06ea1a08a247c05962d1423aa0e89ee99" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4" "checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa" From 03a6e9d255423651ba652d5463f7367107587923 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Tue, 28 Nov 2017 10:25:27 -0500 Subject: [PATCH 036/219] Bug 1421275 - Pass FontInstanceFlags to WebRender. r=jrmuizel This patch was originally developed on bug 1418564. MozReview-Commit-ID: 53oydIqjhvF --HG-- extra : rebase_source : 8980cc947b3b8c46a75d032e7e557f39bae08b97 --- gfx/2d/ScaledFontDWrite.cpp | 28 +++++++++--------- gfx/2d/ScaledFontFontconfig.cpp | 26 +++++------------ gfx/2d/ScaledFontMac.cpp | 37 ++++++++++++------------ gfx/webrender_bindings/webrender_ffi.h | 40 ++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 52 deletions(-) diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp index cbab18181ba8..b71e10404e1a 100644 --- a/gfx/2d/ScaledFontDWrite.cpp +++ b/gfx/2d/ScaledFontDWrite.cpp @@ -391,21 +391,21 @@ ScaledFontDWrite::GetWRFontInstanceOptions(Maybe* aOutO Maybe* aOutPlatformOptions, std::vector* aOutVariations) { - AntialiasMode aaMode = GetDefaultAAMode(); - if (aaMode != AntialiasMode::SUBPIXEL) { - wr::FontInstanceOptions options; - options.render_mode = - aaMode == AntialiasMode::NONE ? wr::FontRenderMode::Mono : wr::FontRenderMode::Alpha; - options.subpx_dir = wr::SubpixelDirection::Horizontal; - options.synthetic_italics = false; - options.bg_color = wr::ToColorU(Color()); - *aOutOptions = Some(options); + wr::FontInstanceOptions options; + options.render_mode = wr::ToFontRenderMode(GetDefaultAAMode()); + options.subpx_dir = wr::SubpixelDirection::Horizontal; + options.flags = 0; + if (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD) { + options.flags |= wr::FontInstanceFlags::SYNTHETIC_BOLD; } - - wr::FontInstancePlatformOptions platformOptions; - platformOptions.use_embedded_bitmap = UseEmbeddedBitmaps(); - platformOptions.force_gdi_rendering = ForceGDIMode(); - *aOutPlatformOptions = Some(platformOptions); + if (UseEmbeddedBitmaps()) { + options.flags |= wr::FontInstanceFlags::EMBEDDED_BITMAPS; + } + if (ForceGDIMode()) { + options.flags |= wr::FontInstanceFlags::FORCE_GDI; + } + options.bg_color = wr::ToColorU(Color()); + *aOutOptions = Some(options); return true; } diff --git a/gfx/2d/ScaledFontFontconfig.cpp b/gfx/2d/ScaledFontFontconfig.cpp index 039ca2fc54d7..df22211cfd89 100644 --- a/gfx/2d/ScaledFontFontconfig.cpp +++ b/gfx/2d/ScaledFontFontconfig.cpp @@ -16,17 +16,6 @@ #include namespace mozilla { -namespace wr { - enum { - FONT_FORCE_AUTOHINT = 1 << 0, - FONT_NO_AUTOHINT = 1 << 1, - FONT_EMBEDDED_BITMAP = 1 << 2, - FONT_EMBOLDEN = 1 << 3, - FONT_VERTICAL_LAYOUT = 1 << 4, - FONT_SUBPIXEL_BGR = 1 << 5 - }; -} - namespace gfx { // On Linux and Android our "platform" font is a cairo_scaled_font_t and we use @@ -252,25 +241,24 @@ ScaledFontFontconfig::GetWRFontInstanceOptions(Maybe* a wr::FontInstanceOptions options; options.render_mode = wr::FontRenderMode::Alpha; options.subpx_dir = wr::SubpixelDirection::Horizontal; - options.synthetic_italics = false; + options.flags = 0; options.bg_color = wr::ToColorU(Color()); wr::FontInstancePlatformOptions platformOptions; - platformOptions.flags = 0; platformOptions.lcd_filter = wr::FontLCDFilter::Legacy; platformOptions.hinting = wr::FontHinting::Normal; FcBool autohint; if (FcPatternGetBool(mPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch && autohint) { - platformOptions.flags |= wr::FONT_FORCE_AUTOHINT; + options.flags |= wr::FontInstanceFlags::FORCE_AUTOHINT; } FcBool embolden; if (FcPatternGetBool(mPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch && embolden) { - platformOptions.flags |= wr::FONT_EMBOLDEN; + options.flags |= wr::FontInstanceFlags::SYNTHETIC_BOLD; } FcBool vertical; if (FcPatternGetBool(mPattern, FC_VERTICAL_LAYOUT, 0, &vertical) == FcResultMatch && vertical) { - platformOptions.flags |= wr::FONT_VERTICAL_LAYOUT; + options.flags |= wr::FontInstanceFlags::VERTICAL_LAYOUT; } FcBool antialias; @@ -288,7 +276,7 @@ ScaledFontFontconfig::GetWRFontInstanceOptions(Maybe* a } platformOptions.hinting = wr::FontHinting::LCD; if (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR) { - platformOptions.flags |= wr::FONT_SUBPIXEL_BGR; + options.flags |= wr::FontInstanceFlags::SUBPIXEL_BGR; } break; case FC_RGBA_NONE: @@ -323,13 +311,13 @@ ScaledFontFontconfig::GetWRFontInstanceOptions(Maybe* a // Otherwise, disable embedded bitmaps unless explicitly enabled. FcBool bitmap; if (FcPatternGetBool(mPattern, FC_EMBEDDED_BITMAP, 0, &bitmap) == FcResultMatch && bitmap) { - platformOptions.flags |= wr::FONT_EMBEDDED_BITMAP; + options.flags |= wr::FontInstanceFlags::EMBEDDED_BITMAPS; } } else { options.render_mode = wr::FontRenderMode::Mono; options.subpx_dir = wr::SubpixelDirection::None; platformOptions.hinting = wr::FontHinting::Mono; - platformOptions.flags |= wr::FONT_EMBEDDED_BITMAP; + options.flags |= wr::FontInstanceFlags::EMBEDDED_BITMAPS; } FcBool hinting; diff --git a/gfx/2d/ScaledFontMac.cpp b/gfx/2d/ScaledFontMac.cpp index f83d4fc94e03..5061d4d6ddff 100644 --- a/gfx/2d/ScaledFontMac.cpp +++ b/gfx/2d/ScaledFontMac.cpp @@ -390,13 +390,13 @@ GetVariationsForCTFont(CTFontRef aCTFont, std::vector* aOutVariat bool ScaledFontMac::GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) { - // Collect any variation settings that were incorporated into the CTFont. - std::vector variations; - if (!GetVariationsForCTFont(mCTFont, &variations)) { - return false; - } - aCb(nullptr, 0, variations.data(), variations.size(), aBaton); - return true; + // Collect any variation settings that were incorporated into the CTFont. + std::vector variations; + if (!GetVariationsForCTFont(mCTFont, &variations)) { + return false; + } + aCb(nullptr, 0, variations.data(), variations.size(), aBaton); + return true; } bool @@ -404,19 +404,18 @@ ScaledFontMac::GetWRFontInstanceOptions(Maybe* aOutOpti Maybe* aOutPlatformOptions, std::vector* aOutVariations) { - GetVariationsForCTFont(mCTFont, aOutVariations); + GetVariationsForCTFont(mCTFont, aOutVariations); - wr::FontInstanceOptions options; - options.render_mode = wr::FontRenderMode::Subpixel; - options.subpx_dir = wr::SubpixelDirection::Horizontal; - options.synthetic_italics = false; - options.bg_color = wr::ToColorU(mFontSmoothingBackgroundColor); - *aOutOptions = Some(options); - - wr::FontInstancePlatformOptions platformOptions; - platformOptions.font_smoothing = mUseFontSmoothing; - *aOutPlatformOptions = Some(platformOptions); - return true; + wr::FontInstanceOptions options; + options.render_mode = wr::FontRenderMode::Subpixel; + options.subpx_dir = wr::SubpixelDirection::Horizontal; + options.flags = 0; + if (mUseFontSmoothing) { + options.flags |= wr::FontInstanceFlags::FONT_SMOOTHING; + } + options.bg_color = wr::ToColorU(mFontSmoothingBackgroundColor); + *aOutOptions = Some(options); + return true; } static CFDictionaryRef diff --git a/gfx/webrender_bindings/webrender_ffi.h b/gfx/webrender_bindings/webrender_ffi.h index a4336b7df10e..eecc13509313 100644 --- a/gfx/webrender_bindings/webrender_ffi.h +++ b/gfx/webrender_bindings/webrender_ffi.h @@ -29,6 +29,46 @@ void* get_proc_address_from_glcontext(void* glcontext_ptr, const char* procname) void gecko_profiler_register_thread(const char* threadname); void gecko_profiler_unregister_thread(); +// Prelude of types necessary before including webrender_ffi_generated.h +namespace mozilla { +namespace wr { + +struct FontInstanceFlags { + uint32_t bits; + + bool operator==(const FontInstanceFlags& aOther) const { + return bits == aOther.bits; + } + + FontInstanceFlags& operator=(uint32_t aBits) { + bits = aBits; + return *this; + } + + FontInstanceFlags& operator|=(uint32_t aBits) { + bits |= aBits; + return *this; + } + + enum : uint32_t { + SYNTHETIC_ITALICS = 1 << 0, + SYNTHETIC_BOLD = 1 << 1, + EMBEDDED_BITMAPS = 1 << 2, + SUBPIXEL_BGR = 1 << 3, + + FORCE_GDI = 1 << 16, + + FONT_SMOOTHING = 1 << 16, + + FORCE_AUTOHINT = 1 << 16, + NO_AUTOHINT = 1 << 17, + VERTICAL_LAYOUT = 1 << 18 + }; +}; + +} // namespace wr +} // namespace mozilla + } // extern "C" // Some useful defines to stub out webrender binding functions for when we From e782a0379ff73cd93ebf7f9810c59f8f099979a1 Mon Sep 17 00:00:00 2001 From: Munro Mengjue Chiang Date: Thu, 30 Nov 2017 15:44:20 +0800 Subject: [PATCH 037/219] Bug 1388219 - add a nsTArray mTargetCapability to record each track target capability. r=jib MozReview-Commit-ID: E8ZCmXEDxKs --HG-- extra : rebase_source : 5cab9cdb5cc1a67d6cf4c0b5c5c7caef5cfe7ea0 --- dom/media/webrtc/MediaEngine.h | 14 ++- .../webrtc/MediaEngineCameraVideoSource.cpp | 48 ++++++++-- .../webrtc/MediaEngineCameraVideoSource.h | 34 +++++++- .../webrtc/MediaEngineRemoteVideoSource.cpp | 87 ++++++++++++++----- .../webrtc/MediaEngineRemoteVideoSource.h | 10 ++- dom/media/webrtc/MediaEngineWebRTC.h | 1 + dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 1 + dom/media/webrtc/MediaTrackConstraints.cpp | 22 +++++ dom/media/webrtc/MediaTrackConstraints.h | 15 +++- 9 files changed, 195 insertions(+), 37 deletions(-) diff --git a/dom/media/webrtc/MediaEngine.h b/dom/media/webrtc/MediaEngine.h index 000d6d838121..0c57c361a1fd 100644 --- a/dom/media/webrtc/MediaEngine.h +++ b/dom/media/webrtc/MediaEngine.h @@ -217,6 +217,7 @@ public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AllocationHandle); protected: ~AllocationHandle() {} + static uint64_t sId; public: AllocationHandle(const dom::MediaTrackConstraints& aConstraints, const mozilla::ipc::PrincipalInfo& aPrincipalInfo, @@ -226,11 +227,15 @@ public: : mConstraints(aConstraints), mPrincipalInfo(aPrincipalInfo), mPrefs(aPrefs), +#ifdef MOZ_WEBRTC + mId(sId++), +#endif mDeviceId(aDeviceId) {} public: NormalizedConstraints mConstraints; mozilla::ipc::PrincipalInfo mPrincipalInfo; MediaEnginePrefs mPrefs; + uint64_t mId; nsString mDeviceId; }; @@ -366,6 +371,7 @@ protected: virtual nsresult UpdateSingleSource(const AllocationHandle* aHandle, const NormalizedConstraints& aNetConstraints, + const NormalizedConstraints& aNewConstraint, const MediaEnginePrefs& aPrefs, const nsString& aDeviceId, const char** aOutBadConstraint) { @@ -394,6 +400,7 @@ protected: // aHandle and/or aConstraintsUpdate may be nullptr (see below) AutoTArray allConstraints; + AutoTArray updatedConstraint; for (auto& registered : mRegisteredHandles) { if (aConstraintsUpdate && registered.get() == aHandle) { continue; // Don't count old constraints @@ -402,9 +409,13 @@ protected: } if (aConstraintsUpdate) { allConstraints.AppendElement(aConstraintsUpdate); + updatedConstraint.AppendElement(aConstraintsUpdate); } else if (aHandle) { // In the case of AddShareOfSingleSource, the handle isn't registered yet. allConstraints.AppendElement(&aHandle->mConstraints); + updatedConstraint.AppendElement(&aHandle->mConstraints); + } else { + updatedConstraint.AppendElements(allConstraints); } NormalizedConstraints netConstraints(allConstraints); @@ -413,7 +424,8 @@ protected: return NS_ERROR_FAILURE; } - nsresult rv = UpdateSingleSource(aHandle, netConstraints, aPrefs, aDeviceId, + NormalizedConstraints newConstraint(updatedConstraint); + nsresult rv = UpdateSingleSource(aHandle, netConstraints, newConstraint, aPrefs, aDeviceId, aOutBadConstraint); if (NS_FAILED(rv)) { return rv; diff --git a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp index 8d182a7d4cbb..25aedad88d04 100644 --- a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp @@ -54,6 +54,19 @@ MediaEngineCameraVideoSource::GetCapability(size_t aIndex, aOut = mHardcodedCapabilities.SafeElementAt(aIndex, webrtc::CaptureCapability()); } +uint32_t +MediaEngineCameraVideoSource::GetDistance( + const webrtc::CaptureCapability& aCandidate, + const NormalizedConstraintSet &aConstraints, + const nsString& aDeviceId, + const DistanceCalculation aCalculate) const +{ + if (aCalculate == kFeasibility) { + return GetFeasibilityDistance(aCandidate, aConstraints, aDeviceId); + } + return GetFitnessDistance(aCandidate, aConstraints, aDeviceId); +} + uint32_t MediaEngineCameraVideoSource::GetFitnessDistance( const webrtc::CaptureCapability& aCandidate, @@ -75,6 +88,27 @@ MediaEngineCameraVideoSource::GetFitnessDistance( return uint32_t(std::min(distance, uint64_t(UINT32_MAX))); } +uint32_t +MediaEngineCameraVideoSource::GetFeasibilityDistance( + const webrtc::CaptureCapability& aCandidate, + const NormalizedConstraintSet &aConstraints, + const nsString& aDeviceId) const +{ + // Treat width|height|frameRate == 0 on capability as "can do any". + // This allows for orthogonal capabilities that are not in discrete steps. + + uint64_t distance = + uint64_t(FitnessDistance(aDeviceId, aConstraints.mDeviceId)) + + uint64_t(FitnessDistance(mFacingMode, aConstraints.mFacingMode)) + + uint64_t(aCandidate.width? FeasibilityDistance(int32_t(aCandidate.width), + aConstraints.mWidth) : 0) + + uint64_t(aCandidate.height? FeasibilityDistance(int32_t(aCandidate.height), + aConstraints.mHeight) : 0) + + uint64_t(aCandidate.maxFPS? FeasibilityDistance(double(aCandidate.maxFPS), + aConstraints.mFrameRate) : 0); + return uint32_t(std::min(distance, uint64_t(UINT32_MAX))); +} + // Find best capability by removing inferiors. May leave >1 of equal distance /* static */ void @@ -218,7 +252,9 @@ bool MediaEngineCameraVideoSource::ChooseCapability( const NormalizedConstraints &aConstraints, const MediaEnginePrefs &aPrefs, - const nsString& aDeviceId) + const nsString& aDeviceId, + webrtc::CaptureCapability& aCapability, + const DistanceCalculation aCalculate) { if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) { LOG(("ChooseCapability: prefs: %dx%d @%dfps", @@ -246,7 +282,7 @@ MediaEngineCameraVideoSource::ChooseCapability( auto& candidate = candidateSet[i]; webrtc::CaptureCapability cap; GetCapability(candidate.mIndex, cap); - candidate.mDistance = GetFitnessDistance(cap, aConstraints, aDeviceId); + candidate.mDistance = GetDistance(cap, aConstraints, aDeviceId, aCalculate); LogCapability("Capability", cap, candidate.mDistance); if (candidate.mDistance == UINT32_MAX) { candidateSet.RemoveElementAt(i); @@ -268,7 +304,7 @@ MediaEngineCameraVideoSource::ChooseCapability( auto& candidate = candidateSet[i]; webrtc::CaptureCapability cap; GetCapability(candidate.mIndex, cap); - if (GetFitnessDistance(cap, cs, aDeviceId) == UINT32_MAX) { + if (GetDistance(cap, cs, aDeviceId, aCalculate) == UINT32_MAX) { rejects.AppendElement(candidate); candidateSet.RemoveElementAt(i); } else { @@ -299,7 +335,7 @@ MediaEngineCameraVideoSource::ChooseCapability( for (auto& candidate : candidateSet) { webrtc::CaptureCapability cap; GetCapability(candidate.mIndex, cap); - candidate.mDistance = GetFitnessDistance(cap, normPrefs, aDeviceId); + candidate.mDistance = GetDistance(cap, normPrefs, aDeviceId, aCalculate); } TrimLessFitCandidates(candidateSet); } @@ -315,13 +351,13 @@ MediaEngineCameraVideoSource::ChooseCapability( if (cap.rawType == webrtc::RawVideoType::kVideoI420 || cap.rawType == webrtc::RawVideoType::kVideoYUY2 || cap.rawType == webrtc::RawVideoType::kVideoYV12) { - mCapability = cap; + aCapability = cap; found = true; break; } } if (!found) { - GetCapability(candidateSet[0].mIndex, mCapability); + GetCapability(candidateSet[0].mIndex, aCapability); } LogCapability("Chosen capability", mCapability, sameDistance); diff --git a/dom/media/webrtc/MediaEngineCameraVideoSource.h b/dom/media/webrtc/MediaEngineCameraVideoSource.h index 284f22be9ec1..d78bcaeed8ff 100644 --- a/dom/media/webrtc/MediaEngineCameraVideoSource.h +++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h @@ -24,6 +24,19 @@ namespace webrtc { namespace mozilla { +// Fitness distance is defined in +// https://www.w3.org/TR/2017/CR-mediacapture-streams-20171003/#dfn-selectsettings +// The main difference of feasibility and fitness distance is that if the +// constraint is required ('max', or 'exact'), and the settings dictionary's value +// for the constraint does not satisfy the constraint, the fitness distance is +// positive infinity. Given a continuous space of settings dictionaries comprising +// all discrete combinations of dimension and frame-rate related properties, +// the feasibility distance is still in keeping with the constraints algorithm. +enum DistanceCalculation { + kFitness, + kFeasibility +}; + class MediaEngineCameraVideoSource : public MediaEngineVideoSource { public: @@ -86,9 +99,16 @@ protected: TrackID aID, StreamTime delta, const PrincipalHandle& aPrincipalHandle); + uint32_t GetDistance(const webrtc::CaptureCapability& aCandidate, + const NormalizedConstraintSet &aConstraints, + const nsString& aDeviceId, + const DistanceCalculation aCalculate) const; uint32_t GetFitnessDistance(const webrtc::CaptureCapability& aCandidate, const NormalizedConstraintSet &aConstraints, const nsString& aDeviceId) const; + uint32_t GetFeasibilityDistance(const webrtc::CaptureCapability& aCandidate, + const NormalizedConstraintSet &aConstraints, + const nsString& aDeviceId) const; static void TrimLessFitCandidates(CapabilitySet& set); static void LogConstraints(const NormalizedConstraintSet& aConstraints); static void LogCapability(const char* aHeader, @@ -96,9 +116,13 @@ protected: uint32_t aDistance); virtual size_t NumCapabilities() const; virtual void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut) const; - virtual bool ChooseCapability(const NormalizedConstraints &aConstraints, - const MediaEnginePrefs &aPrefs, - const nsString& aDeviceId); + virtual bool ChooseCapability( + const NormalizedConstraints &aConstraints, + const MediaEnginePrefs &aPrefs, + const nsString& aDeviceId, + webrtc::CaptureCapability& aCapability, + const DistanceCalculation aCalculate + ); void SetName(nsString aName); void SetUUID(const char* aUUID); const nsCString& GetUUID() const; // protected access @@ -116,6 +140,8 @@ protected: nsTArray> mSources; // When this goes empty, we shut down HW nsTArray mPrincipalHandles; // Directly mapped to mSources. RefPtr mImage; + nsTArray mTargetCapabilities; + nsTArray mHandleIds; RefPtr mImageContainer; // end of data protected by mMonitor @@ -125,6 +151,8 @@ protected: TrackID mTrackID; webrtc::CaptureCapability mCapability; + webrtc::CaptureCapability mTargetCapability; + uint64_t mHandleId; mutable nsTArray mHardcodedCapabilities; private: diff --git a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp index f50b98a60d0b..e7024513cf77 100644 --- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp @@ -17,6 +17,8 @@ extern mozilla::LogModule* GetMediaManagerLog(); namespace mozilla { +uint64_t MediaEngineCameraVideoSource::AllocationHandle::sId = 0; + // These need a definition somewhere because template // code is allowed to take their address, and they aren't // guaranteed to have one without this. @@ -80,6 +82,8 @@ MediaEngineRemoteVideoSource::Shutdown() empty = mSources.IsEmpty(); if (empty) { MOZ_ASSERT(mPrincipalHandles.IsEmpty()); + MOZ_ASSERT(mTargetCapabilities.IsEmpty()); + MOZ_ASSERT(mHandleIds.IsEmpty()); break; } source = mSources[0]; @@ -126,6 +130,8 @@ MediaEngineRemoteVideoSource::Allocate( MonitorAutoLock lock(mMonitor); if (mSources.IsEmpty()) { MOZ_ASSERT(mPrincipalHandles.IsEmpty()); + MOZ_ASSERT(mTargetCapabilities.IsEmpty()); + MOZ_ASSERT(mHandleIds.IsEmpty()); LOG(("Video device %d reallocated", mCaptureIndex)); } else { LOG(("Video device %d allocated shared", mCaptureIndex)); @@ -172,7 +178,12 @@ MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID, MonitorAutoLock lock(mMonitor); mSources.AppendElement(aStream); mPrincipalHandles.AppendElement(aPrincipalHandle); + mTargetCapabilities.AppendElement(mTargetCapability); + mHandleIds.AppendElement(mHandleId); + MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); + MOZ_ASSERT(mSources.Length() == mTargetCapabilities.Length()); + MOZ_ASSERT(mSources.Length() == mHandleIds.Length()); } aStream->AddTrack(aID, 0, new VideoSegment(), SourceMediaStream::ADDTRACK_QUEUED); @@ -218,8 +229,12 @@ MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource, } MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); + MOZ_ASSERT(mSources.Length() == mTargetCapabilities.Length()); + MOZ_ASSERT(mSources.Length() == mHandleIds.Length()); mSources.RemoveElementAt(i); mPrincipalHandles.RemoveElementAt(i); + mTargetCapabilities.RemoveElementAt(i); + mHandleIds.RemoveElementAt(i); aSource->EndTrack(aID); @@ -262,18 +277,21 @@ nsresult MediaEngineRemoteVideoSource::UpdateSingleSource( const AllocationHandle* aHandle, const NormalizedConstraints& aNetConstraints, + const NormalizedConstraints& aNewConstraint, const MediaEnginePrefs& aPrefs, const nsString& aDeviceId, const char** aOutBadConstraint) { - if (!ChooseCapability(aNetConstraints, aPrefs, aDeviceId)) { - *aOutBadConstraint = FindBadConstraint(aNetConstraints, *this, aDeviceId); - return NS_ERROR_FAILURE; - } - switch (mState) { case kReleased: MOZ_ASSERT(aHandle); + mHandleId = aHandle->mId; + if (!ChooseCapability(aNetConstraints, aPrefs, aDeviceId, mCapability, kFitness)) { + *aOutBadConstraint = FindBadConstraint(aNetConstraints, *this, aDeviceId); + return NS_ERROR_FAILURE; + } + mTargetCapability = mCapability; + if (camera::GetChildAndCall(&camera::CamerasChild::AllocateCaptureDevice, mCapEngine, GetUUID().get(), kMaxUniqueIdLength, mCaptureIndex, @@ -286,18 +304,42 @@ MediaEngineRemoteVideoSource::UpdateSingleSource( break; case kStarted: - if (mCapability != mLastCapability) { - camera::GetChildAndCall(&camera::CamerasChild::StopCapture, - mCapEngine, mCaptureIndex); - if (camera::GetChildAndCall(&camera::CamerasChild::StartCapture, - mCapEngine, mCaptureIndex, mCapability, - this)) { - LOG(("StartCapture failed")); + { + size_t index = mHandleIds.NoIndex; + if (aHandle) { + mHandleId = aHandle->mId; + index = mHandleIds.IndexOf(mHandleId); + } + + if (!ChooseCapability(aNewConstraint, aPrefs, aDeviceId, mTargetCapability, + kFitness)) { + *aOutBadConstraint = FindBadConstraint(aNewConstraint, *this, aDeviceId); return NS_ERROR_FAILURE; } - SetLastCapability(mCapability); + + if (index != mHandleIds.NoIndex) { + mTargetCapabilities[index] = mTargetCapability; + } + + if (!ChooseCapability(aNetConstraints, aPrefs, aDeviceId, mCapability, + kFeasibility)) { + *aOutBadConstraint = FindBadConstraint(aNetConstraints, *this, aDeviceId); + return NS_ERROR_FAILURE; + } + + if (mCapability != mLastCapability) { + camera::GetChildAndCall(&camera::CamerasChild::StopCapture, + mCapEngine, mCaptureIndex); + if (camera::GetChildAndCall(&camera::CamerasChild::StartCapture, + mCapEngine, mCaptureIndex, mCapability, + this)) { + LOG(("StartCapture failed")); + return NS_ERROR_FAILURE; + } + SetLastCapability(mCapability); + } + break; } - break; default: LOG(("Video device %d in ignored state %d", mCaptureIndex, mState)); @@ -464,7 +506,9 @@ bool MediaEngineRemoteVideoSource::ChooseCapability( const NormalizedConstraints &aConstraints, const MediaEnginePrefs &aPrefs, - const nsString& aDeviceId) + const nsString& aDeviceId, + webrtc::CaptureCapability& aCapability, + const DistanceCalculation aCalculate) { AssertIsOnOwningThread(); @@ -477,15 +521,16 @@ MediaEngineRemoteVideoSource::ChooseCapability( // time (and may in fact change over time), so as a hack, we push ideal // and max constraints down to desktop_capture_impl.cc and finish the // algorithm there. - mCapability.width = (c.mWidth.mIdeal.valueOr(0) & 0xffff) << 16 | - (c.mWidth.mMax & 0xffff); - mCapability.height = (c.mHeight.mIdeal.valueOr(0) & 0xffff) << 16 | - (c.mHeight.mMax & 0xffff); - mCapability.maxFPS = c.mFrameRate.Clamp(c.mFrameRate.mIdeal.valueOr(aPrefs.mFPS)); + aCapability.width = + (c.mWidth.mIdeal.valueOr(0) & 0xffff) << 16 | (c.mWidth.mMax & 0xffff); + aCapability.height = + (c.mHeight.mIdeal.valueOr(0) & 0xffff) << 16 | (c.mHeight.mMax & 0xffff); + aCapability.maxFPS = + c.mFrameRate.Clamp(c.mFrameRate.mIdeal.valueOr(aPrefs.mFPS)); return true; } default: - return MediaEngineCameraVideoSource::ChooseCapability(aConstraints, aPrefs, aDeviceId); + return MediaEngineCameraVideoSource::ChooseCapability(aConstraints, aPrefs, aDeviceId, aCapability, aCalculate); } } diff --git a/dom/media/webrtc/MediaEngineRemoteVideoSource.h b/dom/media/webrtc/MediaEngineRemoteVideoSource.h index a8921f7b0b1b..738aeb244b76 100644 --- a/dom/media/webrtc/MediaEngineRemoteVideoSource.h +++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.h @@ -84,9 +84,12 @@ public: return mMediaSource; } - bool ChooseCapability(const NormalizedConstraints &aConstraints, - const MediaEnginePrefs &aPrefs, - const nsString& aDeviceId) override; + bool ChooseCapability( + const NormalizedConstraints &aConstraints, + const MediaEnginePrefs &aPrefs, + const nsString& aDeviceId, + webrtc::CaptureCapability& aCapability, + const DistanceCalculation aCalculate) override; void Refresh(int aIndex); @@ -107,6 +110,7 @@ private: nsresult UpdateSingleSource(const AllocationHandle* aHandle, const NormalizedConstraints& aNetConstraints, + const NormalizedConstraints& aNewConstraint, const MediaEnginePrefs& aPrefs, const nsString& aDeviceId, const char** aOutBadConstraint) override; diff --git a/dom/media/webrtc/MediaEngineWebRTC.h b/dom/media/webrtc/MediaEngineWebRTC.h index 177413b5eba5..3958255158e5 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.h +++ b/dom/media/webrtc/MediaEngineWebRTC.h @@ -566,6 +566,7 @@ private: nsresult UpdateSingleSource(const AllocationHandle* aHandle, const NormalizedConstraints& aNetConstraints, + const NormalizedConstraints& aNewConstraint, const MediaEnginePrefs& aPrefs, const nsString& aDeviceId, const char** aOutBadConstraint) override; diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index 9854a2482bd7..21349dc3e7d6 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -279,6 +279,7 @@ nsresult MediaEngineWebRTCMicrophoneSource::UpdateSingleSource( const AllocationHandle* aHandle, const NormalizedConstraints& aNetConstraints, + const NormalizedConstraints& aNewConstraint, /* Ignored */ const MediaEnginePrefs& aPrefs, const nsString& aDeviceId, const char** aOutBadConstraint) diff --git a/dom/media/webrtc/MediaTrackConstraints.cpp b/dom/media/webrtc/MediaTrackConstraints.cpp index d3764d38cb46..509b44518978 100644 --- a/dom/media/webrtc/MediaTrackConstraints.cpp +++ b/dom/media/webrtc/MediaTrackConstraints.cpp @@ -417,6 +417,28 @@ MediaConstraintsHelper::FitnessDistance(ValueType aN, std::max(std::abs(aN), std::abs(aRange.mIdeal.value())))); } +template +/* static */ uint32_t +MediaConstraintsHelper::FeasibilityDistance(ValueType aN, + const NormalizedRange& aRange) +{ + if (aRange.mMin > aN) { + return UINT32_MAX; + } + // We prefer larger resolution because now we support downscaling + if (aN == aRange.mIdeal.valueOr(aN)) { + return 0; + } + + if (aN > aRange.mIdeal.value()) { + return uint32_t(ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) / + std::max(std::abs(aN), std::abs(aRange.mIdeal.value())))); + } + + return 10000 + uint32_t(ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) / + std::max(std::abs(aN), std::abs(aRange.mIdeal.value())))); +} + // Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX /* static */ uint32_t diff --git a/dom/media/webrtc/MediaTrackConstraints.h b/dom/media/webrtc/MediaTrackConstraints.h index d589be0acc9d..228b67f6de16 100644 --- a/dom/media/webrtc/MediaTrackConstraints.h +++ b/dom/media/webrtc/MediaTrackConstraints.h @@ -85,12 +85,19 @@ public: return mMax >= aOther.mMin && mMin <= aOther.mMax; } void Intersect(const Range& aOther) { - MOZ_ASSERT(Intersects(aOther)); mMin = std::max(mMin, aOther.mMin); - mMax = std::min(mMax, aOther.mMax); + if (Intersects(aOther)) { + mMax = std::min(mMax, aOther.mMax); + } else { + // If there is no intersection, we will down-scale or drop frame + mMax = std::max(mMax, aOther.mMax); + } } bool Merge(const Range& aOther) { - if (!Intersects(aOther)) { + if (strcmp(mName, "width") != 0 && + strcmp(mName, "height") != 0 && + strcmp(mName, "frameRate") != 0 && + !Intersects(aOther)) { return false; } Intersect(aOther); @@ -297,6 +304,8 @@ class MediaConstraintsHelper protected: template static uint32_t FitnessDistance(ValueType aN, const NormalizedRange& aRange); + template + static uint32_t FeasibilityDistance(ValueType aN, const NormalizedRange& aRange); static uint32_t FitnessDistance(nsString aN, const NormalizedConstraintSet::StringRange& aConstraint); From 14192dcba4f439b8acfed5ee91674b7dc289b239 Mon Sep 17 00:00:00 2001 From: Munro Mengjue Chiang Date: Fri, 1 Dec 2017 10:12:51 +0800 Subject: [PATCH 038/219] Bug 1388219 - down scale camera output frame to the target capability. r=jib MozReview-Commit-ID: 7dlbWXndbgf --HG-- extra : rebase_source : d29809c0b898648b535afdd2c9e3e97a297d99e0 --- dom/media/systemservices/CamerasParent.cpp | 30 ++-- .../webrtc/MediaEngineCameraVideoSource.cpp | 3 +- .../webrtc/MediaEngineCameraVideoSource.h | 1 + .../webrtc/MediaEngineRemoteVideoSource.cpp | 163 +++++++++++++----- .../video_engine/desktop_capture_impl.cc | 40 +---- 5 files changed, 146 insertions(+), 91 deletions(-) diff --git a/dom/media/systemservices/CamerasParent.cpp b/dom/media/systemservices/CamerasParent.cpp index 398bd20761ec..0340ab5eb395 100644 --- a/dom/media/systemservices/CamerasParent.cpp +++ b/dom/media/systemservices/CamerasParent.cpp @@ -52,7 +52,7 @@ ResolutionFeasibilityDistance(int32_t candidate, int32_t requested) if (candidate >= requested) { distance = (candidate - requested) * 1000 / std::max(candidate, requested); } else { - distance = (UINT32_MAX / 2) + (requested - candidate) * + distance = 10000 + (requested - candidate) * 1000 / std::max(candidate, requested); } return distance; @@ -862,14 +862,14 @@ CamerasParent::RecvStartCapture(const CaptureEngine& aCapEngine, capability.codecType = static_cast(ipcCaps.codecType()); capability.interlaced = ipcCaps.interlaced(); - if (aCapEngine == CameraEngine) { #ifdef DEBUG - auto deviceUniqueID = sDeviceUniqueIDs.find(capnum); - MOZ_ASSERT(deviceUniqueID == sDeviceUniqueIDs.end()); + auto deviceUniqueID = sDeviceUniqueIDs.find(capnum); + MOZ_ASSERT(deviceUniqueID == sDeviceUniqueIDs.end()); #endif - sDeviceUniqueIDs.emplace(capnum, cap.VideoCapture()->CurrentDeviceName()); - sAllRequestedCapabilities.emplace(capnum, capability); + sDeviceUniqueIDs.emplace(capnum, cap.VideoCapture()->CurrentDeviceName()); + sAllRequestedCapabilities.emplace(capnum, capability); + if (aCapEngine == CameraEngine) { for (const auto &it : sDeviceUniqueIDs) { if (strcmp(it.second, cap.VideoCapture()->CurrentDeviceName()) == 0) { capability.width = std::max( @@ -908,6 +908,16 @@ CamerasParent::RecvStartCapture(const CaptureEngine& aCapEngine, } MOZ_ASSERT(minIdx != -1); capability = candidateCapabilities->second[minIdx]; + } else if (aCapEngine == ScreenEngine || + aCapEngine == BrowserEngine || + aCapEngine == WinEngine || + aCapEngine == AppEngine) { + for (const auto &it : sDeviceUniqueIDs) { + if (strcmp(it.second, cap.VideoCapture()->CurrentDeviceName()) == 0) { + capability.maxFPS = std::max( + capability.maxFPS, sAllRequestedCapabilities[it.first].maxFPS); + } + } } error = cap.VideoCapture()->StartCapture(capability); @@ -949,16 +959,14 @@ CamerasParent::StopCapture(const CaptureEngine& aCapEngine, mCallbacks[i - 1]->mStreamId == (uint32_t)capnum) { CallbackHelper* cbh = mCallbacks[i-1]; - engine->WithEntry(capnum,[cbh, &capnum, &aCapEngine](VideoEngine::CaptureEntry& cap){ + engine->WithEntry(capnum,[cbh, &capnum](VideoEngine::CaptureEntry& cap){ if (cap.VideoCapture()) { cap.VideoCapture()->DeRegisterCaptureDataCallback( static_cast*>(cbh)); cap.VideoCapture()->StopCaptureIfAllClientsClose(); - if (aCapEngine == CameraEngine) { - sDeviceUniqueIDs.erase(capnum); - sAllRequestedCapabilities.erase(capnum); - } + sDeviceUniqueIDs.erase(capnum); + sAllRequestedCapabilities.erase(capnum); } }); diff --git a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp index 25aedad88d04..6720628a7981 100644 --- a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp @@ -28,7 +28,8 @@ bool MediaEngineCameraVideoSource::AppendToTrack(SourceMediaStream* aSource, VideoSegment segment; RefPtr image = aImage; - IntSize size(image ? mWidth : 0, image ? mHeight : 0); + IntSize size = image ? image->GetSize() : IntSize(0, 0); + segment.AppendFrame(image.forget(), delta, size, aPrincipalHandle); // This is safe from any thread, and is safe if the track is Finished diff --git a/dom/media/webrtc/MediaEngineCameraVideoSource.h b/dom/media/webrtc/MediaEngineCameraVideoSource.h index d78bcaeed8ff..17e9c91753ae 100644 --- a/dom/media/webrtc/MediaEngineCameraVideoSource.h +++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h @@ -140,6 +140,7 @@ protected: nsTArray> mSources; // When this goes empty, we shut down HW nsTArray mPrincipalHandles; // Directly mapped to mSources. RefPtr mImage; + nsTArray> mImages; nsTArray mTargetCapabilities; nsTArray mHandleIds; RefPtr mImageContainer; diff --git a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp index e7024513cf77..1016a6ec9858 100644 --- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp @@ -10,6 +10,9 @@ #include "nsIPrefService.h" #include "MediaTrackConstraints.h" #include "CamerasChild.h" +#include "VideoFrameUtils.h" +#include "webrtc/api/video/i420_buffer.h" +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" extern mozilla::LogModule* GetMediaManagerLog(); #define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg) @@ -84,6 +87,7 @@ MediaEngineRemoteVideoSource::Shutdown() MOZ_ASSERT(mPrincipalHandles.IsEmpty()); MOZ_ASSERT(mTargetCapabilities.IsEmpty()); MOZ_ASSERT(mHandleIds.IsEmpty()); + MOZ_ASSERT(mImages.IsEmpty()); break; } source = mSources[0]; @@ -132,6 +136,7 @@ MediaEngineRemoteVideoSource::Allocate( MOZ_ASSERT(mPrincipalHandles.IsEmpty()); MOZ_ASSERT(mTargetCapabilities.IsEmpty()); MOZ_ASSERT(mHandleIds.IsEmpty()); + MOZ_ASSERT(mImages.IsEmpty()); LOG(("Video device %d reallocated", mCaptureIndex)); } else { LOG(("Video device %d allocated shared", mCaptureIndex)); @@ -174,16 +179,21 @@ MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID, return NS_ERROR_FAILURE; } + mImageContainer = + layers::LayerManager::CreateImageContainer(layers::ImageContainer::ASYNCHRONOUS); + { MonitorAutoLock lock(mMonitor); mSources.AppendElement(aStream); mPrincipalHandles.AppendElement(aPrincipalHandle); mTargetCapabilities.AppendElement(mTargetCapability); mHandleIds.AppendElement(mHandleId); + mImages.AppendElement(mImageContainer->CreatePlanarYCbCrImage()); MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); MOZ_ASSERT(mSources.Length() == mTargetCapabilities.Length()); MOZ_ASSERT(mSources.Length() == mHandleIds.Length()); + MOZ_ASSERT(mSources.Length() == mImages.Length()); } aStream->AddTrack(aID, 0, new VideoSegment(), SourceMediaStream::ADDTRACK_QUEUED); @@ -191,8 +201,6 @@ MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID, if (mState == kStarted) { return NS_OK; } - mImageContainer = - layers::LayerManager::CreateImageContainer(layers::ImageContainer::ASYNCHRONOUS); mState = kStarted; mTrackID = aID; @@ -231,10 +239,12 @@ MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource, MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); MOZ_ASSERT(mSources.Length() == mTargetCapabilities.Length()); MOZ_ASSERT(mSources.Length() == mHandleIds.Length()); + MOZ_ASSERT(mSources.Length() == mImages.Length()); mSources.RemoveElementAt(i); mPrincipalHandles.RemoveElementAt(i); mTargetCapabilities.RemoveElementAt(i); mHandleIds.RemoveElementAt(i); + mImages.RemoveElementAt(i); aSource->EndTrack(aID); @@ -318,7 +328,12 @@ MediaEngineRemoteVideoSource::UpdateSingleSource( } if (index != mHandleIds.NoIndex) { + MonitorAutoLock lock(mMonitor); mTargetCapabilities[index] = mTargetCapability; + MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); + MOZ_ASSERT(mSources.Length() == mTargetCapabilities.Length()); + MOZ_ASSERT(mSources.Length() == mHandleIds.Length()); + MOZ_ASSERT(mSources.Length() == mImages.Length()); } if (!ChooseCapability(aNetConstraints, aPrefs, aDeviceId, mCapability, @@ -385,18 +400,22 @@ MediaEngineRemoteVideoSource::NotifyPull(MediaStreamGraph* aGraph, TrackID aID, StreamTime aDesiredTime, const PrincipalHandle& aPrincipalHandle) { - VideoSegment segment; - + StreamTime delta = 0; + size_t i; MonitorAutoLock lock(mMonitor); if (mState != kStarted) { return; } - StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID); + i = mSources.IndexOf(aSource); + if (i == mSources.NoIndex) { + return; + } + + delta = aDesiredTime - aSource->GetEndOfAppendedData(aID); if (delta > 0) { - // nullptr images are allowed - AppendToTrack(aSource, mImage, aID, delta, aPrincipalHandle); + AppendToTrack(aSource, mImages[i], aID, delta, aPrincipalHandle); } } @@ -419,11 +438,12 @@ MediaEngineRemoteVideoSource::FrameSizeChange(unsigned int w, unsigned int h) } int -MediaEngineRemoteVideoSource::DeliverFrame(uint8_t* aBuffer , +MediaEngineRemoteVideoSource::DeliverFrame(uint8_t* aBuffer, const camera::VideoFrameProperties& aProps) { + MonitorAutoLock lock(mMonitor); // Check for proper state. - if (mState != kStarted) { + if (mState != kStarted || !mImageContainer) { LOG(("DeliverFrame: video not started")); return 0; } @@ -431,51 +451,114 @@ MediaEngineRemoteVideoSource::DeliverFrame(uint8_t* aBuffer , // Update the dimensions FrameSizeChange(aProps.width(), aProps.height()); - layers::PlanarYCbCrData data; - RefPtr image; - { - // We grab the lock twice, but don't hold it across the (long) CopyData - MonitorAutoLock lock(mMonitor); - if (!mImageContainer) { - LOG(("DeliverFrame() called after Stop()!")); - return 0; - } - // Create a video frame and append it to the track. - image = mImageContainer->CreatePlanarYCbCrImage(); + MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length()); + MOZ_ASSERT(mSources.Length() == mTargetCapabilities.Length()); + MOZ_ASSERT(mSources.Length() == mHandleIds.Length()); + MOZ_ASSERT(mSources.Length() == mImages.Length()); + + for (uint32_t i = 0; i < mTargetCapabilities.Length(); i++ ) { + int32_t req_max_width = mTargetCapabilities[i].width & 0xffff; + int32_t req_max_height = mTargetCapabilities[i].height & 0xffff; + int32_t req_ideal_width = (mTargetCapabilities[i].width >> 16) & 0xffff; + int32_t req_ideal_height = (mTargetCapabilities[i].height >> 16) & 0xffff; + + int32_t dest_max_width = std::min(req_max_width, mWidth); + int32_t dest_max_height = std::min(req_max_height, mHeight); + // This logic works for both camera and screen sharing case. + // for camera case, req_ideal_width and req_ideal_height is 0. + // The following snippet will set dst_width to dest_max_width and dst_height to dest_max_height + int32_t dst_width = std::min(req_ideal_width > 0 ? req_ideal_width : mWidth, dest_max_width); + int32_t dst_height = std::min(req_ideal_height > 0 ? req_ideal_height : mHeight, dest_max_height); + + int dst_stride_y = dst_width; + int dst_stride_uv = (dst_width + 1) / 2; + + camera::VideoFrameProperties properties; + uint8_t* frame; + bool needReScale = !((dst_width == mWidth && dst_height == mHeight) || + (dst_width > mWidth || dst_height > mHeight)); + + if (!needReScale) { + dst_width = mWidth; + dst_height = mHeight; + frame = aBuffer; + } else { + rtc::scoped_refptr i420Buffer; + i420Buffer = webrtc::I420Buffer::Create(mWidth, mHeight, mWidth, + (mWidth + 1) / 2, (mWidth + 1) / 2); + + const int conversionResult = webrtc::ConvertToI420(webrtc::kI420, + aBuffer, + 0, 0, // No cropping + mWidth, mHeight, + mWidth * mHeight * 3 / 2, + webrtc::kVideoRotation_0, + i420Buffer.get()); + + webrtc::VideoFrame captureFrame(i420Buffer, 0, 0, webrtc::kVideoRotation_0); + if (conversionResult < 0) { + return 0; + } + + rtc::scoped_refptr scaledBuffer; + scaledBuffer = webrtc::I420Buffer::Create(dst_width, dst_height, dst_stride_y, + dst_stride_uv, dst_stride_uv); + + scaledBuffer->CropAndScaleFrom(*captureFrame.video_frame_buffer().get()); + webrtc::VideoFrame scaledFrame(scaledBuffer, 0, 0, webrtc::kVideoRotation_0); + + VideoFrameUtils::InitFrameBufferProperties(scaledFrame, properties); + frame = new unsigned char[properties.bufferSize()]; + + if (!frame) { + return 0; + } + + VideoFrameUtils::CopyVideoFrameBuffers(frame, + properties.bufferSize(), scaledFrame); + } + + // Create a video frame and append it to the track. + RefPtr image = mImageContainer->CreatePlanarYCbCrImage(); - uint8_t* frame = static_cast (aBuffer); const uint8_t lumaBpp = 8; const uint8_t chromaBpp = 4; + layers::PlanarYCbCrData data; + // Take lots of care to round up! data.mYChannel = frame; - data.mYSize = IntSize(mWidth, mHeight); - data.mYStride = (mWidth * lumaBpp + 7)/ 8; - data.mCbCrStride = (mWidth * chromaBpp + 7) / 8; - data.mCbChannel = frame + mHeight * data.mYStride; - data.mCrChannel = data.mCbChannel + ((mHeight+1)/2) * data.mCbCrStride; - data.mCbCrSize = IntSize((mWidth+1)/ 2, (mHeight+1)/ 2); + data.mYSize = IntSize(dst_width, dst_height); + data.mYStride = (dst_width * lumaBpp + 7) / 8; + data.mCbCrStride = (dst_width * chromaBpp + 7) / 8; + data.mCbChannel = frame + dst_height * data.mYStride; + data.mCrChannel = data.mCbChannel + ((dst_height + 1) / 2) * data.mCbCrStride; + data.mCbCrSize = IntSize((dst_width + 1) / 2, (dst_height + 1) / 2); data.mPicX = 0; data.mPicY = 0; - data.mPicSize = IntSize(mWidth, mHeight); + data.mPicSize = IntSize(dst_width, dst_height); data.mStereoMode = StereoMode::MONO; - } - if (!image->CopyData(data)) { - MOZ_ASSERT(false); - return 0; - } + if (!image->CopyData(data)) { + MOZ_ASSERT(false); + return 0; + } + + if (needReScale && frame) { + delete frame; + frame = nullptr; + } - MonitorAutoLock lock(mMonitor); #ifdef DEBUG - static uint32_t frame_num = 0; - LOGFRAME(("frame %d (%dx%d); timeStamp %u, ntpTimeMs %" PRIu64 ", renderTimeMs %" PRIu64, - frame_num++, mWidth, mHeight, - aProps.timeStamp(), aProps.ntpTimeMs(), aProps.renderTimeMs())); + static uint32_t frame_num = 0; + LOGFRAME(("frame %d (%dx%d); timeStamp %u, ntpTimeMs %" PRIu64 ", renderTimeMs %" PRIu64, + frame_num++, mWidth, mHeight, + aProps.timeStamp(), aProps.ntpTimeMs(), aProps.renderTimeMs())); #endif - // implicitly releases last image - mImage = image.forget(); + // implicitly releases last image + mImages[i] = image.forget(); + } // We'll push the frame into the MSG on the next NotifyPull. This will avoid // swamping the MSG with frames should it be taking longer than normal to run diff --git a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc index b464893d56ee..e53175f63a21 100644 --- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc +++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc @@ -582,45 +582,7 @@ int32_t DesktopCaptureImpl::IncomingFrame(uint8_t* videoFrame, return -1; } - int32_t req_max_width = _requestedCapability.width & 0xffff; - int32_t req_max_height = _requestedCapability.height & 0xffff; - int32_t req_ideal_width = (_requestedCapability.width >> 16) & 0xffff; - int32_t req_ideal_height = (_requestedCapability.height >> 16) & 0xffff; - - int32_t dest_max_width = std::min(req_max_width, target_width); - int32_t dest_max_height = std::min(req_max_height, target_height); - int32_t dst_width = std::min(req_ideal_width > 0 ? req_ideal_width : target_width, dest_max_width); - int32_t dst_height = std::min(req_ideal_height > 0 ? req_ideal_height : target_height, dest_max_height); - - // scale to average of portrait and landscape - float scale_width = (float)dst_width / (float)target_width; - float scale_height = (float)dst_height / (float)target_height; - float scale = (scale_width + scale_height) / 2; - dst_width = (int)(scale * target_width); - dst_height = (int)(scale * target_height); - - // if scaled rectangle exceeds max rectangle, scale to minimum of portrait and landscape - if (dst_width > dest_max_width || dst_height > dest_max_height) { - scale_width = (float)dest_max_width / (float)dst_width; - scale_height = (float)dest_max_height / (float)dst_height; - scale = std::min(scale_width, scale_height); - dst_width = (int)(scale * dst_width); - dst_height = (int)(scale * dst_height); - } - - int dst_stride_y = dst_width; - int dst_stride_uv = (dst_width + 1) / 2; - if (dst_width == target_width && dst_height == target_height) { - DeliverCapturedFrame(captureFrame, captureTime); - } else { - rtc::scoped_refptr buffer; - buffer = I420Buffer::Create(dst_width, dst_height, dst_stride_y, - dst_stride_uv, dst_stride_uv); - - buffer->ScaleFrom(*captureFrame.video_frame_buffer().get()); - webrtc::VideoFrame scaledFrame(buffer, 0, 0, kVideoRotation_0); - DeliverCapturedFrame(scaledFrame, captureTime); - } + DeliverCapturedFrame(captureFrame, captureTime); } else { assert(false); return -1; From 6e506d37ba5960644e7e0fcaced8ba3dbcfd8d65 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 28 Nov 2017 09:43:27 -0800 Subject: [PATCH 039/219] Bug 1421355, part 1 - Remove nsIXPConnect::GetPrincipal(). r=mrbkap This method is unused. It is the only caller of XPCWrappedNative::GetObjectPrincipal() so remove that, too. MozReview-Commit-ID: 8s5toK85YUS --HG-- extra : rebase_source : 551ec90d893ac9f47ef5166ec1dbd2ac8b5c6988 --- js/xpconnect/idl/nsIXPConnect.idl | 11 ----------- js/xpconnect/src/XPCWrappedNative.cpp | 21 -------------------- js/xpconnect/src/nsXPConnect.cpp | 28 --------------------------- js/xpconnect/src/xpcprivate.h | 5 ----- 4 files changed, 65 deletions(-) diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index bf6f75f6f83f..5bc6484f0d16 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -467,17 +467,6 @@ interface nsIXPConnect : nsISupports */ void NotifyDidPaint(); -%{C++ - /** - * Get the object principal for this wrapper. Note that this may well end - * up being null; in that case one should seek principals elsewhere. Null - * here does NOT indicate system principal or no principals at all, just - * that this wrapper doesn't have an intrinsic one. - */ - virtual nsIPrincipal* GetPrincipal(JSObject* obj, - bool allowShortCircuit) const = 0; -%} - [noscript] void writeScript(in nsIObjectOutputStream aStream, in JSContextPtr aJSContext, in JSScriptPtr aJSScript); diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index 85024a84af0d..b1470db762ce 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -2021,27 +2021,6 @@ NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectProtot return NS_OK; } -nsIPrincipal* -XPCWrappedNative::GetObjectPrincipal() const -{ - nsIPrincipal* principal = GetScope()->GetPrincipal(); -#ifdef DEBUG - // Because of inner window reuse, we can have objects with one principal - // living in a scope with a different (but same-origin) principal. So - // just check same-origin here. - nsCOMPtr objPrin(do_QueryInterface(mIdentity)); - if (objPrin) { - bool equal; - if (!principal) - equal = !objPrin->GetPrincipal(); - else - principal->Equals(objPrin->GetPrincipal(), &equal); - MOZ_ASSERT(equal, "Principal mismatch. Expect bad things to happen"); - } -#endif - return principal; -} - NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithMember(HandleId name, nsIInterfaceInfo * *_retval) { diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index c0605549c909..e3c1b5681033 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -979,34 +979,6 @@ nsXPConnect::JSToVariant(JSContext* ctx, HandleValue value, nsIVariant** _retval return NS_OK; } -nsIPrincipal* -nsXPConnect::GetPrincipal(JSObject* obj, bool allowShortCircuit) const -{ - MOZ_ASSERT(IS_WN_REFLECTOR(obj), "What kind of wrapper is this?"); - - XPCWrappedNative* xpcWrapper = XPCWrappedNative::Get(obj); - if (xpcWrapper) { - if (allowShortCircuit) { - nsIPrincipal* result = xpcWrapper->GetObjectPrincipal(); - if (result) { - return result; - } - } - - // If not, check if it points to an nsIScriptObjectPrincipal - nsCOMPtr objPrin = - do_QueryInterface(xpcWrapper->Native()); - if (objPrin) { - nsIPrincipal* result = objPrin->GetPrincipal(); - if (result) { - return result; - } - } - } - - return nullptr; -} - namespace xpc { bool diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index a245cb0f8814..5f09f23e1a6e 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -269,9 +269,6 @@ public: nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info); nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info); - virtual nsIPrincipal* GetPrincipal(JSObject* obj, - bool allowShortCircuit) const override; - void RecordTraversal(void* p, nsISupports* s); protected: @@ -1545,8 +1542,6 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative) - nsIPrincipal* GetObjectPrincipal() const; - bool IsValid() const { return mFlatJSObject.hasFlag(FLAT_JS_OBJECT_VALID); } From 03b06b90f7ded2b5e0116655c2b4977cec8b3aed Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 28 Nov 2017 09:47:44 -0800 Subject: [PATCH 040/219] Bug 1421355, part 2 - Remove nsIXPConnect::getWrappedNativeOfNativeObject. r=mrbkap This method is unused. It is the only caller of XPCWrappedNative::GetUsedOnly(), so remove that, too. MozReview-Commit-ID: LRMB2bAwgoS --HG-- extra : rebase_source : 8203aae8d0263ca467fff8e63f187caca8aaf733 --- js/xpconnect/idl/nsIXPConnect.idl | 10 ------ js/xpconnect/src/XPCWrappedNative.cpp | 48 --------------------------- js/xpconnect/src/nsXPConnect.cpp | 34 ------------------- js/xpconnect/src/xpcprivate.h | 6 ---- 4 files changed, 98 deletions(-) diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index 5bc6484f0d16..53ac36f699b4 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -404,16 +404,6 @@ interface nsIXPConnect : nsISupports // Methods added since mozilla 0.6.... - /** - * This only succeeds if the native object is already wrapped by xpconnect. - * A new wrapper is *never* constructed. - */ - nsIXPConnectWrappedNative - getWrappedNativeOfNativeObject(in JSContextPtr aJSContext, - in JSObjectPtr aScope, - in nsISupports aCOMObj, - in nsIIDRef aIID); - void setFunctionThisTranslator(in nsIIDRef aIID, in nsIXPCFunctionThisTranslator aTranslator); diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index b1470db762ce..b04e32e4bc7f 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -479,54 +479,6 @@ FinishCreate(XPCWrappedNativeScope* Scope, return NS_OK; } -// static -nsresult -XPCWrappedNative::GetUsedOnly(nsISupports* Object, - XPCWrappedNativeScope* Scope, - XPCNativeInterface* Interface, - XPCWrappedNative** resultWrapper) -{ - AutoJSContext cx; - MOZ_ASSERT(Object, "XPCWrappedNative::GetUsedOnly was called with a null Object"); - MOZ_ASSERT(Interface); - - RefPtr wrapper; - nsWrapperCache* cache = nullptr; - CallQueryInterface(Object, &cache); - if (cache) { - RootedObject flat(cx, cache->GetWrapper()); - if (!flat) { - *resultWrapper = nullptr; - return NS_OK; - } - wrapper = XPCWrappedNative::Get(flat); - } else { - nsCOMPtr identity = do_QueryInterface(Object); - - if (!identity) { - NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!"); - return NS_ERROR_FAILURE; - } - - Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap(); - - wrapper = map->Find(identity); - if (!wrapper) { - *resultWrapper = nullptr; - return NS_OK; - } - } - - nsresult rv; - if (!wrapper->FindTearOff(Interface, false, &rv)) { - MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure"); - return rv; - } - - wrapper.forget(resultWrapper); - return NS_OK; -} - // This ctor is used if this object will have a proto. XPCWrappedNative::XPCWrappedNative(already_AddRefed&& aIdentity, XPCWrappedNativeProto* aProto) diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index e3c1b5681033..362306d82b0b 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -768,40 +768,6 @@ xpc::UnwrapReflectorToISupports(JSObject* reflector) return canonical.forget(); } -NS_IMETHODIMP -nsXPConnect::GetWrappedNativeOfNativeObject(JSContext * aJSContext, - JSObject * aScopeArg, - nsISupports* aCOMObj, - const nsIID & aIID, - nsIXPConnectWrappedNative** _retval) -{ - MOZ_ASSERT(aJSContext, "bad param"); - MOZ_ASSERT(aScopeArg, "bad param"); - MOZ_ASSERT(aCOMObj, "bad param"); - MOZ_ASSERT(_retval, "bad param"); - - *_retval = nullptr; - - RootedObject aScope(aJSContext, aScopeArg); - - XPCWrappedNativeScope* scope = ObjectScope(aScope); - if (!scope) - return UnexpectedFailure(NS_ERROR_FAILURE); - - RefPtr iface = - XPCNativeInterface::GetNewOrUsed(&aIID); - if (!iface) - return NS_ERROR_FAILURE; - - XPCWrappedNative* wrapper; - - nsresult rv = XPCWrappedNative::GetUsedOnly(aCOMObj, scope, iface, &wrapper); - if (NS_FAILED(rv)) - return NS_ERROR_FAILURE; - *_retval = static_cast(wrapper); - return NS_OK; -} - NS_IMETHODIMP nsXPConnect::SetFunctionThisTranslator(const nsIID & aIID, nsIXPCFunctionThisTranslator* aTranslator) diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 5f09f23e1a6e..3c71954728b8 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -1651,12 +1651,6 @@ public: XPCNativeInterface* Interface, XPCWrappedNative** wrapper); - static nsresult - GetUsedOnly(nsISupports* Object, - XPCWrappedNativeScope* Scope, - XPCNativeInterface* Interface, - XPCWrappedNative** wrapper); - void FlatJSObjectFinalized(); void FlatJSObjectMoved(JSObject* obj, const JSObject* old); From c4a35a74f9054c9a6517e61abd2ae85aaadf8ff1 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 28 Nov 2017 09:51:53 -0800 Subject: [PATCH 041/219] Bug 1421355, part 3 - Remove nsIXPConnect::GarbageCollect(). r=mrbkap MozReview-Commit-ID: JGG15HPxpda --HG-- extra : rebase_source : 60369ff0c61d1dec06aa98e37bf2d00dc11dd5e0 --- js/xpconnect/idl/nsIXPConnect.idl | 6 ------ js/xpconnect/src/nsXPConnect.cpp | 7 ------- 2 files changed, 13 deletions(-) diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index 53ac36f699b4..0eb1d7af85b4 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -445,12 +445,6 @@ interface nsIXPConnect : nsISupports in JSContextPtr cx, in JSObjectPtr sandbox); - /** - * Trigger a JS garbage collection. - * Use a js::gcreason::Reason from jsfriendapi.h for the kind. - */ - void GarbageCollect(in uint32_t reason); - /** * Signals a good place to do an incremental GC slice, because the * browser is drawing a frame. diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 362306d82b0b..9466df90ce3e 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -399,13 +399,6 @@ nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info) return NS_FAILED(rv) ? NS_OK : NS_ERROR_NO_INTERFACE; } -NS_IMETHODIMP -nsXPConnect::GarbageCollect(uint32_t reason) -{ - mRuntime->GarbageCollect(reason); - return NS_OK; -} - void xpc_MarkInCCGeneration(nsISupports* aVariant, uint32_t aGeneration) { From 34d939862e1260d13a9947c72bf39a7cfd5832cd Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 28 Nov 2017 09:53:04 -0800 Subject: [PATCH 042/219] Bug 1421355, part 4 - Remove nsXPConnect::GetInfoForName. r=mrbkap MozReview-Commit-ID: 6Y0ujiF0RBw --HG-- extra : rebase_source : 2eca5f54fef5fde7281e9c7bbff860c8451329cc --- js/xpconnect/src/nsXPConnect.cpp | 7 ------- js/xpconnect/src/xpcprivate.h | 1 - 2 files changed, 8 deletions(-) diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 9466df90ce3e..56f4f19c15b7 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -392,13 +392,6 @@ nsXPConnect::GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info) return XPTInterfaceInfoManager::GetSingleton()->GetInfoForIID(aIID, info); } -nsresult -nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info) -{ - nsresult rv = XPTInterfaceInfoManager::GetSingleton()->GetInfoForName(name, info); - return NS_FAILED(rv) ? NS_OK : NS_ERROR_NO_INTERFACE; -} - void xpc_MarkInCCGeneration(nsISupports* aVariant, uint32_t aGeneration) { diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 3c71954728b8..f652fbc1512e 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -267,7 +267,6 @@ public: bool IsShuttingDown() const {return mShuttingDown;} nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info); - nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info); void RecordTraversal(void* p, nsISupports* s); From 71dbca0a82493282b81163e1ee3a02d2455e59c5 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 28 Nov 2017 10:00:38 -0800 Subject: [PATCH 043/219] Bug 1421355, part 5 - Remove nsXPConnect::NotifyDidPaint(). r=mrbkap Presshell still does something along these lines, but it works completely differently. MozReview-Commit-ID: JRenEDNlo6p --HG-- extra : rebase_source : d90924fcbbf81b1b23311b8589ea86403f0fd630 --- js/xpconnect/idl/nsIXPConnect.idl | 6 ------ js/xpconnect/src/nsXPConnect.cpp | 7 ------- 2 files changed, 13 deletions(-) diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index 0eb1d7af85b4..a70c69b96988 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -445,12 +445,6 @@ interface nsIXPConnect : nsISupports in JSContextPtr cx, in JSObjectPtr sandbox); - /** - * Signals a good place to do an incremental GC slice, because the - * browser is drawing a frame. - */ - void NotifyDidPaint(); - [noscript] void writeScript(in nsIObjectOutputStream aStream, in JSContextPtr aJSContext, in JSScriptPtr aJSScript); diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 56f4f19c15b7..93a7dcefc1fb 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -997,13 +997,6 @@ SetLocationForGlobal(JSObject* global, nsIURI* locationURI) } // namespace xpc -NS_IMETHODIMP -nsXPConnect::NotifyDidPaint() -{ - JS::NotifyDidPaint(XPCJSContext::Get()->Context()); - return NS_OK; -} - static nsresult WriteScriptOrFunction(nsIObjectOutputStream* stream, JSContext* cx, JSScript* scriptArg, HandleObject functionObj) From 6d088f4ffe73d6a2966af1da4cb0a4254591f0f2 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 28 Nov 2017 10:04:23 -0800 Subject: [PATCH 044/219] Bug 1421355, part 6 - Remove JS::NotifyDidPaint. r=jonco MozReview-Commit-ID: 6rdvr6QfAD1 --HG-- extra : rebase_source : e3e1fb7de9cc30d10cd17d00f4290e27a46b4c0e --- js/public/GCAPI.h | 6 ------ js/src/jsfriendapi.cpp | 6 ------ 2 files changed, 12 deletions(-) diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 43686aa86eae..ee18d49f88ad 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -881,12 +881,6 @@ class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoRequireNoGC extern JS_FRIEND_API(void) NotifyGCRootsRemoved(JSContext* cx); -/* - * Internal to Firefox. - */ -extern JS_FRIEND_API(void) -NotifyDidPaint(JSContext* cx); - } /* namespace JS */ /** diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index c977162218a7..c1a1460b56e8 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1248,12 +1248,6 @@ js::SetActivityCallback(JSContext* cx, ActivityCallback cb, void* arg) cx->activityCallbackArg = arg; } -JS_FRIEND_API(void) -JS::NotifyDidPaint(JSContext* cx) -{ - cx->runtime()->gc.notifyDidPaint(); -} - JS_FRIEND_API(void) JS::NotifyGCRootsRemoved(JSContext* cx) { From e901a26540c7b876feb7b54aa454b923d4c3e64d Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 28 Nov 2017 10:31:30 -0800 Subject: [PATCH 045/219] Bug 1421355, part 7 - Remove unused defines and forward decls from nsIXPConnect.idl. r=mrbkap MozReview-Commit-ID: EI05aS33n2R --HG-- extra : rebase_source : 3a52b327c9d34e0c3d4c987ce4877d1981b7be34 --- js/xpconnect/idl/nsIXPConnect.idl | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index a70c69b96988..e75694a7a8cd 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -14,8 +14,6 @@ #include "mozilla/Attributes.h" #include "nsCOMPtr.h" -struct JSFreeOp; - class nsWrapperCache; %} @@ -24,30 +22,18 @@ class nsWrapperCache; // NB: jsval and jsid are declared in nsrootidl.idl [ptr] native JSContextPtr(JSContext); -[ptr] native JSClassPtr(JSClass); -[ptr] native JSFreeOpPtr(JSFreeOp); [ptr] native JSObjectPtr(JSObject); -[ptr] native JSValConstPtr(const JS::Value); - native JSEqualityOp(JSEqualityOp); [ptr] native JSScriptPtr(JSScript); -[ptr] native voidPtrPtr(void*); [ptr] native nsWrapperCachePtr(nsWrapperCache); -[ref] native JSCompartmentOptions(JS::CompartmentOptions); -[ref] native JSCallArgsRef(const JS::CallArgs); native JSHandleId(JS::Handle); /***************************************************************************/ // forward declarations... -interface nsIXPCScriptable; -interface nsIXPConnect; -interface nsIXPConnectWrappedNative; interface nsIInterfaceInfo; -interface nsIXPCSecurityManager; interface nsIPrincipal; interface nsIClassInfo; interface nsIVariant; -interface nsIStackFrame; interface nsIObjectInputStream; interface nsIObjectOutputStream; From 7e0973c356b1139ef63fa2011129de0e00f955fb Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Tue, 28 Nov 2017 10:48:12 -0800 Subject: [PATCH 046/219] Bug 1421355, part 8 - Remove unused attributes and methods from nsIXPConnectWrappedNative. r=mrbkap Also remove some out of date comments. GetObjectPrincipal() was removed a while ago. MozReview-Commit-ID: IQFoVyaEMlY --HG-- extra : rebase_source : 935ecc1094d46ac8cab11e236b6ffb1a95aa9a06 --- js/xpconnect/idl/nsIXPConnect.idl | 16 --------- js/xpconnect/src/XPCInlines.h | 14 -------- js/xpconnect/src/XPCWrappedNative.cpp | 50 --------------------------- js/xpconnect/src/xpcprivate.h | 2 -- 4 files changed, 82 deletions(-) diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index e75694a7a8cd..b6e9c913c994 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -48,25 +48,9 @@ interface nsIXPConnectJSObjectHolder : nsISupports interface nsIXPConnectWrappedNative : nsIXPConnectJSObjectHolder { /* attribute 'JSObject' inherited from nsIXPConnectJSObjectHolder */ - readonly attribute nsISupports Native; - readonly attribute JSObjectPtr JSObjectPrototype; - - /** - * These are here as an aid to nsIXPCScriptable implementors - */ - - nsIInterfaceInfo FindInterfaceWithMember(in JSHandleId nameID); - nsIInterfaceInfo FindInterfaceWithName(in JSHandleId nameID); - [notxpcom] bool HasNativeMember(in JSHandleId name); void debugDump(in short depth); - /* - * NOTE: Add new IDL methods _before_ the C++ block below if you - * add them. Otherwise the vtable won't be what xpidl thinks it - * is, since GetObjectPrincipal() is virtual. - */ - %{C++ /** * Faster access to the native object from C++. Will never return null. diff --git a/js/xpconnect/src/XPCInlines.h b/js/xpconnect/src/XPCInlines.h index d054ff864ad8..128df083f1a6 100644 --- a/js/xpconnect/src/XPCInlines.h +++ b/js/xpconnect/src/XPCInlines.h @@ -359,20 +359,6 @@ XPCNativeSet::FindMember(JS::HandleId name, return true; } -inline XPCNativeInterface* -XPCNativeSet::FindNamedInterface(jsid name) const -{ - XPCNativeInterface* const * pp = mInterfaces; - - for (int i = (int) mInterfaceCount; i > 0; i--, pp++) { - XPCNativeInterface* iface = *pp; - - if (name == iface->GetName()) - return iface; - } - return nullptr; -} - inline XPCNativeInterface* XPCNativeSet::FindInterfaceWithIID(const nsIID& iid) const { diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index b04e32e4bc7f..f45d742100da 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -1957,56 +1957,6 @@ XPCWrappedNative::GetJSObject() return GetFlatJSObject(); } -NS_IMETHODIMP XPCWrappedNative::GetNative(nsISupports * *aNative) -{ - // No need to QI here, we already have the correct nsISupports - // vtable. - nsCOMPtr rval = mIdentity; - rval.forget(aNative); - return NS_OK; -} - -NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectPrototype) -{ - *aJSObjectPrototype = HasProto() ? - GetProto()->GetJSProtoObject() : GetFlatJSObject(); - return NS_OK; -} - -NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithMember(HandleId name, - nsIInterfaceInfo * *_retval) -{ - RefPtr iface; - XPCNativeMember* member; - - if (GetSet()->FindMember(name, &member, &iface) && iface) { - nsCOMPtr temp = iface->GetInterfaceInfo(); - temp.forget(_retval); - } else - *_retval = nullptr; - return NS_OK; -} - -NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithName(HandleId name, - nsIInterfaceInfo * *_retval) -{ - XPCNativeInterface* iface = GetSet()->FindNamedInterface(name); - if (iface) { - nsCOMPtr temp = iface->GetInterfaceInfo(); - temp.forget(_retval); - } else - *_retval = nullptr; - return NS_OK; -} - -NS_IMETHODIMP_(bool) -XPCWrappedNative::HasNativeMember(HandleId name) -{ - XPCNativeMember* member = nullptr; - uint16_t ignored; - return GetSet()->FindMember(name, &member, &ignored) && !!member; -} - NS_IMETHODIMP XPCWrappedNative::DebugDump(int16_t depth) { #ifdef DEBUG diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index f652fbc1512e..bddb262047fe 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -1331,8 +1331,6 @@ class XPCNativeSet final inline XPCNativeInterface* FindInterfaceWithIID(const nsIID& iid) const; - inline XPCNativeInterface* FindNamedInterface(jsid name) const; - uint16_t GetMemberCount() const { return mMemberCount; } From dce6b5fececb568e6c8f59aa18113a1780a6f542 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 30 Nov 2017 13:23:56 -0500 Subject: [PATCH 047/219] Bug 1422321. Eliminate double promotion in Round(). r=kats This uses std::floor(T) instead of floor(double) so that we end up calling floorf(float) for floats instead floor(double). It also changes the literals to be floats. 0.5f is exactly representable in float and so will transparently promote to double as needed. --HG-- extra : rebase_source : ea193026b3c7d1f97f5abbc3f9220eca5ac5523c --- gfx/2d/BasePoint.h | 4 ++-- gfx/2d/BaseRect.h | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/gfx/2d/BasePoint.h b/gfx/2d/BasePoint.h index 755f25e303e2..3901e5c639e1 100644 --- a/gfx/2d/BasePoint.h +++ b/gfx/2d/BasePoint.h @@ -91,8 +91,8 @@ struct BasePoint { // They are always rounding as floor(n + 0.5). // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14 Sub& Round() { - x = Coord(floor(T(x) + T(0.5))); - y = Coord(floor(T(y) + T(0.5))); + x = Coord(std::floor(T(x) + T(0.5f))); + y = Coord(std::floor(T(y) + T(0.5f))); return *static_cast(this); } diff --git a/gfx/2d/BaseRect.h b/gfx/2d/BaseRect.h index e8993f29d1c7..4b86987f3734 100644 --- a/gfx/2d/BaseRect.h +++ b/gfx/2d/BaseRect.h @@ -418,10 +418,10 @@ struct BaseRect { // new |RoundAwayFromZero()| method. void Round() { - T x0 = static_cast(floor(T(X()) + 0.5)); - T y0 = static_cast(floor(T(Y()) + 0.5)); - T x1 = static_cast(floor(T(XMost()) + 0.5)); - T y1 = static_cast(floor(T(YMost()) + 0.5)); + T x0 = static_cast(std::floor(T(X()) + 0.5f)); + T y0 = static_cast(std::floor(T(Y()) + 0.5f)); + T x1 = static_cast(std::floor(T(XMost()) + 0.5f)); + T y1 = static_cast(std::floor(T(YMost()) + 0.5f)); x = x0; y = y0; @@ -434,10 +434,10 @@ struct BaseRect { // original rectangle contains the resulting rectangle. void RoundIn() { - T x0 = static_cast(ceil(T(X()))); - T y0 = static_cast(ceil(T(Y()))); - T x1 = static_cast(floor(T(XMost()))); - T y1 = static_cast(floor(T(YMost()))); + T x0 = static_cast(std::ceil(T(X()))); + T y0 = static_cast(std::ceil(T(Y()))); + T x1 = static_cast(std::floor(T(XMost()))); + T y1 = static_cast(std::floor(T(YMost()))); x = x0; y = y0; @@ -450,10 +450,10 @@ struct BaseRect { // resulting rectangle contains the original rectangle. void RoundOut() { - T x0 = static_cast(floor(T(X()))); - T y0 = static_cast(floor(T(Y()))); - T x1 = static_cast(ceil(T(XMost()))); - T y1 = static_cast(ceil(T(YMost()))); + T x0 = static_cast(std::floor(T(X()))); + T y0 = static_cast(std::floor(T(Y()))); + T x1 = static_cast(std::ceil(T(XMost()))); + T y1 = static_cast(std::ceil(T(YMost()))); x = x0; y = y0; From 6806c911a3b9e5d878af4f99cddebadc0ba12808 Mon Sep 17 00:00:00 2001 From: Chung-Sheng Fu Date: Tue, 12 Sep 2017 17:32:07 +0800 Subject: [PATCH 048/219] Bug 1039069 - Provide a popup about English for international users. r=arthuredelstein,mconley MozReview-Commit-ID: IL8i4vzjWQd --HG-- extra : rebase_source : 7524445d3508b6de6a347034efb4a28e8d5738c5 --- browser/app/profile/firefox.js | 6 + browser/base/content/browser.js | 3 + browser/components/nsBrowserGlue.js | 5 + .../en-US/chrome/browser/browser.properties | 3 + .../resistfingerprinting/LanguagePrompt.jsm | 205 ++++++++++++++++++ .../components/resistfingerprinting/moz.build | 4 + 6 files changed, 226 insertions(+) create mode 100644 toolkit/components/resistfingerprinting/LanguagePrompt.jsm diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 2be842557bd3..10704fd0b0a4 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -582,6 +582,12 @@ pref("privacy.panicButton.enabled", true); // Time until temporary permissions expire, in ms pref("privacy.temporary_permission_expire_time_ms", 3600000); +// If Accept-Language should be spoofed by en-US +// 0 - will prompt +// 1 - don't spoof +// 2 - spoof +pref("privacy.spoof_english", 0); + pref("network.proxy.share_proxy_settings", false); // use the same proxy settings for all protocols // simple gestures support diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index d718a29395d3..27d6ba76ee07 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -34,6 +34,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { E10SUtils: "resource:///modules/E10SUtils.jsm", ExtensionsUI: "resource:///modules/ExtensionsUI.jsm", FormValidationHandler: "resource:///modules/FormValidationHandler.jsm", + LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm", LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm", Log: "resource://gre/modules/Log.jsm", LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm", @@ -1821,6 +1822,8 @@ var gBrowserInit = { gAccessibilityServiceIndicator.uninit(); + LanguagePrompt.uninit(); + // Now either cancel delayedStartup, or clean up the services initialized from // it. if (this._boundDelayedStartup) { diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index ee8a6e03f4c1..e707dd0ab246 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -41,6 +41,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { FormValidationHandler: "resource:///modules/FormValidationHandler.jsm", Integration: "resource://gre/modules/Integration.jsm", L10nRegistry: "resource://gre/modules/L10nRegistry.jsm", + LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm", LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm", LoginHelper: "resource://gre/modules/LoginHelper.jsm", LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm", @@ -1166,6 +1167,10 @@ BrowserGlue.prototype = { JawsScreenReaderVersionCheck.onWindowsRestored(); }); } + + Services.tm.idleDispatchToMainThread(() => { + LanguagePrompt.init(); + }); }, /** diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index f251cf605195..f58ef5b2bb64 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -497,6 +497,9 @@ canvas.allow=Allow Data Access canvas.allow.accesskey=A canvas.remember=Always remember my decision +# Spoof Accept-Language prompt +privacy.spoof_english=Changing your language setting to English will make you more difficult to identify and enhance your privacy. Do you want to request English language versions of web pages? + identity.identified.verifier=Verified by: %S identity.identified.verified_by_you=You have added a security exception for this site. identity.identified.state_and_country=%S, %S diff --git a/toolkit/components/resistfingerprinting/LanguagePrompt.jsm b/toolkit/components/resistfingerprinting/LanguagePrompt.jsm new file mode 100644 index 000000000000..cc4d37e4a639 --- /dev/null +++ b/toolkit/components/resistfingerprinting/LanguagePrompt.jsm @@ -0,0 +1,205 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +this.EXPORTED_SYMBOLS = ["LanguagePrompt"]; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +const kPrefResistFingerprinting = "privacy.resistFingerprinting"; +const kPrefSpoofEnglish = "privacy.spoof_english"; +const kTopicHttpOnModifyRequest = "http-on-modify-request"; + +class _LanguagePrompt { + constructor() { + this._initialized = false; + } + + init() { + if (this._initialized) { + return; + } + this._initialized = true; + + Services.prefs.addObserver(kPrefResistFingerprinting, this); + this._handleResistFingerprintingChanged(); + } + + uninit() { + if (!this._initialized) { + return; + } + this._initialized = false; + + Services.prefs.removeObserver(kPrefResistFingerprinting, this); + this._removeObservers(); + } + + observe(subject, topic, data) { + switch (topic) { + case "nsPref:changed": + this._handlePrefChanged(data); + break; + case kTopicHttpOnModifyRequest: + this._handleHttpOnModifyRequest(subject, data); + break; + default: + break; + } + } + + _removeObservers() { + try { + Services.pref.removeObserver(kPrefSpoofEnglish, this); + } catch (e) { + // do nothing + } + try { + Services.obs.removeObserver(this, kTopicHttpOnModifyRequest); + } catch (e) { + // do nothing + } + } + + _shouldPromptForLanguagePref() { + return (Services.locale.getAppLocaleAsLangTag().substr(0, 2) !== "en") + && (Services.prefs.getIntPref(kPrefSpoofEnglish) === 0); + } + + _handlePrefChanged(data) { + switch (data) { + case kPrefResistFingerprinting: + this._handleResistFingerprintingChanged(); + break; + case kPrefSpoofEnglish: + this._handleSpoofEnglishChanged(); + break; + default: + break; + } + } + + _handleResistFingerprintingChanged() { + if (Services.prefs.getBoolPref(kPrefResistFingerprinting)) { + Services.prefs.addObserver(kPrefSpoofEnglish, this); + if (this._shouldPromptForLanguagePref()) { + Services.obs.addObserver(this, kTopicHttpOnModifyRequest); + } + } else { + this._removeObservers(); + Services.prefs.setIntPref(kPrefSpoofEnglish, 0); + } + } + + _handleSpoofEnglishChanged() { + switch (Services.prefs.getIntPref(kPrefSpoofEnglish)) { + case 0: // will prompt + // This should only happen when turning privacy.resistFingerprinting off. + // Works like disabling accept-language spoofing. + case 1: // don't spoof + if (Services.prefs.prefHasUserValue("javascript.use_us_english_locale")) { + Services.prefs.clearUserPref("javascript.use_us_english_locale"); + } + // We don't reset intl.accept_languages. Instead, setting + // privacy.spoof_english to 1 allows user to change preferred language + // settings through Preferences UI. + break; + case 2: // spoof + Services.prefs.setCharPref("intl.accept_languages", "en-US, en"); + Services.prefs.setBoolPref("javascript.use_us_english_locale", true); + break; + default: + break; + } + } + + _handleHttpOnModifyRequest(subject, data) { + // If we are loading an HTTP page from content, show the + // "request English language web pages?" prompt. + let httpChannel; + try { + httpChannel = subject.QueryInterface(Ci.nsIHttpChannel); + } catch (e) { + return; + } + + if (!httpChannel) { + return; + } + + let notificationCallbacks = httpChannel.notificationCallbacks; + if (!notificationCallbacks) { + return; + } + + let loadContext = notificationCallbacks.getInterface(Ci.nsILoadContext); + if (!loadContext || !loadContext.isContent) { + return; + } + + if (!subject.URI.schemeIs("http") && !subject.URI.schemeIs("https")) { + return; + } + // The above QI did not throw, the scheme is http[s], and we know the + // load context is content, so we must have a true HTTP request from content. + // Stop the observer and display the prompt if another window has + // not already done so. + Services.obs.removeObserver(this, kTopicHttpOnModifyRequest); + + if (!this._shouldPromptForLanguagePref()) { + return; + } + + this._promptForLanguagePreference(); + + // The Accept-Language header for this request was set when the + // channel was created. Reset it to match the value that will be + // used for future requests. + let val = this._getCurrentAcceptLanguageValue(subject.URI); + if (val) { + httpChannel.setRequestHeader("Accept-Language", val, false); + } + } + + _promptForLanguagePreference() { + // Display two buttons, both with string titles. + let flags = Services.prompt.STD_YES_NO_BUTTONS; + let brandBundle = Services.strings.createBundle( + "chrome://branding/locale/brand.properties"); + let brandShortName = brandBundle.GetStringFromName("brandShortName"); + let navigatorBundle = Services.strings.createBundle( + "chrome://browser/locale/browser.properties"); + let message = navigatorBundle.formatStringFromName( + "privacy.spoof_english", [brandShortName], 1); + let response = Services.prompt.confirmEx( + null, "", message, flags, null, null, null, null, {value: false}); + + // Update preferences to reflect their response and to prevent the prompt + // from being displayed again. + Services.prefs.setIntPref(kPrefSpoofEnglish, (response == 0) ? 2 : 1); + } + + _getCurrentAcceptLanguageValue(uri) { + let channel = Services.io.newChannelFromURI2( + uri, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, + Ci.nsIContentPolicy.TYPE_OTHER); + let httpChannel; + try { + httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); + } catch (e) { + return null; + } + return httpChannel.getRequestHeader("Accept-Language"); + } +} + +let LanguagePrompt = new _LanguagePrompt(); diff --git a/toolkit/components/resistfingerprinting/moz.build b/toolkit/components/resistfingerprinting/moz.build index 514688973393..2fc3104dc2b9 100644 --- a/toolkit/components/resistfingerprinting/moz.build +++ b/toolkit/components/resistfingerprinting/moz.build @@ -13,3 +13,7 @@ FINAL_LIBRARY = 'xul' EXPORTS += [ 'nsRFPService.h', ] + +EXTRA_JS_MODULES += [ + 'LanguagePrompt.jsm', +] From 9fdde28a64162f8ea219e09cdc3317a4ce6c9a5d Mon Sep 17 00:00:00 2001 From: Chung-Sheng Fu Date: Tue, 24 Oct 2017 16:28:53 +0800 Subject: [PATCH 049/219] Bug 1039069 - Show a hint of the change of the preferred language list. r=mconley MozReview-Commit-ID: LHyXmdnyf3N --HG-- extra : rebase_source : 671f99ad432245b7dfe925e4867c76849a3bc2a7 --- browser/components/preferences/languages.js | 44 ++++++++++++++++++- browser/components/preferences/languages.xul | 8 ++++ .../chrome/browser/preferences/languages.dtd | 2 +- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/browser/components/preferences/languages.js b/browser/components/preferences/languages.js index 3f80f89eb308..814cc061376d 100644 --- a/browser/components/preferences/languages.js +++ b/browser/components/preferences/languages.js @@ -3,6 +3,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +Components.utils.import("resource://gre/modules/Services.jsm"); + var gLanguagesDialog = { _availableLanguagesList: [], @@ -149,6 +151,10 @@ var gLanguagesDialog = { this._activeLanguages.selectedIndex = selectedIndex; } + // Update states of accept-language list and buttons according to + // privacy.resistFingerprinting and privacy.spoof_english. + this.readSpoofEnglish(); + return undefined; }, @@ -157,8 +163,10 @@ var gLanguagesDialog = { }, onAvailableLanguageSelect() { + var availableLanguages = this._availableLanguages; var addButton = document.getElementById("addButton"); - addButton.disabled = false; + addButton.disabled = availableLanguages.disabled || + availableLanguages.selectedIndex < 0; this._availableLanguages.removeAttribute("accesskey"); }, @@ -291,6 +299,40 @@ var gLanguagesDialog = { downButton.disabled = true; removeButton.disabled = false; } + }, + + readSpoofEnglish() { + var checkbox = document.getElementById("spoofEnglish"); + var resistFingerprinting = Services.prefs.getBoolPref("privacy.resistFingerprinting"); + if (!resistFingerprinting) { + checkbox.hidden = true; + return false; + } + + var spoofEnglish = document.getElementById("privacy.spoof_english").value; + var activeLanguages = this._activeLanguages; + var availableLanguages = this._availableLanguages; + checkbox.hidden = false; + switch (spoofEnglish) { + case 1: // don't spoof intl.accept_lanauges + activeLanguages.disabled = false; + activeLanguages.selectItem(activeLanguages.firstChild); + availableLanguages.disabled = false; + this.onAvailableLanguageSelect(); + return false; + case 2: // spoof intl.accept_lanauges + activeLanguages.clearSelection(); + activeLanguages.disabled = true; + availableLanguages.disabled = true; + this.onAvailableLanguageSelect(); + return true; + default: // will prompt for spoofing intl.accept_lanauges if resisting fingerprinting + return false; + } + }, + + writeSpoofEnglish() { + return document.getElementById("spoofEnglish").checked ? 2 : 1; } }; diff --git a/browser/components/preferences/languages.xul b/browser/components/preferences/languages.xul index 45f7a1ce758a..ce1073a2b430 100644 --- a/browser/components/preferences/languages.xul +++ b/browser/components/preferences/languages.xul @@ -40,6 +40,9 @@ + diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/test-bug-597136-external-script-errors.js b/devtools/client/webconsole/new-console-output/test/mochitest/test-external-script-errors.js similarity index 100% rename from devtools/client/webconsole/new-console-output/test/mochitest/test-bug-597136-external-script-errors.js rename to devtools/client/webconsole/new-console-output/test/mochitest/test-external-script-errors.js From 689bddee1283c9aa53cf63d7e1accedcc32426d5 Mon Sep 17 00:00:00 2001 From: Gabriele Svelto Date: Fri, 1 Dec 2017 15:11:21 +0100 Subject: [PATCH 073/219] Bug 1421874 - Fix SessionStore to include AppConstants before using it; r=sebastian MozReview-Commit-ID: IOSHvaOOE19 --HG-- extra : rebase_source : 674a7d486a156187bad7427abdb8a6feadf773a8 --- mobile/android/components/SessionStore.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/android/components/SessionStore.js b/mobile/android/components/SessionStore.js index 3c5b077b8d87..7ec914314ff9 100644 --- a/mobile/android/components/SessionStore.js +++ b/mobile/android/components/SessionStore.js @@ -8,6 +8,7 @@ const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; +Cu.import("resource://gre/modules/AppConstants.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); From cd431e85f55813d39bbea27f64314f8069d285ae Mon Sep 17 00:00:00 2001 From: Henrik Skupin Date: Fri, 1 Dec 2017 22:15:44 +0100 Subject: [PATCH 074/219] Bug 1422373 - Re-add Firefox UI safebrowsing tests. r=jmaher MozReview-Commit-ID: 1BitgKPG1JD --HG-- extra : rebase_source : 3f79d15ef377d0aac546e1227f255fcf0f961ef0 --- testing/firefox-ui/tests/functional/manifest.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/firefox-ui/tests/functional/manifest.ini b/testing/firefox-ui/tests/functional/manifest.ini index bc254e96291d..fd9ac0a948d9 100644 --- a/testing/firefox-ui/tests/functional/manifest.ini +++ b/testing/firefox-ui/tests/functional/manifest.ini @@ -1,5 +1,6 @@ [include:keyboard_shortcuts/manifest.ini] [include:locationbar/manifest.ini] [include:private_browsing/manifest.ini] +[include:safebrowsing/manifest.ini] [include:security/manifest.ini] [include:sessionstore/manifest.ini] From afb8b81041678aaedaf111b0972237c8f2ea8992 Mon Sep 17 00:00:00 2001 From: Edouard Oger Date: Thu, 30 Nov 2017 16:01:40 -0500 Subject: [PATCH 075/219] Bug 1422106 - Show broken heart when unverified in synced tabs sidebar/panel. r=markh MozReview-Commit-ID: BDTdmcIOHmn --HG-- extra : rebase_source : 747637f3746356e3da208acc41a3629bd2597e2e --- browser/base/content/browser-menubar.inc | 7 ++++++- browser/base/content/browser-sets.inc | 1 + browser/base/content/browser-sync.js | 6 ++++-- browser/base/content/test/sync/browser_sync.js | 9 +++++---- .../components/customizableui/content/panelUI.inc.xul | 11 +++++++++++ .../customizableui/test/browser_synced_tabs_menu.js | 7 +++++++ .../components/syncedtabs/SyncedTabsDeckComponent.js | 6 +++++- browser/components/syncedtabs/sidebar.xhtml | 5 +++++ .../test/browser/browser_sidebar_syncedtabslist.js | 6 ++++++ .../test/xpcshell/test_SyncedTabsDeckComponent.js | 2 +- browser/locales/en-US/chrome/browser/browser.dtd | 2 ++ .../browser/browser_BrowserUITelemetry_syncedtabs.js | 2 ++ browser/themes/shared/customizableui/panelUI.inc.css | 1 + 13 files changed, 56 insertions(+), 9 deletions(-) diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc index e818cdfafcbe..dab38488c671 100644 --- a/browser/base/content/browser-menubar.inc +++ b/browser/base/content/browser-menubar.inc @@ -477,12 +477,17 @@ key="key_openAddons" command="Tools:Addons"/> - + + +

&syncedTabs.sidebar.notsignedin.label;

+
+
+

&syncedTabs.sidebar.unverified.label;

+ +

&syncedTabs.sidebar.noclients.subtitle;

diff --git a/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js b/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js index fc492893d5ed..fcd278f89f74 100644 --- a/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js +++ b/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js @@ -228,6 +228,12 @@ add_task(async function testSyncedTabsSidebarStatus() { Assert.ok(selectedPanel.classList.contains("notAuthedInfo"), "not-authed panel is selected"); + account = {verified: false}; + await syncedTabsDeckComponent.updatePanel(); + selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected"); + Assert.ok(selectedPanel.classList.contains("unverified"), + "unverified panel is selected"); + account = {verified: true}; await syncedTabsDeckComponent.updatePanel(); selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected"); diff --git a/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js b/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js index ceef304ada84..8f8a6c43f6e2 100644 --- a/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js +++ b/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js @@ -159,7 +159,7 @@ add_task(async function testPanelStatus() { account = {verified: false}; result = await component.getPanelStatus(); - Assert.equal(result, component.PANELS.NOT_AUTHED_INFO); + Assert.equal(result, component.PANELS.UNVERIFIED); account = {verified: true}; diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index 7f677278158a..ca57bfe3e478 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -381,6 +381,7 @@ These should match what Safari and other Apple applications use on OS X Lion. -- + @@ -794,6 +795,7 @@ you can use these alternative items. Otherwise, their values should be empty. - + + - -
-
-
-

&syncedTabs.sidebar.unverified.label;

- -

&syncedTabs.sidebar.noclients.subtitle;

diff --git a/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js b/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js index fcd278f89f74..fc492893d5ed 100644 --- a/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js +++ b/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js @@ -228,12 +228,6 @@ add_task(async function testSyncedTabsSidebarStatus() { Assert.ok(selectedPanel.classList.contains("notAuthedInfo"), "not-authed panel is selected"); - account = {verified: false}; - await syncedTabsDeckComponent.updatePanel(); - selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected"); - Assert.ok(selectedPanel.classList.contains("unverified"), - "unverified panel is selected"); - account = {verified: true}; await syncedTabsDeckComponent.updatePanel(); selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected"); diff --git a/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js b/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js index 8f8a6c43f6e2..ceef304ada84 100644 --- a/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js +++ b/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js @@ -159,7 +159,7 @@ add_task(async function testPanelStatus() { account = {verified: false}; result = await component.getPanelStatus(); - Assert.equal(result, component.PANELS.UNVERIFIED); + Assert.equal(result, component.PANELS.NOT_AUTHED_INFO); account = {verified: true}; diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index ca57bfe3e478..7f677278158a 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -381,7 +381,6 @@ These should match what Safari and other Apple applications use on OS X Lion. -- - @@ -795,7 +794,6 @@ you can use these alternative items. Otherwise, their values should be empty. - - 4o_;Osm^QiDqtb#imhEOXHZSTTtxVi%~zdh#Kwr$(CZQHhO+qP}nw(Z^ad#>-z^t4HuH^rQ1=C|HA?Y~IMFrm3c zp@n8Pn}bjSJunvsaU0L^89(tB4{;IeFbWNk90qqBN@@(k8T^7hy~GMshMy}B#UHJ4 z8KU}xc}V9gqN9G*dGYmwO5h7%VjU@H?Y zUXl_aKhsS$h=nacg^t{mvI6oaRme6EBUuZ)WKG~z$cUyaABsy6&d$~ zb+p=G2g%;1o5KHZL}Q$gtW!tuk@{v>>kGOgk-Zx`hky4Ag=GF;53GW;W`cJp=@Abj zV69(xiPUO06a$v}h(d^oD(H;i7=Vr_fy6itY0Grj;|5?G)?*;P!diC`SfnME3869``DIuq;Fv8c;B3mIk6UM+#r@<700RfQHMdUz9 zOZW7jA-d)|A1DsQPZ9D~K+y&I4)*6S6^0*Kh;-r30YtP3Sx_CT5nty4WrLc&>2U=82HyYLh9^99 z9#9&n>8*63O3=E^PVlmLDyZqPbfHGjx`X0JAnY1+)beX!+V&IC9Xz0%P}4%`LVY2k z%?Rtj0hNcEdP*1Sjf(>JWWfr~R3MTfx&{O41U2QCF4O?iA*x5H0%HhgBtH7#F8jJ3&N3IL}5F3?FXQy+c3&`SJ*>noyp48{yA2m5)5_@Cj->>4xVy0G8`frjwr~E zTu6tw8Xr0Dggu3qvylY&0TIoBhoFZQ)nHFQ;30QovmvU($g0sx%7gy=b#{D$JzZ3? z@p$+Sk-fq?G(=p5!-;^y_=&X|oz^i*FQMrW-Fu8hN(JxtNsg1yx?D;J6cw+bSJyBX z9Z?*4kQWuvAN%kG7om0g)ZWhP!re0=lRbBaTj0(Ouy=0}5qIH^Ern654~l$-JGESs zV=~Ey=f1-ongFASqt!AlKBP~mtH(Ro-@w({gt&UXLHi0^p+Zl3@E002ovPDHLkV1oIkfsX(H diff --git a/mobile/android/app/src/photon/res/drawable-xxxhdpi/flat_icon.png b/mobile/android/app/src/photon/res/drawable-xxxhdpi/flat_icon.png deleted file mode 100644 index 3c86961bb08700025b9d1e28454d826fe058540c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1887 zcmV-l2cYE`?W{?D(IW_CC38eU#r zUS3|hboA)atauQgp$d9pCYEC}_9GpKu^p>1AA?aJKjB#vG%V$<@Jx6f)iDLBkkvA@ z!Y3$T5Rtdi6_!^*!8 z{e%4bc(jGpwxJ3xRs23;@G7RmYO|4md+>=<6EGV)g6vvOgq3#TYs6{rMfEtw!Ajfl z8ICm&xm7Ocy*Sn7=l#1V%)`aSAW+kgS#af#fLZ%-mBR;*Z{r@sp(E_H0mWr5(9a0g800wX9R3}6318q7yotwA z@Z^6}94ip0*EQHPDufwWh>uYhc3O;_vKFQ@F4h7}Mk<1}4OMw|OI94hS!%iQ{_<5KF6;0IioYuWagGgJDBb32&K~>*qd}8rm zh*l$%fr~;_B{jcvx*Q^<;&Rb}3qw^0af`;2@B&1uA-*S|w%xcx<4Kqdk#-_R?KVKX zH1xn<_!3{>GrWiQ@d@sAW&#*ryn96kEDc>7@6dNaZHUyy z?m)w!bKM-8fTtkRC_97P37t#B{SHk)W*mXtR@fP&D|GG;hxdkt!){4dUpxV6(6Mxs z)?jzE8bm5+<;DMjj*Y~Pnj8;&36ZW8Kl|xiYw(I@2V{>yq)QPK?1is^E~TR;vg&q2 z8h@PtkrMDdqD8NMscH@G)aHD77ZsL9awKpt7K=F51RR8nnwj7CqQ3qvTKO^XLq>m^4e&0U=r@;4Kmug-jM)Hh zVj)!LM`;&Kz$1{654p?+cnKBZR6~(q^C_DT(H+aN2b<9!B~V1tioZ_ssrm)M9!$Fz zPIeGAP&mj6xeN8N2f;dmDu@el0)EBA5^q#!9B80>5RD`_T{6a^5`M++Xo_Wm*OIe_ zFafPmN#ZW(vjOh|J^AN3h_wC{@fRDgAtJ3tAv~hwS8z9hF~Ee^NlLjx3)v&uSZ$M^A$YysT z{($P{pfIk&b9e_Y;1S%8EQ-#0E`;ogiaes43e_!E|4}tcZqLjkvM_DnL>q9Iq7@!{ z-nQ=uWVcgv;jV=rPLz!5C>*90AB8_r>*U|xgbfHsqvd{Fjr!JN5NhKM31>Lt&U#;q z&=f}yuKo`76L3@DbUW}P@}qDFzoQ@;-Jr5-6g(TNtW_wFo9#SYfnV_j+CgQrWqq9E zI_TCG^hJ4mj#u$K-oOuNfayp*X#rIChP;n*^is9)HP6vFfV=t1?1>=y4Q1f9OhM^w5#qEeTyiD)e zc@nkpIj+SMD1!mmgtPOb3_iw3cpER_aomJLh%v}3gnM~RezC;ecpX3BCtPWGd3m|C Z{{cbnqftbfB})JR002ovPDHLkV1hBAd?f$? From caac6a3325ab98dd3a7a26cd2a6f66ea913cd4e9 Mon Sep 17 00:00:00 2001 From: James Teh Date: Mon, 4 Dec 2017 15:32:56 +1000 Subject: [PATCH 143/219] Bug 1422201: Only handle remote ids passed to IAccessible::accChild on the root accessible. r=MarcoZ Previously, we could return remote accessibles which weren't actually descendants of the accessible on which accChild was called. For example, calling accChild on a local document with the id of a remote document would happily return the remote accessible. This confused clients such as NVDA which use accChild to check whether something is a descendant of a document. MozReview-Commit-ID: 8mJ4m6RC3r2 --HG-- extra : rebase_source : c56040d84e09c2b9ede94985cdd94606c27160a3 --- accessible/windows/msaa/AccessibleWrap.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index 83731f7d486c..60179df48046 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -1481,6 +1481,13 @@ AccessibleWrap::GetIAccessibleFor(const VARIANT& aVarChild, bool* aIsDefunct) // window and the child id points in the content documents. Thus we need to // make sure that it is never called on proxies. if (XRE_IsParentProcess() && !IsProxy() && !sIDGen.IsChromeID(varChild.lVal)) { + if (!IsRoot()) { + // Bug 1422201: accChild with a remote id is only valid on the root accessible. + // Otherwise, we might return remote accessibles which aren't descendants + // of this accessible. This would confuse clients which use accChild to + // check whether something is a descendant of a document. + return nullptr; + } return GetRemoteIAccessibleFor(varChild); } From 87408bd0b048cd468090ee22a9dd0d0752375248 Mon Sep 17 00:00:00 2001 From: Ethan Lin Date: Mon, 4 Dec 2017 11:12:25 +0800 Subject: [PATCH 144/219] Bug 1422013 - Invalidate image items for webrender. r=mattwoodrow MozReview-Commit-ID: 701MZhcrf4l --HG-- extra : rebase_source : 8bed0e5f812570685cf04f5b371da885294513e0 --- gfx/layers/wr/WebRenderCommandBuilder.cpp | 8 ++-- gfx/layers/wr/WebRenderUserData.cpp | 4 +- gfx/layers/wr/WebRenderUserData.h | 3 +- layout/painting/FrameLayerBuilder.cpp | 24 ++++++------ layout/painting/FrameLayerBuilder.h | 2 - layout/style/ImageLoader.cpp | 48 +++++++++++++++++------ 6 files changed, 58 insertions(+), 31 deletions(-) diff --git a/gfx/layers/wr/WebRenderCommandBuilder.cpp b/gfx/layers/wr/WebRenderCommandBuilder.cpp index 26f6f2abaf17..24603ab456fd 100644 --- a/gfx/layers/wr/WebRenderCommandBuilder.cpp +++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp @@ -531,7 +531,7 @@ WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem, LayoutDeviceIntPoint offset = RoundedToInt(bounds.TopLeft()); aImageRect = LayoutDeviceRect(offset, LayoutDeviceSize(RoundedToInt(bounds.Size()))); LayerRect paintRect = LayerRect(LayerPoint(0, 0), LayerSize(paintSize)); - nsAutoPtr geometry = fallbackData->GetGeometry(); + nsDisplayItemGeometry* geometry = fallbackData->GetGeometry(); // nsDisplayFilter is rendered via BasicLayerManager which means the invalidate // region is unknown until we traverse the displaylist contained by it. @@ -561,6 +561,10 @@ WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem, } if (needPaint || !fallbackData->GetKey()) { + nsAutoPtr newGeometry; + newGeometry = aItem->AllocateGeometry(aDisplayListBuilder); + fallbackData->SetGeometry(Move(newGeometry)); + gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ? gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8; if (useBlobImage) { @@ -645,13 +649,11 @@ WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem, } } - geometry = aItem->AllocateGeometry(aDisplayListBuilder); fallbackData->SetScale(scale); fallbackData->SetInvalid(false); } // Update current bounds to fallback data - fallbackData->SetGeometry(Move(geometry)); fallbackData->SetBounds(paintBounds); MOZ_ASSERT(fallbackData->GetKey()); diff --git a/gfx/layers/wr/WebRenderUserData.cpp b/gfx/layers/wr/WebRenderUserData.cpp index eef58932c16c..ea4e9200a561 100644 --- a/gfx/layers/wr/WebRenderUserData.cpp +++ b/gfx/layers/wr/WebRenderUserData.cpp @@ -246,10 +246,10 @@ WebRenderFallbackData::~WebRenderFallbackData() { } -nsAutoPtr +nsDisplayItemGeometry* WebRenderFallbackData::GetGeometry() { - return mGeometry; + return mGeometry.get(); } void diff --git a/gfx/layers/wr/WebRenderUserData.h b/gfx/layers/wr/WebRenderUserData.h index 4d90cd4e9a91..67f66688e570 100644 --- a/gfx/layers/wr/WebRenderUserData.h +++ b/gfx/layers/wr/WebRenderUserData.h @@ -58,6 +58,7 @@ public: uint32_t GetDisplayItemKey() { return mDisplayItemKey; } void RemoveFromTable(); virtual void ClearCachedResources() {}; + virtual nsDisplayItemGeometry* GetGeometry() { return nullptr; } protected: virtual ~WebRenderUserData(); @@ -121,7 +122,7 @@ public: virtual WebRenderFallbackData* AsFallbackData() override { return this; } virtual UserDataType GetType() override { return UserDataType::eFallback; } static UserDataType Type() { return UserDataType::eFallback; } - nsAutoPtr GetGeometry(); + nsDisplayItemGeometry* GetGeometry() override; void SetGeometry(nsAutoPtr aGeometry); nsRect GetBounds() { return mBounds; } void SetBounds(const nsRect& aRect) { mBounds = aRect; } diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp index 050aaa23d63b..5e5f8d3c6a37 100644 --- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -2060,20 +2060,14 @@ FrameLayerBuilder::HasRetainedDataFor(nsIFrame* aFrame, uint32_t aDisplayItemKey return true; } } - return false; -} - -void -FrameLayerBuilder::IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback) -{ - const SmallPointerArray& array = aFrame->DisplayItemData(); - - for (uint32_t i = 0; i < array.Length(); i++) { - DisplayItemData* data = DisplayItemData::AssertDisplayItemData(array.ElementAt(i)); - if (data->mDisplayItemKey != 0) { - aCallback(aFrame, data); + if (auto userDataTable = + aFrame->GetProperty(nsIFrame::WebRenderUserDataProperty())) { + RefPtr data = userDataTable->Get(aDisplayItemKey); + if (data) { + return true; } } + return false; } DisplayItemData* @@ -6279,6 +6273,12 @@ FrameLayerBuilder::GetMostRecentGeometry(nsDisplayItem* aItem) return data->GetGeometry(); } } + if (auto userDataTable = + aItem->Frame()->GetProperty(nsIFrame::WebRenderUserDataProperty())) { + if (RefPtr data = userDataTable->Get(itemPerFrameKey)) { + return data->GetGeometry(); + } + } return nullptr; } diff --git a/layout/painting/FrameLayerBuilder.h b/layout/painting/FrameLayerBuilder.h index 6a25d9e721b7..e1ec00e8a3cf 100644 --- a/layout/painting/FrameLayerBuilder.h +++ b/layout/painting/FrameLayerBuilder.h @@ -555,8 +555,6 @@ public: typedef void (*DisplayItemDataCallback)(nsIFrame *aFrame, DisplayItemData* aItem); - static void IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback); - /** * Save transform that was in aLayer when we last painted, and the position * of the active scrolled root frame. It must be an integer diff --git a/layout/style/ImageLoader.cpp b/layout/style/ImageLoader.cpp index be5544687c81..466428a0c3c9 100644 --- a/layout/style/ImageLoader.cpp +++ b/layout/style/ImageLoader.cpp @@ -324,22 +324,48 @@ ImageLoader::GetPresContext() return shell->GetPresContext(); } -void InvalidateImagesCallback(nsIFrame* aFrame, - DisplayItemData* aItem) +static bool +IsRenderNoImages(uint32_t aDisplayItemKey) { - DisplayItemType type = GetDisplayItemTypeFromKey(aItem->GetDisplayItemKey()); + DisplayItemType type = GetDisplayItemTypeFromKey(aDisplayItemKey); uint8_t flags = GetDisplayItemFlagsForType(type); + return flags & TYPE_RENDERS_NO_IMAGES; +} - if (flags & TYPE_RENDERS_NO_IMAGES) { - return; +void InvalidateImages(nsIFrame* aFrame) +{ + bool invalidateFrame = false; + const SmallPointerArray& array = aFrame->DisplayItemData(); + for (uint32_t i = 0; i < array.Length(); i++) { + DisplayItemData* data = DisplayItemData::AssertDisplayItemData(array.ElementAt(i)); + uint32_t displayItemKey = data->GetDisplayItemKey(); + if (displayItemKey != 0 && !IsRenderNoImages(displayItemKey)) { + if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { + DisplayItemType type = GetDisplayItemTypeFromKey(displayItemKey); + printf_stderr("Invalidating display item(type=%d) based on frame %p \ + because it might contain an invalidated image\n", + static_cast(type), aFrame); + } + + data->Invalidate(); + invalidateFrame = true; + } + } + if (auto userDataTable = + aFrame->GetProperty(nsIFrame::WebRenderUserDataProperty())) { + for (auto iter = userDataTable->Iter(); !iter.Done(); iter.Next()) { + RefPtr data = iter.UserData(); + if (data->GetType() == layers::WebRenderAnimationData::UserDataType::eFallback && + !IsRenderNoImages(data->GetDisplayItemKey())) { + static_cast(data.get())->SetInvalid(true); + } + invalidateFrame = true; + } } - if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf_stderr("Invalidating display item(type=%d) based on frame %p \ - because it might contain an invalidated image\n", static_cast(type), aFrame); + if (invalidateFrame) { + aFrame->SchedulePaint(); } - aItem->Invalidate(); - aFrame->SchedulePaint(); } void @@ -359,7 +385,7 @@ ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint) // might not find the right display item. frame->InvalidateFrame(); } else { - FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback); + InvalidateImages(frame); // Update ancestor rendering observers (-moz-element etc) nsIFrame *f = frame; From a2b5ec45e822cb21d48b0dca500546ad71f0f5f8 Mon Sep 17 00:00:00 2001 From: Liang-Heng Chen Date: Fri, 1 Dec 2017 15:10:36 +0800 Subject: [PATCH 145/219] Bug 1421793 - nsPACMan needs to be shutdown before dtor; r=schien MozReview-Commit-ID: D2aKTC8s9MQ --HG-- extra : rebase_source : e1442aebd9bd5f6e155ba4e33a9b30a97b3427e3 --- netwerk/base/nsProtocolProxyService.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/netwerk/base/nsProtocolProxyService.cpp b/netwerk/base/nsProtocolProxyService.cpp index 19e6eb26651b..11d90a6d156c 100644 --- a/netwerk/base/nsProtocolProxyService.cpp +++ b/netwerk/base/nsProtocolProxyService.cpp @@ -1166,8 +1166,10 @@ nsProtocolProxyService::SetupPACThread(nsIEventTarget *mainThreadEventTarget) rv = mPACMan->Init(nullptr); } - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { + mPACMan->Shutdown(); mPACMan = nullptr; + } return rv; } From 69a04c680f2f0bd5890e1ccca3ad96769d20477c Mon Sep 17 00:00:00 2001 From: "Francesco Lodolo (:flod)" Date: Sun, 3 Dec 2017 09:30:31 +0100 Subject: [PATCH 146/219] Bug 914817 - [ja] Update icon for Rakuten searchplugin, switch SearchForm to https r=mkaply MozReview-Commit-ID: FJsgvRgtFuh --HG-- extra : rebase_source : bc4c51468b9f612a7c7618c58b8299323e8cf284 --- browser/locales/searchplugins/rakuten.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/locales/searchplugins/rakuten.xml b/browser/locales/searchplugins/rakuten.xml index 942a5dbd9504..fc7baff99644 100644 --- a/browser/locales/searchplugins/rakuten.xml +++ b/browser/locales/searchplugins/rakuten.xml @@ -6,11 +6,11 @@ 楽天市場 楽天市場 商品検索 EUC-JP -data:image/x-icon;base64,R0lGODlhEAAQALMOAOefn9psbMswMMMQEPjj4/PPz++/v+OPj99/f9+AgMcgIM9AQOuvr////wAAAL8AACH5BAEAAA4ALAAAAAAQABAAAARf0EnJ0FqIze2YemCoaBsQniEwMeIloOQHJk1NvKDSnTRQNIZThddYPAiNk4UYCDQIpwux1ghEEUTGc6BkhWiLA1DokD3Ag5/1odvlFlzFAkdymFAn1caDH3FWFhh1EhEAOw== +data:image/x-icon;base64,AAABAAIAEBAAAAEAIACYAgAAJgAAACAgAAABACAARwUAAL4CAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAJfSURBVDiNdZFLSJRhFIaf8/9jOoFa3tLEBC/JCEU5yUgGahIS5TIqiCxTNIiKpCiiVWJBpV0Iw9vGnbMqokW6kYRJZCK6qRNmXhJprMzQ0XH8vxZOo47T2Z7zPuc95xWCytV4J3ug6WE5IoWiSFWiDIEhFJ3Z1TWt6VUXR1fPS0DoehE+cLT6NnBOBFMwGADFXNweW11eU0ediKgAwC9+KkJJSOE6jmordY5WiIjSAQ70vLorwgkApQyiMi1EZWSxMXkb4THxLEx9B8MAEb9t2f3D6fjd+ubTaxl83GBxtTS8R9ABDK+X3PttJBUfDGycGx/BUXUMz+TEigulZjLKr6TpJZP91xDyA42lJZJLSjEnJNF3qZJf75wkFR8iMiOLsWd2RNeXXYiEa5oxblJQEPjkqjJ8i7gd3SzNe9iybz/Rlp2Iae1vp5yOIpMoUglBEE0nMi2TiMStbN6Vy5/P/SjfIhK2YdWQpJiWc15PCIuKpsDeBcDizDQf62+imcKC4zA0UXwJcQE+zxyDT+5heL3Mfhtj+sPbQAorBhjWELpCAYyFeQYb6+l/dItNlh1Yzl/F8PnWzMTm2Dq17KqaZqXUfDBaNB3NZGKovQm3o5v0srMk5BeCUn73uDNPXegQAEflketTzt5aAKUU5oRE9Agzs2NfAdDDIzAnJePzzOGZnEBEiLHaTuY329vFL9KeW1NbgNOhzgmuWKutdm+z/QaAtuxYjMPOkTMxVttlpdTM/4QK3LE5eWX/xMD6/FytD+LdvT3Hf/b1FikhRcBAMRxrtb3cXlFjj8vLW7PgL85j5b2Pva1GAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAFDklEQVRYha2Wa2xUVRSFv33ubadT2lLCQwSngwQRCYVCiwIqSIgPEHkkPDQQoyYYjBgjCBgfGFCDkT/8UjCRVE0UwYhUXlEiEiJRbCkgNEKgQikoD0npTDvtzL13+6N0+hqmM4SVTHLu3mevte6dk7O3kCK+n3BvrmluHI/HQ4KMVNGgQL6qeMBVhDNApYi3v7yitnw1eKnwSncbto0KDrIs7yVgviDBVEgVPWJUNjl+f+msgydDt2Rg3yTscCiwQpHXBXqlItzVCKeM8Pb0ipqtaRkoKx5cgBcrFZHJtyKcABsiTTmvzquqinZroKw4OAxPd4ow+DaJ34DuznSicx8/dqmhfdS0f9hVOPAuUW/X7RcHkKlRK3PzvknY7aNW66K8uDjjuolsF6Soa63gOTGchga8aHOXn8aigCCW1aW0E8/Q5mhe1tf/1O+Nh1oXZcWBN0RlbaI6L9pMr1ElFMx6uktOXZdwTTWXD/xM/akqLH82YkwClhv7QfG8yTOO1O6PG9hWMiBgu9YJRHITFTnhEIPmPkvR++tvSuxGGjnz+Qb++ngdYtnJTSiHKiprxq8GzwBYnrXkZuJA/C9IBsufzdDFSxmxYg1uUyTpXhHuLykOPgFgtgzvmwOyIGlFJzjhELU7vuX89m+4+NMOYvXX47nBCxfRb8IjuJHkJvD0RQDb588aL8rAdAxELv9LxfLFuLEYiJA/vJBxn3xF9oAAAIHZz3D5133JSUQn7Srq39cYZWI64gBiDHZOLhm5eWTm9aTuz0rObi6N53vecx9Wdg/US9YOJN+xMkYbRUama6CrIYtYuL7t2baSHsL4PkyhDRpMoSd1gHoeTjiE5zioemTm9SIwY34833ihBqexAdufnZzI8wpsID8tdSAzL5+7Fy5CHQfL72fg1Nn0Khwdz1/YUwZJP38rJM+WFPt2e/j69GXkWwnvLK4dLefinu1Y3b09oKKeAbmaroHEbMqV3w9QvnQRbiya2hlQrhmgOl0tz4nRWHuOWLht1lDg7JYvCFWfwvJlpUZkqDYoh9M10HjhPL/MmcKBBU/SdPkSACJC4cr3yBsyDLepKSUeceWoUfW6uTESQBUvFqPu+BGOrlkOqgBk9evPyFUfgWo3dwAAFxujkWMmt2dtJXAiXQ9iDBl5Pbn4Yxlnvvw0Hr/j4SkMeeFlnHDSURBV3Tmv6krYTN6PI8rGdA20ws7uQdX6D6g7fiQeG7ZkJb3HTsBpbEhYo6BGZCPcmIgcv79U0XNJ7CJW2yBjZfnja7Fs3KYIlateiwtaPh8l6zaS1a8/6jhd6AT9bvrhmoqW9Q38UFQwB0PC6dVzHHoEgvQe/QAAsXCISwf2tlw20kLhNkXoM/ZBehQMQlUxGZn898dBwjV/Y+w286pabxvGTKs4f6aDAYCy0YFPRWRRV8uCxmK4zS2nW4xJeNG4zU0d3tjK8iO2HT+kAB76/MzD50tbnzsMiHbI94qbGw0iPNaBWRWxbWw7J9EHahP0ZYGvU7CduCprZ1a2iUOnqXja6dPNrt8/R1V3JlW6BaiydkZlzZud4wnb4L5J2KH6wIcisuw2SNehZtlTlec2Jcom7cPbiwOPGo81iIy7JWllq2eZd2aVnz15sz3dDgLvgikZE5yp6HOCTgRJ2r5VqRV0jyfms5mHz/3WHX9ak8ju4oI7Y64UYXSEqAYVyRFQhGsK1YIes2zf0WmHTtd3z9aC/wGRJv+fU1BOjgAAAABJRU5ErkJggg== -http://www.rakuten.co.jp/ +https://www.rakuten.co.jp/ From f2f6e686dff4e56cfd239d64dc03bc4e23b67205 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Mon, 4 Dec 2017 16:29:35 +1300 Subject: [PATCH 147/219] Bug 1422662 - Move BaseMediaResource::Close() into MediaResource class. r=jwwang For the project to export Gecko's media stack and import it into Servo, I need a way to shutdown an abstract MediaResource. So I'd like to move BaseMediaResource::Close() up into MediaResource class. MozReview-Commit-ID: 9JmxJPs02PN --HG-- extra : rebase_source : 77128d1f78bea516dda92cdae6975d010a4b303c --- dom/media/BaseMediaResource.h | 5 ----- dom/media/MediaResource.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dom/media/BaseMediaResource.h b/dom/media/BaseMediaResource.h index 28a7d9007892..1fdfb6dd75cb 100644 --- a/dom/media/BaseMediaResource.h +++ b/dom/media/BaseMediaResource.h @@ -29,11 +29,6 @@ public: nsIChannel* aChannel, bool aIsPrivateBrowsing); - // Close the resource, stop any listeners, channels, etc. - // Cancels any currently blocking Read request and forces that request to - // return an error. - virtual nsresult Close() = 0; - // Pass true to limit the amount of readahead data (specified by // "media.cache_readahead_limit") or false to read as much as the // cache size allows. diff --git a/dom/media/MediaResource.h b/dom/media/MediaResource.h index 097cb0c0e4a1..a23ba4e55ef9 100644 --- a/dom/media/MediaResource.h +++ b/dom/media/MediaResource.h @@ -56,6 +56,11 @@ public: NS_METHOD_(MozExternalRefCountType) AddRef(void); NS_METHOD_(MozExternalRefCountType) Release(void); + // Close the resource, stop any listeners, channels, etc. + // Cancels any currently blocking Read request and forces that request to + // return an error. + virtual nsresult Close() { return NS_OK; } + // These methods are called off the main thread. // Read up to aCount bytes from the stream. The read starts at // aOffset in the stream, seeking to that location initially if From 3f75a6d4107196f297ec0f6b9539efef24e8104e Mon Sep 17 00:00:00 2001 From: Coroiu Cristina Date: Mon, 4 Dec 2017 09:21:51 +0200 Subject: [PATCH 148/219] Backed out changeset 7a2629417a47 (bug 1422662) for bustage r=backout on a CLOSED TREE --- dom/media/BaseMediaResource.h | 5 +++++ dom/media/MediaResource.h | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dom/media/BaseMediaResource.h b/dom/media/BaseMediaResource.h index 1fdfb6dd75cb..28a7d9007892 100644 --- a/dom/media/BaseMediaResource.h +++ b/dom/media/BaseMediaResource.h @@ -29,6 +29,11 @@ public: nsIChannel* aChannel, bool aIsPrivateBrowsing); + // Close the resource, stop any listeners, channels, etc. + // Cancels any currently blocking Read request and forces that request to + // return an error. + virtual nsresult Close() = 0; + // Pass true to limit the amount of readahead data (specified by // "media.cache_readahead_limit") or false to read as much as the // cache size allows. diff --git a/dom/media/MediaResource.h b/dom/media/MediaResource.h index a23ba4e55ef9..097cb0c0e4a1 100644 --- a/dom/media/MediaResource.h +++ b/dom/media/MediaResource.h @@ -56,11 +56,6 @@ public: NS_METHOD_(MozExternalRefCountType) AddRef(void); NS_METHOD_(MozExternalRefCountType) Release(void); - // Close the resource, stop any listeners, channels, etc. - // Cancels any currently blocking Read request and forces that request to - // return an error. - virtual nsresult Close() { return NS_OK; } - // These methods are called off the main thread. // Read up to aCount bytes from the stream. The read starts at // aOffset in the stream, seeking to that location initially if From b970e21a6901d0b51a010552962fe392a2ec946b Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Sun, 12 Nov 2017 16:06:18 +0900 Subject: [PATCH 149/219] Bug 1032671 - Part 1. Add emoji presentation API for UTR#51. r=jfkthame From ICU 57, ICU supports emoji presentation as draft API. MozReview-Commit-ID: 6JgQMv4Ky9m --HG-- extra : rebase_source : 60f451e7476f16ed48aeaccb1fc55d3962a2b4b2 --- intl/unicharutil/util/nsUnicodeProperties.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/intl/unicharutil/util/nsUnicodeProperties.h b/intl/unicharutil/util/nsUnicodeProperties.h index 03fafc547440..c16e67521ad0 100644 --- a/intl/unicharutil/util/nsUnicodeProperties.h +++ b/intl/unicharutil/util/nsUnicodeProperties.h @@ -46,6 +46,12 @@ enum IdentifierType { IDTYPE_ALLOWED = 1, }; +enum EmojiPresentation { + TextOnly = 0, + TextDefault = 1, + EmojiDefault = 2 +}; + extern const hb_unicode_general_category_t sICUtoHBcategory[]; inline uint32_t @@ -172,6 +178,19 @@ IsDefaultIgnorable(uint32_t aCh) return u_hasBinaryProperty(aCh, UCHAR_DEFAULT_IGNORABLE_CODE_POINT); } +inline EmojiPresentation +GetEmojiPresentation(uint32_t aCh) +{ + if (!u_hasBinaryProperty(aCh, UCHAR_EMOJI)) { + return TextOnly; + } + + if (u_hasBinaryProperty(aCh, UCHAR_EMOJI_PRESENTATION)) { + return EmojiDefault; + } + return TextDefault; +} + // returns the simplified Gen Category as defined in nsUGenCategory inline nsUGenCategory GetGenCategory(uint32_t aCh) { return sDetailedToGeneralCategory[GetGeneralCategory(aCh)]; From d60606ea884d3cac53558d8df7e8ab218e8c9b53 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Sun, 12 Nov 2017 16:24:58 +0900 Subject: [PATCH 150/219] Bug 1032671 - Part 2. Use font.name-list.emoji preference for emoji presenration. r=jfkthame MozReview-Commit-ID: DbrGeXzCpNT --HG-- extra : rebase_source : 2a824295ff9c89b243cf49675e4c5c54a7a7259d --- gfx/thebes/gfxFontConstants.h | 1 + gfx/thebes/gfxFontFamilyList.h | 1 + gfx/thebes/gfxPlatformFontList.cpp | 69 ++++++++++++++++++++++++------ gfx/thebes/gfxPlatformFontList.h | 10 +++++ gfx/thebes/gfxTextRun.cpp | 23 +++++++--- gfx/thebes/gfxTextRun.h | 3 +- 6 files changed, 87 insertions(+), 20 deletions(-) diff --git a/gfx/thebes/gfxFontConstants.h b/gfx/thebes/gfxFontConstants.h index 7ffa5b0b3851..ee1a4db3ecab 100644 --- a/gfx/thebes/gfxFontConstants.h +++ b/gfx/thebes/gfxFontConstants.h @@ -184,6 +184,7 @@ enum eFontPrefLang { #undef FONT_PREF_LANG , eFontPrefLang_CJKSet // special code for CJK set + , eFontPrefLang_Emoji // special code for emoji presentation , eFontPrefLang_First = eFontPrefLang_Western , eFontPrefLang_Last = eFontPrefLang_Others , eFontPrefLang_Count = (eFontPrefLang_Last - eFontPrefLang_First + 1) diff --git a/gfx/thebes/gfxFontFamilyList.h b/gfx/thebes/gfxFontFamilyList.h index d661b5d68465..50db4a275f99 100644 --- a/gfx/thebes/gfxFontFamilyList.h +++ b/gfx/thebes/gfxFontFamilyList.h @@ -40,6 +40,7 @@ enum FontFamilyType : uint32_t { // special eFamily_moz_variable, eFamily_moz_fixed, + eFamily_moz_emoji, eFamily_generic_first = eFamily_serif, eFamily_generic_last = eFamily_fantasy, diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp index a4b3a7ca795e..bd75ac57151c 100644 --- a/gfx/thebes/gfxPlatformFontList.cpp +++ b/gfx/thebes/gfxPlatformFontList.cpp @@ -960,20 +960,9 @@ gfxPlatformFontList::ResolveGenericFontNames( nsAtom* langGroup = GetLangGroupForPrefLang(aPrefLang); NS_ASSERTION(langGroup, "null lang group for pref lang"); - // lookup and add platform fonts uniquely - for (const nsString& genericFamily : genericFamilies) { - gfxFontStyle style; - style.language = langGroup; - style.systemFont = false; - AutoTArray families; - FindAndAddFamilies(genericFamily, &families, FindFamiliesFlags(0), - &style); - for (gfxFontFamily* f : families) { - if (!aGenericFamilies->Contains(f)) { - aGenericFamilies->AppendElement(f); - } - } - } + gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies, + langGroup, + aGenericFamilies); #if 0 // dump out generic mappings printf("%s ===> ", prefFontName.get()); @@ -985,6 +974,43 @@ gfxPlatformFontList::ResolveGenericFontNames( #endif } +void +gfxPlatformFontList::ResolveEmojiFontNames( + nsTArray>* aGenericFamilies) +{ + // emoji preference has no lang name + AutoTArray genericFamilies; + + nsAutoCString prefFontListName("font.name-list.emoji"); + gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies); + + gfxPlatformFontList::GetFontFamiliesFromGenericFamilies(genericFamilies, + nullptr, + aGenericFamilies); +} + +void +gfxPlatformFontList::GetFontFamiliesFromGenericFamilies( + nsTArray& aGenericNameFamilies, + nsAtom* aLangGroup, + nsTArray>* aGenericFamilies) +{ + // lookup and add platform fonts uniquely + for (const nsString& genericFamily : aGenericNameFamilies) { + gfxFontStyle style; + style.language = aLangGroup; + style.systemFont = false; + AutoTArray families; + FindAndAddFamilies(genericFamily, &families, FindFamiliesFlags(0), + &style); + for (gfxFontFamily* f : families) { + if (!aGenericFamilies->Contains(f)) { + aGenericFamilies->AppendElement(f); + } + } + } +} + nsTArray>* gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType, eFontPrefLang aPrefLang) @@ -994,6 +1020,17 @@ gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType, aGenericType = eFamily_monospace; } + if (aGenericType == eFamily_moz_emoji) { + // Emoji font has no lang + PrefFontList* prefFonts = mEmojiPrefFont.get(); + if (MOZ_UNLIKELY(!prefFonts)) { + prefFonts = new PrefFontList; + ResolveEmojiFontNames(prefFonts); + mEmojiPrefFont.reset(prefFonts); + } + return prefFonts; + } + PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][aGenericType].get(); if (MOZ_UNLIKELY(!prefFonts)) { @@ -1298,6 +1335,10 @@ gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, mozilla::FontFamilyType gfxPlatformFontList::GetDefaultGeneric(eFontPrefLang aLang) { + if (aLang == eFontPrefLang_Emoji) { + return eFamily_moz_emoji; + } + // initialize lang group pref font defaults (i.e. serif/sans-serif) if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) { mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames)); diff --git a/gfx/thebes/gfxPlatformFontList.h b/gfx/thebes/gfxPlatformFontList.h index 9358ebd74440..415253990819 100644 --- a/gfx/thebes/gfxPlatformFontList.h +++ b/gfx/thebes/gfxPlatformFontList.h @@ -497,6 +497,15 @@ protected: eFontPrefLang aPrefLang, nsTArray>* aGenericFamilies); + void + ResolveEmojiFontNames(nsTArray>* aGenericFamilies); + + void + GetFontFamiliesFromGenericFamilies( + nsTArray& aGenericFamilies, + nsAtom* aLangGroup, + nsTArray>* aFontFamilies); + virtual nsresult InitFontListForPlatform() = 0; void ApplyWhitelist(); @@ -562,6 +571,7 @@ protected: mozilla::RangedArray mLangGroupPrefFonts; + mozilla::UniquePtr mEmojiPrefFont; // when system-wide font lookup fails for a character, cache it to skip future searches gfxSparseBitSet mCodepointsWithNoFonts; diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index 70927a803fdb..9a0202bf371f 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -37,6 +37,9 @@ using mozilla::services::GetObserverService; static const char16_t kEllipsisChar[] = { 0x2026, 0x0 }; static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 }; +const uint32_t kVariationSelector15 = 0xFE0E; // text presentation +const uint32_t kVariationSelector16 = 0xFE0F; // emoji presentation + #ifdef DEBUG_roc #define DEBUG_TEXT_RUN_STORAGE_METRICS #endif @@ -3056,7 +3059,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh, return nullptr; // 2. search pref fonts - gfxFont* font = WhichPrefFontSupportsChar(aCh); + gfxFont* font = WhichPrefFontSupportsChar(aCh, aNextCh); if (font) { *aMatchType = gfxTextRange::kPrefsFallback; return font; @@ -3337,12 +3340,22 @@ gfxFontGroup::ContainsUserFont(const gfxUserFontEntry* aUserFont) } gfxFont* -gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh) +gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh, uint32_t aNextCh) { - // get the pref font list if it hasn't been set up already - uint32_t unicodeRange = FindCharUnicodeRange(aCh); + eFontPrefLang charLang; gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); - eFontPrefLang charLang = pfl->GetFontPrefLangFor(unicodeRange); + + EmojiPresentation emoji = GetEmojiPresentation(aCh); + if ((emoji != EmojiPresentation::TextOnly && + (aNextCh == kVariationSelector16 || + (emoji == EmojiPresentation::EmojiDefault && + aNextCh != kVariationSelector15)))) { + charLang = eFontPrefLang_Emoji; + } else { + // get the pref font list if it hasn't been set up already + uint32_t unicodeRange = FindCharUnicodeRange(aCh); + charLang = pfl->GetFontPrefLangFor(unicodeRange); + } // if the last pref font was the first family in the pref list, no need to recheck through a list of families if (mLastPrefFont && charLang == mLastPrefLang && diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h index daa34f15f253..f0871fa3290a 100644 --- a/gfx/thebes/gfxTextRun.h +++ b/gfx/thebes/gfxTextRun.h @@ -1001,7 +1001,8 @@ public: protected: // search through pref fonts for a character, return nullptr if no matching pref font - gfxFont* WhichPrefFontSupportsChar(uint32_t aCh); + gfxFont* WhichPrefFontSupportsChar(uint32_t aCh, + uint32_t aNextCh); gfxFont* WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh, Script aRunScript); From 9fa1068dcc7de8e9447cd30d4380a574847422b4 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Sun, 12 Nov 2017 16:30:23 +0900 Subject: [PATCH 151/219] Bug 1032671 - Part 3. Set font.name-list.emoji for some platforms. r=jfkthame - Segoe UI Emoji for Windows 8+ - EmojiOne Mozilla for Windows 7 - Apple Color Emoji for OSX - EmojiOne Mozilla for GTK(Linux). fontconfig doesn't support emoji as family name. - Noto Color Emoji for Android MozReview-Commit-ID: GOkOFRujk93 --HG-- extra : rebase_source : e8bae62d555440d9881f111fb26393d59fd15ba4 --- modules/libpref/init/all.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 56f4e13c30f1..3124b347964b 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3639,6 +3639,8 @@ pref("ui.mouse.radius.inputSource.touchOnly", true); #ifdef XP_WIN +pref("font.name-list.emoji", "Segoe UI Emoji, EmojiOne Mozilla"); + pref("font.name-list.serif.ar", "Times New Roman"); pref("font.name-list.sans-serif.ar", "Segoe UI, Tahoma, Arial"); pref("font.name-list.monospace.ar", "Courier New"); @@ -3995,6 +3997,8 @@ pref("ui.key.saveLink.shift", false); // true = shift, false = meta // enable NSPR logging for module fontInfoLog:5 // canonical names immediately follow '(fontinit) family:' in the log +pref("font.name-list.emoji", "Apple Color Emoji"); + pref("font.name-list.serif.ar", "Al Bayan"); pref("font.name-list.sans-serif.ar", "Geeza Pro"); pref("font.name-list.monospace.ar", "Geeza Pro"); @@ -4286,6 +4290,10 @@ pref("print.print_in_color", true); // font names +// fontconfig doesn't support emoji yet +// https://lists.freedesktop.org/archives/fontconfig/2016-October/005842.html +pref("font.name-list.emoji", "EmojiOne Mozilla"); + pref("font.name-list.serif.ar", "serif"); pref("font.name-list.sans-serif.ar", "sans-serif"); pref("font.name-list.monospace.ar", "monospace"); @@ -4461,6 +4469,8 @@ pref("font.size.fixed.x-western", 12); #if defined(ANDROID) // We use the bundled fonts for Firefox for Android +pref("font.name-list.emoji", "Noto Color Emoji"); + pref("font.name-list.serif.ar", "Noto Naskh Arabic, Noto Serif, Droid Serif"); pref("font.name-list.sans-serif.ar", "Noto Naskh Arabic, Clear Sans, Roboto, Droid Sans"); pref("font.name-list.monospace.ar", "Noto Naskh Arabic"); From 153c591749d7807372a4ba99e2685b5461d36cf7 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Sun, 12 Nov 2017 16:36:24 +0900 Subject: [PATCH 152/219] Bug 1032671 - Part 4. Font fallback should detect emoji range for color emoji font. r=jfkthame MozReview-Commit-ID: AeqSLUU9GRH --HG-- extra : rebase_source : 3a3d0c70072b990d83b1304eb527f30307f6e944 --- gfx/thebes/gfxAndroidPlatform.cpp | 22 ++++++++--------- gfx/thebes/gfxPlatformGtk.cpp | 20 +++++++--------- gfx/thebes/gfxPlatformMac.cpp | 23 ++++++++++-------- gfx/thebes/gfxTextRun.cpp | 3 --- gfx/thebes/gfxWindowsPlatform.cpp | 26 ++++++++++----------- intl/unicharutil/util/nsUnicodeProperties.h | 3 +++ 6 files changed, 47 insertions(+), 50 deletions(-) diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index 402e22d72c2b..520f397b4651 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -22,6 +22,7 @@ #include "nsIScreen.h" #include "nsIScreenManager.h" #include "nsServiceManagerUtils.h" +#include "nsUnicodeProperties.h" #include "gfxPrefs.h" #include "cairo.h" #include "VsyncSource.h" @@ -35,6 +36,7 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gfx; +using namespace mozilla::unicode; using mozilla::intl::LocaleService; using mozilla::intl::OSPreferences; @@ -166,19 +168,17 @@ gfxAndroidPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, static const char kNotoSansCJKJP[] = "Noto Sans CJK JP"; static const char kNotoColorEmoji[] = "Noto Color Emoji"; - if (aNextCh == 0xfe0fu) { - // if char is followed by VS16, try for a color emoji glyph - aFontList.AppendElement(kNotoColorEmoji); + EmojiPresentation emoji = GetEmojiPresentation(aCh); + if (emoji != EmojiPresentation::TextOnly) { + if (aNextCh == kVariationSelector16 || + (aNextCh != kVariationSelector15 && + emoji == EmojiPresentation::EmojiDefault)) { + // if char is followed by VS16, try for a color emoji glyph + aFontList.AppendElement(kNotoColorEmoji); + } } - if (!IS_IN_BMP(aCh)) { - uint32_t p = aCh >> 16; - if (p == 1) { // try color emoji font, unless VS15 (text style) present - if (aNextCh != 0xfe0fu && aNextCh != 0xfe0eu) { - aFontList.AppendElement(kNotoColorEmoji); - } - } - } else { + if (IS_IN_BMP(aCh)) { // try language-specific "Droid Sans *" and "Noto Sans *" fonts for // certain blocks, as most devices probably have these uint8_t block = (aCh >> 8) & 0xff; diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp index 48195a785306..22d82b497835 100644 --- a/gfx/thebes/gfxPlatformGtk.cpp +++ b/gfx/thebes/gfxPlatformGtk.cpp @@ -217,9 +217,14 @@ gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, Script aRunScript, nsTArray& aFontList) { - if (aNextCh == 0xfe0fu) { - // if char is followed by VS16, try for a color emoji glyph - aFontList.AppendElement(kFontEmojiOneMozilla); + EmojiPresentation emoji = GetEmojiPresentation(aCh); + if (emoji != EmojiPresentation::TextOnly) { + if (aNextCh == kVariationSelector16 || + (aNextCh != kVariationSelector15 && + emoji == EmojiPresentation::EmojiDefault)) { + // if char is followed by VS16, try for a color emoji glyph + aFontList.AppendElement(kFontEmojiOneMozilla); + } } aFontList.AppendElement(kFontDejaVuSerif); @@ -227,15 +232,6 @@ gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, aFontList.AppendElement(kFontDejaVuSans); aFontList.AppendElement(kFontFreeSans); - if (!IS_IN_BMP(aCh)) { - uint32_t p = aCh >> 16; - if (p == 1) { // try color emoji font, unless VS15 (text style) present - if (aNextCh != 0xfe0fu && aNextCh != 0xfe0eu) { - aFontList.AppendElement(kFontEmojiOneMozilla); - } - } - } - // add fonts for CJK ranges // xxx - this isn't really correct, should use the same CJK font ordering // as the pref font code diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp index b3af8c3329d4..4bcbc56da091 100644 --- a/gfx/thebes/gfxPlatformMac.cpp +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -18,6 +18,7 @@ #include "nsTArray.h" #include "mozilla/Preferences.h" #include "mozilla/VsyncDispatcher.h" +#include "nsUnicodeProperties.h" #include "qcms.h" #include "gfx2DGlue.h" @@ -29,6 +30,7 @@ using namespace mozilla; using namespace mozilla::gfx; +using namespace mozilla::unicode; using mozilla::dom::SystemFontListEntry; @@ -191,23 +193,24 @@ gfxPlatformMac::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, Script aRunScript, nsTArray& aFontList) { - if (aNextCh == 0xfe0f) { - aFontList.AppendElement(kFontAppleColorEmoji); + EmojiPresentation emoji = GetEmojiPresentation(aCh); + if (emoji != EmojiPresentation::TextOnly) { + if (aNextCh == kVariationSelector16 || + (aNextCh != kVariationSelector15 && + emoji == EmojiPresentation::EmojiDefault)) { + // if char is followed by VS16, try for a color emoji glyph + aFontList.AppendElement(kFontAppleColorEmoji); + } } aFontList.AppendElement(kFontLucidaGrande); if (!IS_IN_BMP(aCh)) { uint32_t p = aCh >> 16; - uint32_t b = aCh >> 8; if (p == 1) { - if (b >= 0x1f0 && b < 0x1f7) { - aFontList.AppendElement(kFontAppleColorEmoji); - } else { - aFontList.AppendElement(kFontAppleSymbols); - aFontList.AppendElement(kFontSTIXGeneral); - aFontList.AppendElement(kFontGeneva); - } + aFontList.AppendElement(kFontAppleSymbols); + aFontList.AppendElement(kFontSTIXGeneral); + aFontList.AppendElement(kFontGeneva); } else if (p == 2) { // OSX installations with MS Office may have these fonts aFontList.AppendElement(kFontMingLiUExtB); diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index 9a0202bf371f..96e9dc077f35 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -37,9 +37,6 @@ using mozilla::services::GetObserverService; static const char16_t kEllipsisChar[] = { 0x2026, 0x0 }; static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 }; -const uint32_t kVariationSelector15 = 0xFE0E; // text presentation -const uint32_t kVariationSelector16 = 0xFE0F; // emoji presentation - #ifdef DEBUG_roc #define DEBUG_TEXT_RUN_STORAGE_METRICS #endif diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 2077ee47b3b5..0b54917aa4b0 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -14,6 +14,7 @@ #include "gfxWindowsSurface.h" #include "nsUnicharUtils.h" +#include "nsUnicodeProperties.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" @@ -84,6 +85,7 @@ using namespace mozilla::gfx; using namespace mozilla::layers; using namespace mozilla::widget; using namespace mozilla::image; +using namespace mozilla::unicode; DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget) { @@ -603,9 +605,15 @@ gfxWindowsPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, Script aRunScript, nsTArray& aFontList) { - if (aNextCh == 0xfe0fu) { - aFontList.AppendElement(kFontSegoeUIEmoji); - aFontList.AppendElement(kFontEmojiOneMozilla); + EmojiPresentation emoji = GetEmojiPresentation(aCh); + if (emoji != EmojiPresentation::TextOnly) { + if (aNextCh == kVariationSelector16 || + (aNextCh != kVariationSelector15 && + emoji == EmojiPresentation::EmojiDefault)) { + // if char is followed by VS16, try for a color emoji glyph + aFontList.AppendElement(kFontSegoeUIEmoji); + aFontList.AppendElement(kFontEmojiOneMozilla); + } } // Arial is used as the default fallback for system fallback @@ -614,17 +622,7 @@ gfxWindowsPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, if (!IS_IN_BMP(aCh)) { uint32_t p = aCh >> 16; if (p == 1) { // SMP plane - if (aNextCh == 0xfe0eu) { - aFontList.AppendElement(kFontSegoeUISymbol); - aFontList.AppendElement(kFontSegoeUIEmoji); - aFontList.AppendElement(kFontEmojiOneMozilla); - } else { - if (aNextCh != 0xfe0fu) { - aFontList.AppendElement(kFontSegoeUIEmoji); - aFontList.AppendElement(kFontEmojiOneMozilla); - } - aFontList.AppendElement(kFontSegoeUISymbol); - } + aFontList.AppendElement(kFontSegoeUISymbol); aFontList.AppendElement(kFontEbrima); aFontList.AppendElement(kFontNirmalaUI); aFontList.AppendElement(kFontCambriaMath); diff --git a/intl/unicharutil/util/nsUnicodeProperties.h b/intl/unicharutil/util/nsUnicodeProperties.h index c16e67521ad0..e2ee2a99bfe4 100644 --- a/intl/unicharutil/util/nsUnicodeProperties.h +++ b/intl/unicharutil/util/nsUnicodeProperties.h @@ -52,6 +52,9 @@ enum EmojiPresentation { EmojiDefault = 2 }; +const uint32_t kVariationSelector15 = 0xFE0E; // text presentation +const uint32_t kVariationSelector16 = 0xFE0F; // emoji presentation + extern const hb_unicode_general_category_t sICUtoHBcategory[]; inline uint32_t From ded9cb4ade6f15673a12c952923832a845ff01c8 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Sat, 18 Nov 2017 20:15:05 +0900 Subject: [PATCH 153/219] Bug 1032671 - Part 5. Add reftest for emoji. r=jfkthame Windows 7 doesn't have mono symbol font for emoji code mapping. And fontconfig doesn't have better option for this. So I except to these platforms. MozReview-Commit-ID: 7TB1guyzBVS --HG-- extra : rebase_source : 4a014a26618471cf8bee635bae961321c98a162e --- .../font-matching/emoji-fallback-1-ref.html | 18 ++++++++++++++++++ .../font-matching/emoji-fallback-1.html | 17 +++++++++++++++++ .../font-matching/emoji-fallback-2-ref.html | 18 ++++++++++++++++++ .../font-matching/emoji-fallback-2.html | 17 +++++++++++++++++ .../font-matching/emoji-fallback-3-ref.html | 18 ++++++++++++++++++ .../font-matching/emoji-fallback-3.html | 17 +++++++++++++++++ layout/reftests/font-matching/reftest.list | 6 ++++++ 7 files changed, 111 insertions(+) create mode 100644 layout/reftests/font-matching/emoji-fallback-1-ref.html create mode 100644 layout/reftests/font-matching/emoji-fallback-1.html create mode 100644 layout/reftests/font-matching/emoji-fallback-2-ref.html create mode 100644 layout/reftests/font-matching/emoji-fallback-2.html create mode 100644 layout/reftests/font-matching/emoji-fallback-3-ref.html create mode 100644 layout/reftests/font-matching/emoji-fallback-3.html diff --git a/layout/reftests/font-matching/emoji-fallback-1-ref.html b/layout/reftests/font-matching/emoji-fallback-1-ref.html new file mode 100644 index 000000000000..0acad534a127 --- /dev/null +++ b/layout/reftests/font-matching/emoji-fallback-1-ref.html @@ -0,0 +1,18 @@ + + + + +emoji fallback + + + + +
⌚⌛🌀🌁
+ + + diff --git a/layout/reftests/font-matching/emoji-fallback-1.html b/layout/reftests/font-matching/emoji-fallback-1.html new file mode 100644 index 000000000000..fc0a479950e0 --- /dev/null +++ b/layout/reftests/font-matching/emoji-fallback-1.html @@ -0,0 +1,17 @@ + + + + +emoji fallback + + + + +
⌚⌛🌀🌁
+ + + diff --git a/layout/reftests/font-matching/emoji-fallback-2-ref.html b/layout/reftests/font-matching/emoji-fallback-2-ref.html new file mode 100644 index 000000000000..2922dd28cc4c --- /dev/null +++ b/layout/reftests/font-matching/emoji-fallback-2-ref.html @@ -0,0 +1,18 @@ + + + + +emoji fallback to text font + + + + +
⌚⌛🌀🌁
+ + + diff --git a/layout/reftests/font-matching/emoji-fallback-2.html b/layout/reftests/font-matching/emoji-fallback-2.html new file mode 100644 index 000000000000..d136ff7d67af --- /dev/null +++ b/layout/reftests/font-matching/emoji-fallback-2.html @@ -0,0 +1,17 @@ + + + + +emoji fallback to text font + + + + +
⌚︎⌛︎🌀︎🌁︎
+ + + diff --git a/layout/reftests/font-matching/emoji-fallback-3-ref.html b/layout/reftests/font-matching/emoji-fallback-3-ref.html new file mode 100644 index 000000000000..627a0ea7f001 --- /dev/null +++ b/layout/reftests/font-matching/emoji-fallback-3-ref.html @@ -0,0 +1,18 @@ + + + + +emoji fallback to color font + + + + +
🅰🅱
+ + + diff --git a/layout/reftests/font-matching/emoji-fallback-3.html b/layout/reftests/font-matching/emoji-fallback-3.html new file mode 100644 index 000000000000..57046b555641 --- /dev/null +++ b/layout/reftests/font-matching/emoji-fallback-3.html @@ -0,0 +1,17 @@ + + + + +emoji fallback to color font + + + + +
🅰️🅱️
+ + + diff --git a/layout/reftests/font-matching/reftest.list b/layout/reftests/font-matching/reftest.list index 3e92f6276449..89dbb44fc2cc 100644 --- a/layout/reftests/font-matching/reftest.list +++ b/layout/reftests/font-matching/reftest.list @@ -121,6 +121,12 @@ skip-if(!cocoaWidget) != apple-symbols-1.html apple-symbols-1-notref.html == italic-oblique-9.html italic-oblique-ref.html != italic-oblique-kinnari.html italic-oblique-kinnari-ref.html +# GTK and Windows 7 don't have full emoji and symbol font, so emoji-fallback-2 +# don't work well. +== emoji-fallback-1.html emoji-fallback-1-ref.html +skip-if(gtkWidget||/^Windows\x20NT\x206\.1/.test(http.oscpu)) == emoji-fallback-2.html emoji-fallback-2-ref.html +== emoji-fallback-3.html emoji-fallback-3-ref.html + # system font generic per-language tests, only works under OSX currently # Bug 1212731 - initial implementation caused startup regression and # regression with full-width digits display in UI elements. Disable From 8f329d4896dd509d95f5a1edd5329b2cfa20419d Mon Sep 17 00:00:00 2001 From: Russell Date: Thu, 23 Nov 2017 09:44:04 -0800 Subject: [PATCH 154/219] Bug 1419405 - remove Immutable usage in filters reducer; r=nchevobbe MozReview-Commit-ID: GfxqmE3nGjs --HG-- extra : rebase_source : e6a2dc059d6f5cd487b619c66a86db3ed34c633d --- .../new-console-output/actions/filters.js | 4 +-- .../new-console-output/reducers/filters.js | 29 +++++++++++-------- .../new-console-output/reducers/messages.js | 10 +++---- .../webconsole/new-console-output/store.js | 2 +- .../test/components/filter-bar.test.js | 8 ++--- .../test/store/filters.test.js | 12 +++----- 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/devtools/client/webconsole/new-console-output/actions/filters.js b/devtools/client/webconsole/new-console-output/actions/filters.js index 552146c46965..bbcc9bdf39f5 100644 --- a/devtools/client/webconsole/new-console-output/actions/filters.js +++ b/devtools/client/webconsole/new-console-output/actions/filters.js @@ -34,7 +34,7 @@ function filterToggle(filter) { }); const filterState = getAllFilters(getState()); Services.prefs.setBoolPref(PREFS.FILTER[filter.toUpperCase()], - filterState.get(filter)); + filterState[filter]); }; } @@ -44,7 +44,7 @@ function filtersClear() { type: FILTERS_CLEAR, }); - const filterState = getAllFilters(getState()).toJS(); + const filterState = getAllFilters(getState()); for (let filter in filterState) { if (filter !== FILTERS.TEXT) { Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]); diff --git a/devtools/client/webconsole/new-console-output/reducers/filters.js b/devtools/client/webconsole/new-console-output/reducers/filters.js index bd53aea6708e..be1ce9312712 100644 --- a/devtools/client/webconsole/new-console-output/reducers/filters.js +++ b/devtools/client/webconsole/new-console-output/reducers/filters.js @@ -5,32 +5,37 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -const Immutable = require("devtools/client/shared/vendor/immutable"); const constants = require("devtools/client/webconsole/new-console-output/constants"); -const FilterState = Immutable.Record(constants.DEFAULT_FILTERS_VALUES); +const FilterState = (overrides) => Object.freeze( + cloneState(constants.DEFAULT_FILTERS_VALUES, overrides) +); -function filters(state = new FilterState(), action) { +function filters(state = FilterState(), action) { switch (action.type) { case constants.FILTER_TOGGLE: const {filter} = action; - const active = !state.get(filter); - return state.set(filter, active); + const active = !state[filter]; + return cloneState(state, {[filter]: active}); case constants.FILTERS_CLEAR: - return new FilterState(); + return FilterState(); case constants.DEFAULT_FILTERS_RESET: - return state.withMutations(record => { - constants.DEFAULT_FILTERS.forEach(filterName => { - record.set(filterName, constants.DEFAULT_FILTERS_VALUES[filterName]); - }); + const newState = cloneState(state); + constants.DEFAULT_FILTERS.forEach(filterName => { + newState[filterName] = constants.DEFAULT_FILTERS_VALUES[filterName]; }); + return newState; case constants.FILTER_TEXT_SET: - let {text} = action; - return state.set(constants.FILTERS.TEXT, text); + const {text} = action; + return cloneState(state, {[constants.FILTERS.TEXT]: text}); } return state; } +function cloneState(state, overrides) { + return Object.assign({}, state, overrides); +} + exports.FilterState = FilterState; exports.filters = filters; diff --git a/devtools/client/webconsole/new-console-output/reducers/messages.js b/devtools/client/webconsole/new-console-output/reducers/messages.js index 3cece897d582..67db9f7c40ab 100644 --- a/devtools/client/webconsole/new-console-output/reducers/messages.js +++ b/devtools/client/webconsole/new-console-output/reducers/messages.js @@ -602,7 +602,7 @@ function passNetworkFilter(message, filters) { return ( message.source !== MESSAGE_SOURCE.NETWORK || message.isXHR === true || - filters.get(FILTERS.NET) === true + filters[FILTERS.NET] === true ); } @@ -620,7 +620,7 @@ function passXhrFilter(message, filters) { return ( message.source !== MESSAGE_SOURCE.NETWORK || message.isXHR === false || - filters.get(FILTERS.NETXHR) === true + filters[FILTERS.NETXHR] === true ); } @@ -637,7 +637,7 @@ function passLevelFilters(message, filters) { return ( (message.source !== MESSAGE_SOURCE.CONSOLE_API && message.source !== MESSAGE_SOURCE.JAVASCRIPT) || - filters.get(message.level) === true + filters[message.level] === true ); } @@ -653,7 +653,7 @@ function passCssFilters(message, filters) { // or if the CSS filter is on. return ( message.source !== MESSAGE_SOURCE.CSS || - filters.get("css") === true + filters.css === true ); } @@ -665,7 +665,7 @@ function passCssFilters(message, filters) { * @returns {Boolean} */ function passSearchFilters(message, filters) { - let text = (filters.get("text") || "").trim(); + let text = (filters.text || "").trim(); // If there is no search, the message passes the filter. if (!text) { diff --git a/devtools/client/webconsole/new-console-output/store.js b/devtools/client/webconsole/new-console-output/store.js index a28ea3dbbc69..34025ddb4deb 100644 --- a/devtools/client/webconsole/new-console-output/store.js +++ b/devtools/client/webconsole/new-console-output/store.js @@ -47,7 +47,7 @@ function configureStore(hud, options = {}) { const initialState = { prefs: PrefState({ logLimit, sidebarToggle }), - filters: new FilterState({ + filters: FilterState({ error: Services.prefs.getBoolPref(PREFS.FILTER.ERROR), warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN), info: Services.prefs.getBoolPref(PREFS.FILTER.INFO), diff --git a/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js b/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js index c7cdea240aaa..242b33a2102f 100644 --- a/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js +++ b/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js @@ -90,7 +90,7 @@ describe("FilterBar component:", () => { // Toolbar is now hidden const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages"); expect(toolbar.exists()).toBeFalsy(); - expect(getAllFilters(store.getState()).get(FILTERS.LOG)).toBeTruthy(); + expect(getAllFilters(store.getState())[FILTERS.LOG]).toBeTruthy(); }); it("displays the number of hidden messages when a search hide messages", () => { @@ -185,9 +185,9 @@ describe("FilterBar component:", () => { // Let's make sure those non-default filters are off. const filters = getAllFilters(store.getState()); - expect(filters.get(FILTERS.CSS)).toBe(false); - expect(filters.get(FILTERS.NET)).toBe(false); - expect(filters.get(FILTERS.NETXHR)).toBe(false); + expect(filters[FILTERS.CSS]).toBe(false); + expect(filters[FILTERS.NET]).toBe(false); + expect(filters[FILTERS.NETXHR]).toBe(false); const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages"); expect(toolbar.exists()).toBeFalsy(); diff --git a/devtools/client/webconsole/new-console-output/test/store/filters.test.js b/devtools/client/webconsole/new-console-output/test/store/filters.test.js index f7b81ca033e8..5a8f2c76eb1a 100644 --- a/devtools/client/webconsole/new-console-output/test/store/filters.test.js +++ b/devtools/client/webconsole/new-console-output/test/store/filters.test.js @@ -213,8 +213,7 @@ describe("Clear filters", () => { store.dispatch(actions.filterToggle(FILTERS.NETXHR)); store.dispatch(actions.filterTextSet("foobar")); - let filters = getAllFilters(store.getState()); - expect(filters.toJS()).toEqual({ + expect(getAllFilters(store.getState())).toEqual({ // default [FILTERS.WARN]: true, [FILTERS.LOG]: true, @@ -240,8 +239,7 @@ describe("Clear filters", () => { store.dispatch(actions.filtersClear()); - filters = getAllFilters(store.getState()); - expect(filters.toJS()).toEqual({ + expect(getAllFilters(store.getState())).toEqual({ [FILTERS.CSS]: false, [FILTERS.DEBUG]: true, [FILTERS.ERROR]: true, @@ -278,8 +276,7 @@ describe("Resets filters", () => { store.dispatch(actions.filterToggle(FILTERS.NETXHR)); store.dispatch(actions.filterTextSet("foobar")); - let filters = getAllFilters(store.getState()); - expect(filters.toJS()).toEqual({ + expect(getAllFilters(store.getState())).toEqual({ // default [FILTERS.WARN]: true, [FILTERS.INFO]: true, @@ -306,8 +303,7 @@ describe("Resets filters", () => { store.dispatch(actions.defaultFiltersReset()); - filters = getAllFilters(store.getState()); - expect(filters.toJS()).toEqual({ + expect(getAllFilters(store.getState())).toEqual({ // default [FILTERS.ERROR]: true, [FILTERS.WARN]: true, From 98bddd976d76846671f6d9d8774dd22bf98bca01 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Mon, 4 Dec 2017 14:57:53 +0800 Subject: [PATCH 155/219] Bug 1422676 - Fix small CSS error in test_2d.fill.pattern.imageSmoothingEnabled.html. r=TYLin MozReview-Commit-ID: JEBm4hNrnfw --HG-- extra : rebase_source : a6722cd4add5d3a55f5da8f8f12ea779d8c2d628 --- dom/canvas/test/test_2d.fill.pattern.imageSmoothingEnabled.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/dom/canvas/test/test_2d.fill.pattern.imageSmoothingEnabled.html b/dom/canvas/test/test_2d.fill.pattern.imageSmoothingEnabled.html index bdadf5ebf0bd..cf50ad4a93b0 100644 --- a/dom/canvas/test/test_2d.fill.pattern.imageSmoothingEnabled.html +++ b/dom/canvas/test/test_2d.fill.pattern.imageSmoothingEnabled.html @@ -45,7 +45,6 @@ img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAA img.onload = function () { ctx.imageSmoothingEnabled = false; ctx.save(); - ctx.fillStyle = "rgb(127, 127, 127);"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.scale(16, 16); ctx.fillStyle = ctx.createPattern(img, 'no-repeat'); @@ -60,7 +59,6 @@ img.onload = function () { ctx.imageSmoothingEnabled = true; ctx.save(); - ctx.fillStyle = "rgb(127, 127, 127);"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.scale(16, 16); ctx.fillStyle = ctx.createPattern(img, 'no-repeat'); From 92398b50fb704aa26a128c6829d06b657cc3913e Mon Sep 17 00:00:00 2001 From: Nevin Chen Date: Thu, 23 Nov 2017 17:07:59 +0800 Subject: [PATCH 156/219] Bug 1419245 - A2HS badge is shown on site with broken HTTPS. r=walkingice MozReview-Commit-ID: BFWdqTcxOi9 --HG-- extra : rebase_source : d891304d2e484e178309dd9fcfa873266cd55f59 --- .../org/mozilla/gecko/GeckoApplication.java | 15 ++++++--- .../base/java/org/mozilla/gecko/Tab.java | 12 +++---- .../java/org/mozilla/gecko/pwa/PwaUtils.java | 31 +++++++++++++++++++ .../gecko/toolbar/PageActionLayout.java | 5 ++- mobile/android/base/moz.build | 1 + 5 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 mobile/android/base/java/org/mozilla/gecko/pwa/PwaUtils.java diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java index ddcf1a6af56f..84e684fed6bf 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java @@ -12,7 +12,6 @@ import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.net.Uri; import android.os.Environment; import android.os.Process; @@ -39,12 +38,11 @@ import org.mozilla.gecko.icons.Icons; import org.mozilla.gecko.lwt.LightweightTheme; import org.mozilla.gecko.mdns.MulticastDNSManager; import org.mozilla.gecko.media.AudioFocusAgent; -import org.mozilla.gecko.media.RemoteManager; import org.mozilla.gecko.notifications.NotificationClient; import org.mozilla.gecko.notifications.NotificationHelper; import org.mozilla.gecko.permissions.Permissions; import org.mozilla.gecko.preferences.DistroSharedPrefsImport; -import org.mozilla.gecko.util.ActivityUtils; +import org.mozilla.gecko.pwa.PwaUtils; import org.mozilla.gecko.telemetry.TelemetryBackgroundReceiver; import org.mozilla.gecko.util.ActivityResultHandler; import org.mozilla.gecko.util.BundleEventListener; @@ -54,7 +52,6 @@ import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.PRNGFixes; import org.mozilla.gecko.util.ShortcutUtils; import org.mozilla.gecko.util.ThreadUtils; -import org.mozilla.gecko.util.UIAsyncTask; import java.io.ByteArrayOutputStream; import java.io.File; @@ -569,7 +566,15 @@ public class GeckoApplication extends Application final String manifestUrl = selectedTab.getManifestUrl(); if (manifestUrl != null) { - // If a page has associated manifest, lets install it + // If a page has associated manifest, lets install it (PWA A2HS) + // At this time, this page must be a secure page. + // Please hide PWA badge UI in front end side. + // Otherwise we'll throw an exception here. + final boolean safeForPwa = PwaUtils.shouldAddPwaShortcut(selectedTab); + if (!safeForPwa) { + throw new IllegalStateException("This page is not safe for PWA"); + } + final GeckoBundle message = new GeckoBundle(); message.putInt("iconSize", GeckoAppShell.getPreferredIconSize()); message.putString("manifestUrl", manifestUrl); diff --git a/mobile/android/base/java/org/mozilla/gecko/Tab.java b/mobile/android/base/java/org/mozilla/gecko/Tab.java index bace159acdeb..dd8d1c24ddc1 100644 --- a/mobile/android/base/java/org/mozilla/gecko/Tab.java +++ b/mobile/android/base/java/org/mozilla/gecko/Tab.java @@ -5,7 +5,6 @@ package org.mozilla.gecko; -import java.util.ArrayList; import java.util.Map; import java.util.concurrent.Future; import java.util.regex.Pattern; @@ -20,10 +19,10 @@ import org.mozilla.gecko.icons.IconDescriptor; import org.mozilla.gecko.icons.IconRequestBuilder; import org.mozilla.gecko.icons.IconResponse; import org.mozilla.gecko.icons.Icons; +import org.mozilla.gecko.pwa.PwaUtils; import org.mozilla.gecko.reader.ReaderModeUtils; import org.mozilla.gecko.reader.ReadingListHelper; import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState; -import org.mozilla.gecko.toolbar.PageActionLayout; import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.ShortcutUtils; import org.mozilla.gecko.util.ThreadUtils; @@ -38,7 +37,6 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; -import android.view.View; import static org.mozilla.gecko.toolbar.PageActionLayout.PageAction.UUID_PAGE_ACTION_PWA; @@ -486,7 +484,7 @@ public class Tab { } if (mManifestUrl != null) { - showPwaPageAction(); + showPwaBadge(); } else { clearPwaPageAction(); @@ -868,14 +866,16 @@ public class Tab { return mShouldShowToolbarWithoutAnimationOnFirstSelection; } + private void clearPwaPageAction() { GeckoBundle bundle = new GeckoBundle(); bundle.putString("id", UUID_PAGE_ACTION_PWA); EventDispatcher.getInstance().dispatch("PageActions:Remove", bundle); } - private void showPwaPageAction() { - if (!isPrivate()) { + + private void showPwaBadge() { + if (PwaUtils.shouldAddPwaShortcut(this)) { GeckoBundle bundle = new GeckoBundle(); bundle.putString("id", UUID_PAGE_ACTION_PWA); bundle.putString("title", mAppContext.getString(R.string.pwa_add_to_launcher_badge)); diff --git a/mobile/android/base/java/org/mozilla/gecko/pwa/PwaUtils.java b/mobile/android/base/java/org/mozilla/gecko/pwa/PwaUtils.java new file mode 100644 index 000000000000..d12630d505ab --- /dev/null +++ b/mobile/android/base/java/org/mozilla/gecko/pwa/PwaUtils.java @@ -0,0 +1,31 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +package org.mozilla.gecko.pwa; + +import android.support.annotation.CheckResult; + +import org.mozilla.gecko.Tab; + + +public class PwaUtils { + /** + * Check if this tab should add as PWA shortcut + * From the document :https://developers.google.com/web/fundamentals/security/encrypt-in-transit/why-https + * PWA must be served from a secure origin. We also restrict it to + * 1. Not allow user accept exception + * 2. The tab can't be a private tab. + * + * @param tab Since website info is in the tab, we pass the tab here. + * @return true if the tab shouldAddPwaShortcut + */ + @CheckResult + public static boolean shouldAddPwaShortcut(Tab tab) { + final boolean secure = tab.getSiteIdentity().isSecure(); + // This tab is safe for pwa only when the site is absolutely secure. + // so no exception is allowed + final boolean exception = tab.getSiteIdentity().isSecurityException(); + return !tab.isPrivate() && secure && !exception; + } +} diff --git a/mobile/android/base/java/org/mozilla/gecko/toolbar/PageActionLayout.java b/mobile/android/base/java/org/mozilla/gecko/toolbar/PageActionLayout.java index 2b73697319a6..f189664ca384 100644 --- a/mobile/android/base/java/org/mozilla/gecko/toolbar/PageActionLayout.java +++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/PageActionLayout.java @@ -11,6 +11,9 @@ import org.mozilla.gecko.R; import org.mozilla.gecko.Tab; import org.mozilla.gecko.Tabs; import org.mozilla.gecko.preferences.GeckoPreferences; +import org.mozilla.gecko.pwa.PwaUtils; +import org.mozilla.gecko.util.DrawableUtil; +import org.mozilla.gecko.util.ResourceDrawableUtils; import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.DrawableUtil; import org.mozilla.gecko.util.EventCallback; @@ -164,7 +167,7 @@ public class PageActionLayout extends ThemedLinearLayout implements BundleEventL private void maybeShowPwaOnboarding(String id) { // only show pwa at normal mode final Tab selectedTab = Tabs.getInstance().getSelectedTab(); - if (selectedTab.isPrivate()) { + if (!PwaUtils.shouldAddPwaShortcut(selectedTab)) { return; } if (UUID_PAGE_ACTION_PWA.equals(id)) { diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 2e94fce755d2..5060a038a44b 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -833,6 +833,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [ 'prompts/PromptListItem.java', 'prompts/PromptService.java', 'prompts/TabInput.java', + 'pwa/PwaUtils.java', 'reader/ReaderModeUtils.java', 'reader/ReadingListHelper.java', 'reader/SavedReaderViewHelper.java', From 9d786faef50d7d109e095765731ee849abfb8aed Mon Sep 17 00:00:00 2001 From: Nevin Chen Date: Mon, 4 Dec 2017 16:07:57 +0800 Subject: [PATCH 157/219] Bug 1421916 - Refine/update PWA action icon r=maliu MozReview-Commit-ID: 7gndnFFHezm --HG-- extra : rebase_source : eb804561083aaf2c210523fe346e80712666e5d1 --- .../res/drawable-xhdpi/add_to_homescreen.png | Bin 494 -> 610 bytes .../res/drawable-xxhdpi/add_to_homescreen.png | Bin 707 -> 871 bytes .../res/drawable-xxxhdpi/add_to_homescreen.png | Bin 667 -> 1091 bytes .../photon/res/drawable/add_to_homescreen.png | Bin 561 -> 423 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/mobile/android/app/src/photon/res/drawable-xhdpi/add_to_homescreen.png b/mobile/android/app/src/photon/res/drawable-xhdpi/add_to_homescreen.png index e3a0f110d930b312c0191d15b07cb520ee9ac353..a902ec3925f6f8721908223a2649f58af78c3d36 100644 GIT binary patch delta 586 zcmV-Q0=50_1L6dbBYy%ZNkl>zOH+i_#h+MvSQ7d0w$;nq6$$Zl6`=VYo^% zncS90B)GA-^I0E0%8F55Sw>b$NMtHT(j|5PI{h z05lNTFf&8JgMXL-9%X>&BLlopJ#CPQN~Y;{?FEajN%(X_}Md zd!Pf7PhsObOc(!tRO$e*+zq+}-Iay>ufOXuY`+Ti*8%+Vp4b7shxQi~6f`TR4-&vp zvEGMnX#q6h7hsSd0Mt*26+i^8@4BXGHkbA9Jzy9{YkzsW>$>$tee3}KZ{i$44xn@b zAphB~x-a5eD;oiZApaGnljLpu0p!R5lvM!iWU1UHPN&mV!vJtySJfsS?DwdQR2-E- zVT@{GzY7%rJ^c0!6=wJdI|R*z=J=IqnhP*Jsp-+o}h7cG9 Y09olNN;Ai>S^xk507*qoM6N<$f@sPusy=c zU{ElyM;Hdc@)ToxjACPeVS$b6c0Ll~xLXqaa^}`O9Jgt2zVrRsd()FlLhN6J2oYkj zSj?g*DkUq7IQGr_lZ;10#v#uJ@>ww_) zHuL%Xy2tqswUM_ZNv6Neg&wtc(&in)t1b~$B*X@BW&<21x$xFP~O3Z(6&A(BfUA3=HE`8lQ!B;PU zqA31lS$2fP>z@kftId$qS0}f=+sp9yLjYPBE+KmW{w59p0boA>bTj82fNrO~10d@M zfTj8;y=Ce{PXR2c0I~^B91m?zu{n3tokB+pf422ek4_DMLTiW{B2qujLktf00000 LNkvXXu0mjfOn~Aa diff --git a/mobile/android/app/src/photon/res/drawable-xxhdpi/add_to_homescreen.png b/mobile/android/app/src/photon/res/drawable-xxhdpi/add_to_homescreen.png index 039c4ae00d07962a831fddbb74404da2f968dc85..f1bc30f139a6ce86fa4c1b45911f1e3e8f1fe481 100644 GIT binary patch delta 849 zcmV-X1FrnT1?L8kBYy)fNklVzZsQJuiFTZf8z%-sR|bHFzN@LWK$y-@bkO&$6uSx~>;1TC`~1E?v4L zQYx`grc9aCMT!*JiTuVBNcS{N3o9Jp0AoLgu^~ONBPkq(B!9+HNMam?WXDlR0vv@z zz`=7~5s;+8IgNp5#pOJ=NK)aLrkRD#yO{{$pCa9B`~B*LL1NrXd3k_d;6BoPiB zNh%yVk~BDUB#Ch7ND|@DktD*QBT0lqN0JDKjwBHd9Z3QlA0QEMZpRe|<@1HZ;k0o? zT0ubaew(ydv43JcsqIv)S~Vf+SQFd%52&jRwJu%Pa}l<0J;mWb9jR@hod&l0p0VR- z;=3>>X^)$Z<2?|2_u1AU88^$H^+6fr^$7 z{{oPUWyyoajT#vf&)zI0PX6#Xo@|BQFT@fkScB@X*{83qzqAU2=CAlf14A{Ptth{NDlL?|;+*L9P%<4Yj_|B5Lf z1*Cu!kOERb3djdQDBr`O&QuEMCr~xpB=I`CUjhA&)E_WUad{S(EbMfY6V;fkV(&}YFbbP`#fZT830M{AjAi?(?2;!Qm72pZbrVz2lO@@^FKDQ{blZ;`2@@k&); z4F)aI@4=yH1$!$84CgvKZ;$3&BLx~h9uS<@{Pcq!#4l{*cAw+iVJF3*_=*xGN+d!j zdP<`+&wYmT1^t=>oU9?AOwPVf2-K1W;w?bO-38ScbWSP1f%Fp~J_5uwK)xEoU&#=tvgf(2*>{p(9y@Lr1a* zhmNEl=Pm`OWy_Yea3L+ab?a8yC?vvuP9MSrcESI>KzW$sn&UW)M3IpQT9G2+$P4U7 z82f-S#vH;Jhkvwfy7}Y826RCgV_p0}bR;^CnLuGPZhY9PSktQ7C)S@hDurl;{Q6z z-<4GOznjW9`~3bE>7L(EY!PjF@ zAAbJ)19}@F{wJR2mBFo5U6%a1*oM?aV9ZE!DE;*A-MgZ^N24l;xMMDmhM4sb(-56t zJ7TAXUVqwS-mgA;&h>Nw4G7q?S(QSRUC7wewr$&*n5H*+x#xGRBXmW+ETe%}{z&>O zlgUic{SLDUNyR~LPYZ=Y9icOFQHN%ZB@pNclg!2VBLq(F}I8HS* zwFu39){Ju*eRLN|O+KH`puy>=Q>e$K0P-I5TqQ$o$Y!&H{yyvCa5x+ehw}|uMH4M- SBA3VBzv~aSW+od^?wU!jmY8*7uup zqhG!{v&Veir3D`XDz@d`-gbT4_AN`d0vWyM=0#hd;LBf?=l?I~+{$08ODp$X^ZlpN zwMe6D(V`W*-|c!mWzCv3#h*TXI;W?lWff@Jqj9}df8W<@(ZBB&oz{)3eDKlki2wb% z-)}#5dCgDw_SB$i-u*vX3Rd=CE;y@SSSfHuoT2dZC*I;V; zb?VHSmFrj->Nvig6=z_0o3~=+dSDQ7FJ<`P%yF`Y>4Wozd^^S)?E)uim^Sb`%9r*X z+WfZ6hrv#N*7fa7e{2()XLBmt=(1E+Rt^kb?%kXdx8vut+4uXc8TMqx^DrGOoD=@~ zRZ0DXT-ey$5AZVichX?$ObVb465oqio3ci3N43HauqTkC6@%Y5;6;hdn^3|lTd zo4g|W_K$rlr|)TFm}Eaq@}1%~g{Z}gy+u=m9gJq~5lt;q-ZMdo_3*n*oD0_EDaJ`h ze49P%(A$)XUmaXp=Rfxa+udYqxGweDNb$JBZH7PpUItuCk#`7ZWLS2CJwb_);Y)wm z4F-dK^OLv_Z2x5^BGmAXm6dh*QK<&8Q*)Pof4wMYnr8y*zNOPb(^h)cI_ziOF22IZ zRi5EGBZ~tA8hRU5-z-NH+_?xd)eXUMV1BYr!;Qc@1o630uojSF2(ig|7b+6+jO&KbR_of+a z=hgCg__%y);=8l=19$tW9<#Zekasy`#-+@1tK?fxG=%1ES+!)fj=5NIR`!hWD_)2C zLq9V_X4IC{*(k|>->kxVEWGD7e}VP!61y44ZH4OAczsQHB-XGsvOa3bpHyivX9jt% z->0W_P5gQK3;)(Ft*5se8rI7%3h)fRX}{>6A;X6?leE@w`(3ME)))Bsds3di`_n%c z^TRC{hh9k*d=|W5kxFgA{5WNWZ|*mKeC^o&RXy4@^p{Rp!v~8+(n&!#Z?5C>{L^*t r#xw;t#eeNDRZ}CyL|g+!Qe)d0dA68wUuxC`6%GuZu6{1-oD!M<`FQ8D literal 667 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#z0(?STfwY#EmO2<{Yin!3fTpG< zgb9=ZGJsqN1IRWuHioGJ(fa!OKoTMgHU>n%6+lEF3V`MVxj#1kI|Z~=wIs+dn8EzR zM>FFypCr}Ry6?FMJ^s7x^ShjXTZN^Bb~fy8Vqjq0?CIhdQW5v| z`t7_|1_G@Q3m4q>bFz5mvEa*wzyH?XXyTc;$weZ%c~w~4_67eZOXvyx%IyXMZ{xL_ zzf?AOz4`v>XYucAYM-Bfbrg8ltZ;|jaR+}>fjrAkJFZWERTv$&*av5^HD%1_V0mcA zai~^c$!`sh?|~EEEmpYe?zpSJsYIUjr5)F$S`l=Ka{Iu#`1#)713$cqev@CmzMGdr zj3w@V(>|Nc+6lTGd+eBAcV{y&G+uXqaG$keCew$48~p462kI;un7%nPPMhE~pTA*( zke;+1A8VU%TFd3b6P+3#c2qHWh_o^-nZ&8E`|IJx*%Jiy&YpKzm%)=${_WhSqiHM_ zivH?xkB=X?x!elpTKfACZ>(*;}1*Z_mGFMh+}W0*o~ugxEe@ z3=})i9-qkYUA%!qMmC}1z;b32=?s~GPYstCd8I8Z68_I;u6ZN-;T#)RwW!x;4uhv# z^SR9ewwop7`o1Yuba4M8`IbM!S#EVz{DSo-veo?;+E0A0^_KZX*PirU)3~?HT{z+D o(S??FOnqk;u2^neSXda(xlY_IcDj==FuoZ)UHx3vIVCg!00qGUrT_o{ diff --git a/mobile/android/app/src/photon/res/drawable/add_to_homescreen.png b/mobile/android/app/src/photon/res/drawable/add_to_homescreen.png index a80852f514829d0dbbb9f1d7f48aa9a66806afc0..d2b30e1b7b55276cdde9d01dd2ed5fb177f15bae 100644 GIT binary patch delta 398 zcmV;90dfAZ1g8U#BYy#INklv4CXgJE?}%;*bqt3BJeE*yI6+G& zH=M8~#HNIwo4b3oS%6mtWr{|nXk5@ZP>!~X*nm49O~s04_AhlYkSBa2&F zT5=-vB7B0ZaSD>;d#D9vSS+Xl$r&3Pv*NM?dH{%GUr4WmpRWb00000Ne4wvM6N<$f{CS`Y5)KL delta 537 zcmV+!0_Odv1F-~Px$>q$gGR7ef&R6R(;KorhTGl=Mtf}5Mr z(x!-XP;h8*5D^!lf(&) z+N9zlc<^%X@B3cz?p-A5e+6_v4T$q_JRV<+Mx&3AWHy_hihcz(#j+e{J|hiZxBKg#zd0 z3W6o%(J0;%;0NmonD6N_O>=?aBTT`5h!NCqq3Y0Am_HVaoj^~MjhC3fwgq1rI!)9O zxgoULnx@f(%&V&U(sM}?FHo=7&&UaI24EH+Yp+x))PJr65yT6KPEkkUJ!YeL8~TOE zmtk?gou7eU@Hc#86~LsNqJ3B66b@{WNM>2qzG#Qh{l4Jc1tt@T#Ox>%_eV!RKUxge zv)t#vuL3QQap@kgFZE>x7?DV1ty-<#c+&>H0DX2a$D2qjLRwyc$99najQg|WNAAh} bqvjWJ!V>B{T#_-A00000NkvXXu0mjfzZm^H From fb207e4f8a189afe0934d28f602bd523919b6504 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Mon, 4 Dec 2017 16:29:35 +1300 Subject: [PATCH 158/219] Bug 1422662 - Move BaseMediaResource::Close() into MediaResource class. r=jwwang For the project to export Gecko's media stack and import it into Servo, I need a way to shutdown an abstract MediaResource. So I'd like to move BaseMediaResource::Close() up into MediaResource class. MozReview-Commit-ID: 9JmxJPs02PN --HG-- extra : rebase_source : e61cc1a3a79b3e4ca7e9fa86a602e6e26044e247 --- dom/media/BaseMediaResource.h | 5 ----- dom/media/MediaResource.h | 5 +++++ dom/media/mediasource/SourceBufferResource.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dom/media/BaseMediaResource.h b/dom/media/BaseMediaResource.h index 28a7d9007892..1fdfb6dd75cb 100644 --- a/dom/media/BaseMediaResource.h +++ b/dom/media/BaseMediaResource.h @@ -29,11 +29,6 @@ public: nsIChannel* aChannel, bool aIsPrivateBrowsing); - // Close the resource, stop any listeners, channels, etc. - // Cancels any currently blocking Read request and forces that request to - // return an error. - virtual nsresult Close() = 0; - // Pass true to limit the amount of readahead data (specified by // "media.cache_readahead_limit") or false to read as much as the // cache size allows. diff --git a/dom/media/MediaResource.h b/dom/media/MediaResource.h index 097cb0c0e4a1..a23ba4e55ef9 100644 --- a/dom/media/MediaResource.h +++ b/dom/media/MediaResource.h @@ -56,6 +56,11 @@ public: NS_METHOD_(MozExternalRefCountType) AddRef(void); NS_METHOD_(MozExternalRefCountType) Release(void); + // Close the resource, stop any listeners, channels, etc. + // Cancels any currently blocking Read request and forces that request to + // return an error. + virtual nsresult Close() { return NS_OK; } + // These methods are called off the main thread. // Read up to aCount bytes from the stream. The read starts at // aOffset in the stream, seeking to that location initially if diff --git a/dom/media/mediasource/SourceBufferResource.h b/dom/media/mediasource/SourceBufferResource.h index 4fd0e5060134..a647485c8187 100644 --- a/dom/media/mediasource/SourceBufferResource.h +++ b/dom/media/mediasource/SourceBufferResource.h @@ -31,7 +31,7 @@ class SourceBufferResource final : public MediaResource { public: SourceBufferResource(); - nsresult Close(); + nsresult Close() override; nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, From 33f3ce5cd671ed9adf9af30b7f2ffd3ba8903568 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Thu, 16 Nov 2017 17:58:37 +0800 Subject: [PATCH 159/219] Bug 1417837 - Part 1: De-scope about:reader style sheets. r=Gijs MozReview-Commit-ID: 8C65ljtFDrh --HG-- extra : rebase_source : 442d8cbaf7f7252b6b6393845d2d09e265678ae9 --- mobile/android/themes/core/aboutReader.css | 406 +++++++++++++ .../themes/core/aboutReaderContent.css | 114 ---- .../themes/core/aboutReaderControls.css | 292 --------- mobile/android/themes/core/jar.mn | 4 - .../components/narrate/NarrateControls.jsm | 6 +- .../printing/content/simplifyMode.css | 8 +- .../reader/content/aboutReader.html | 12 - toolkit/content/browser-content.js | 6 - toolkit/themes/shared/aboutReader.css | 573 ++++++++++++++++++ toolkit/themes/shared/aboutReaderContent.css | 178 ------ toolkit/themes/shared/aboutReaderControls.css | 394 ------------ toolkit/themes/shared/jar.inc.mn | 5 +- toolkit/themes/shared/narrate.css | 187 ++++++ toolkit/themes/shared/narrateControls.css | 186 ------ 14 files changed, 1172 insertions(+), 1199 deletions(-) delete mode 100644 mobile/android/themes/core/aboutReaderContent.css delete mode 100644 mobile/android/themes/core/aboutReaderControls.css delete mode 100644 toolkit/themes/shared/aboutReaderContent.css delete mode 100644 toolkit/themes/shared/aboutReaderControls.css delete mode 100644 toolkit/themes/shared/narrateControls.css diff --git a/mobile/android/themes/core/aboutReader.css b/mobile/android/themes/core/aboutReader.css index 2df2d1ff6206..32f52080c04d 100644 --- a/mobile/android/themes/core/aboutReader.css +++ b/mobile/android/themes/core/aboutReader.css @@ -118,3 +118,409 @@ body.dark > .container > .content blockquote { color: #aaaaaa !important; border-left-color: #777777 !important; } + +#reader-message { + margin-top: 40px; + display: none; + text-align: center; + width: 100%; + font-size: 0.9em; +} + +.header { + text-align: start; + display: none; +} + +.domain, +.credits { + font-size: 0.9em; + font-family: sans-serif; +} + +.domain { + margin-top: 10px; + padding-bottom: 10px; + color: #00acff !important; + text-decoration: none; +} + +.domain-border { + margin-top: 15px; + border-bottom: 1.5px solid #777777; + width: 50%; +} + +.header > h1 { + font-size: 1.33em; + font-weight: 700; + line-height: 1.1em; + width: 100%; + margin: 0px; + margin-top: 32px; + margin-bottom: 16px; + padding: 0px; +} + +.header > .credits { + padding: 0px; + margin: 0px; + margin-bottom: 32px; +} + +/*======= Controls toolbar =======*/ + +.toolbar { + font-family: sans-serif; + position: fixed; + width: 100%; + left: 0; + margin: 0; + padding: 0; + bottom: 0; + list-style: none; + pointer-events: none; + transition: opacity 420ms linear; +} + +.toolbar > * { + float: right; +} + +.button { + width: 56px; + height: 56px; + display: block; + background-position: center; + background-size: 26px 16px; + background-repeat: no-repeat; + background-color: #0979D9; + border-radius: 10000px; + margin: 20px; + border: 0; + box-shadow: 0px 4px 8px 0px rgba(0,0,0,0.40); +} + +.button:active { + background-color: #086ACC; +} + +/* Remove dotted border when button is focused */ +.button::-moz-focus-inner, +.dropdown-popup > div > button::-moz-focus-inner { + border: 0; +} + +.button[hidden], +.toolbar[hidden] { + display: none; +} + +.dropdown-toggle, +#reader-popup { + pointer-events: auto; +} + +.dropdown { + left: 0; + text-align: center; + display: inline-block; + list-style: none; + margin: 0px; + padding: 0px; +} + +/*======= Font style popup =======*/ + +.dropdown-popup { + position: absolute; + left: 0; + width: calc(100% - 30px); + margin: 15px; + z-index: 1000; + background: #EBEBF0; + visibility: hidden; + border: 0; + border-radius: 4px; + box-shadow: 0px 4px 8px 0px rgba(0,0,0,0.40); + -moz-user-select: none; +} + +/* Only used on desktop */ +.dropdown-popup > hr, +.dropdown-arrow, +#font-type-buttons > button > .name, +#content-width-buttons, +#line-height-buttons { + display: none; +} + +.open > .dropdown-popup { + visibility: visible; + bottom: 0; +} + +#font-type-buttons, +#font-size-buttons, +#color-scheme-buttons { + display: flex; + flex-direction: row; +} + +#font-type-buttons > button, +#color-scheme-buttons > button { + text-align: center; +} + +#font-type-buttons > button, +#font-size-buttons > button { + width: 50%; + background-color: transparent; + border: 0; +} + +#font-type-buttons > button { + font-size: 24px; + color: #AFB1B3; + padding: 15px 0; +} + +#font-type-buttons > button:active, +#font-type-buttons > button.selected { + color: #222222; +} + +#font-size-sample { + flex: 0; + font-size: 24px; + color: #000000; + margin: 0 30px; + padding: 0 10px; +} + +.serif-button { + font-family: serif; +} + +.minus-button, +.plus-button { + background-color: transparent; + border: 0; + height: 60px; + background-size: 18px 18px; + background-repeat: no-repeat; + background-position: center; +} + +.minus-button { + background-size: 24px 6px; + margin-left: 50px; + padding: 0 5px; +} + +.plus-button { + background-size: 24px 24px; + margin-right: 50px; + padding: 0 5px; +} + +#color-scheme-buttons > button { + width: 33%; + border-radius: 4px; + border: 1px solid #BFBFBF; + padding: 10px; + margin: 15px 10px; + font-size: 14px; +} + +#color-scheme-buttons > button:active, +#color-scheme-buttons > button.selected { + border: 2px solid #0A84FF; +} + +.dark-button { + color: #eeeeee; + background-color: #333333; +} + +.auto-button { + color: #000000; + background-color: transparent; +} + +.light-button { + color: #333333; + background-color: #ffffff; +} + +/*======= Toolbar icons =======*/ + +/* desktop-only controls */ +.close-button { + display: none; +} + +.style-button { + background-image: url('chrome://browser/skin/images/reader-style-icon-hdpi.png'); +} + +.minus-button { + background-image: url('chrome://browser/skin/images/reader-minus-hdpi.png'); +} + +.plus-button { + background-image: url('chrome://browser/skin/images/reader-plus-hdpi.png'); +} + +@media screen and (min-resolution: 2dppx) { + .style-button { + background-image: url('chrome://browser/skin/images/reader-style-icon-xhdpi.png'); + } + + .minus-button { + background-image: url('chrome://browser/skin/images/reader-minus-xhdpi.png'); + } + + .plus-button { + background-image: url('chrome://browser/skin/images/reader-plus-xhdpi.png'); + } +} + +@media screen and (min-resolution: 3dppx) { + .style-button { + background-image: url('chrome://browser/skin/images/reader-style-icon-xxhdpi.png'); + } + + .minus-button { + background-image: url('chrome://browser/skin/images/reader-minus-xxhdpi.png'); + } + + .plus-button { + background-image: url('chrome://browser/skin/images/reader-plus-xxhdpi.png'); + } +} + +@media screen and (min-width: 960px) { + .dropdown-popup { + width: 350px; + left: auto; + right: 0; + } +} + +/*======= Article content =======*/ + +/* Note that any class names from the original article that we want to match on + * must be added to CLASSES_TO_PRESERVE in ReaderMode.jsm, so that + * Readability.js doesn't strip them out */ + +#moz-reader-content { + display: none; + font-size: 1em; +} + +#moz-reader-content a { + text-decoration: underline !important; + font-weight: normal; +} + +#moz-reader-content a, +#moz-reader-content a:visited, +#moz-reader-content a:hover, +#moz-reader-content a:active { + color: #00acff !important; +} + +#moz-reader-content * { + max-width: 100% !important; + height: auto !important; +} + +#moz-reader-content p { + line-height: 1.4em !important; + margin: 0px !important; + margin-bottom: 20px !important; +} + +/* Covers all images showing edge-to-edge using a + an optional caption text */ +#moz-reader-content .wp-caption, +#moz-reader-content figure { + display: block !important; + width: 100% !important; + margin: 0px !important; + margin-bottom: 32px !important; +} + +/* Images marked to be shown edge-to-edge with an + optional captio ntext */ +#moz-reader-content p > img:only-child, +#moz-reader-content p > a:only-child > img:only-child, +#moz-reader-content .wp-caption img, +#moz-reader-content figure img { + display: block; + margin-left: auto; + margin-right: auto; +} + +/* Account for body padding to make image full width */ +#moz-reader-content img[moz-reader-full-width] { + width: calc(100% + 40px); + margin-left: -20px; + margin-right: -20px; + max-width: none !important; +} + +/* Image caption text */ +#moz-reader-content .caption, +#moz-reader-content .wp-caption-text, +#moz-reader-content figcaption { + font-size: 0.9em; + font-family: sans-serif; + margin: 0px !important; + padding-top: 4px !important; +} + +/* Ensure all pre-formatted code inside the reader content + are properly wrapped inside content width */ +#moz-reader-content code, +#moz-reader-content pre { + white-space: pre-wrap !important; + margin-bottom: 20px !important; +} + +#moz-reader-content blockquote { + margin: 0px !important; + margin-bottom: 20px !important; + padding: 0px !important; + padding-inline-start: 16px !important; + border: 0px !important; + border-left: 2px solid !important; +} + +#moz-reader-content ul, +#moz-reader-content ol { + margin: 0px !important; + margin-bottom: 20px !important; + padding: 0px !important; + line-height: 1.5em; +} + +#moz-reader-content ul { + padding-inline-start: 30px !important; + list-style: disc !important; +} + +#moz-reader-content ol { + padding-inline-start: 35px !important; + list-style: decimal !important; +} + +/* Hide elements with common "hidden" class names */ +#moz-reader-content .visually-hidden, +#moz-reader-content .visuallyhidden, +#moz-reader-content .hidden, +#moz-reader-content .invisible, +#moz-reader-content .sr-only { + display: none; +} diff --git a/mobile/android/themes/core/aboutReaderContent.css b/mobile/android/themes/core/aboutReaderContent.css deleted file mode 100644 index fe6451df2f52..000000000000 --- a/mobile/android/themes/core/aboutReaderContent.css +++ /dev/null @@ -1,114 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#moz-reader-content { - display: none; - font-size: 1em; -} - -a { - text-decoration: underline !important; - font-weight: normal; -} - -a, -a:visited, -a:hover, -a:active { - color: #00acff !important; -} - -* { - max-width: 100% !important; - height: auto !important; -} - -p { - line-height: 1.4em !important; - margin: 0px !important; - margin-bottom: 20px !important; -} - -/* Covers all images showing edge-to-edge using a - an optional caption text */ -.wp-caption, -figure { - display: block !important; - width: 100% !important; - margin: 0px !important; - margin-bottom: 32px !important; -} - -/* Images marked to be shown edge-to-edge with an - optional captio ntext */ -p > img:only-child, -p > a:only-child > img:only-child, -.wp-caption img, -figure img { - display: block; - margin-left: auto; - margin-right: auto; -} - -/* Account for body padding to make image full width */ -img[moz-reader-full-width] { - width: calc(100% + 40px); - margin-left: -20px; - margin-right: -20px; - max-width: none !important; -} - -/* Image caption text */ -.caption, -.wp-caption-text, -figcaption { - font-size: 0.9em; - font-family: sans-serif; - margin: 0px !important; - padding-top: 4px !important; -} - -/* Ensure all pre-formatted code inside the reader content - are properly wrapped inside content width */ -code, -pre { - white-space: pre-wrap !important; - margin-bottom: 20px !important; -} - -blockquote { - margin: 0px !important; - margin-bottom: 20px !important; - padding: 0px !important; - padding-inline-start: 16px !important; - border: 0px !important; - border-left: 2px solid !important; -} - -ul, -ol { - margin: 0px !important; - margin-bottom: 20px !important; - padding: 0px !important; - line-height: 1.5em; -} - -ul { - padding-inline-start: 30px !important; - list-style: disc !important; -} - -ol { - padding-inline-start: 35px !important; - list-style: decimal !important; -} - -/* Hide elements with common "hidden" class names */ -.visually-hidden, -.visuallyhidden, -.hidden, -.invisible, -.sr-only { - display: none; -} diff --git a/mobile/android/themes/core/aboutReaderControls.css b/mobile/android/themes/core/aboutReaderControls.css deleted file mode 100644 index 4bfae9cb0da6..000000000000 --- a/mobile/android/themes/core/aboutReaderControls.css +++ /dev/null @@ -1,292 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#reader-message { - margin-top: 40px; - display: none; - text-align: center; - width: 100%; - font-size: 0.9em; -} - -.header { - text-align: start; - display: none; -} - -.domain, -.credits { - font-size: 0.9em; - font-family: sans-serif; -} - -.domain { - margin-top: 10px; - padding-bottom: 10px; - color: #00acff !important; - text-decoration: none; -} - -.domain-border { - margin-top: 15px; - border-bottom: 1.5px solid #777777; - width: 50%; -} - -.header > h1 { - font-size: 1.33em; - font-weight: 700; - line-height: 1.1em; - width: 100%; - margin: 0px; - margin-top: 32px; - margin-bottom: 16px; - padding: 0px; -} - -.header > .credits { - padding: 0px; - margin: 0px; - margin-bottom: 32px; -} - -/*======= Controls toolbar =======*/ - -.toolbar { - font-family: sans-serif; - position: fixed; - width: 100%; - left: 0; - margin: 0; - padding: 0; - bottom: 0; - list-style: none; - pointer-events: none; - transition: opacity 420ms linear; -} - -.toolbar > * { - float: right; -} - -.button { - width: 56px; - height: 56px; - display: block; - background-position: center; - background-size: 26px 16px; - background-repeat: no-repeat; - background-color: #0979D9; - border-radius: 10000px; - margin: 20px; - border: 0; - box-shadow: 0px 4px 8px 0px rgba(0,0,0,0.40); -} - -.button:active { - background-color: #086ACC; -} - -/* Remove dotted border when button is focused */ -.button::-moz-focus-inner, -.dropdown-popup > div > button::-moz-focus-inner { - border: 0; -} - -.button[hidden], -.toolbar[hidden] { - display: none; -} - -.dropdown-toggle, -#reader-popup { - pointer-events: auto; -} - -.dropdown { - left: 0; - text-align: center; - display: inline-block; - list-style: none; - margin: 0px; - padding: 0px; -} - -/*======= Font style popup =======*/ - -.dropdown-popup { - position: absolute; - left: 0; - width: calc(100% - 30px); - margin: 15px; - z-index: 1000; - background: #EBEBF0; - visibility: hidden; - border: 0; - border-radius: 4px; - box-shadow: 0px 4px 8px 0px rgba(0,0,0,0.40); - -moz-user-select: none; -} - -/* Only used on desktop */ -.dropdown-popup > hr, -.dropdown-arrow, -#font-type-buttons > button > .name, -#content-width-buttons, -#line-height-buttons { - display: none; -} - -.open > .dropdown-popup { - visibility: visible; - bottom: 0; -} - -#font-type-buttons, -#font-size-buttons, -#color-scheme-buttons { - display: flex; - flex-direction: row; -} - -#font-type-buttons > button, -#color-scheme-buttons > button { - text-align: center; -} - -#font-type-buttons > button, -#font-size-buttons > button { - width: 50%; - background-color: transparent; - border: 0; -} - -#font-type-buttons > button { - font-size: 24px; - color: #AFB1B3; - padding: 15px 0; -} - -#font-type-buttons > button:active, -#font-type-buttons > button.selected { - color: #222222; -} - -#font-size-sample { - flex: 0; - font-size: 24px; - color: #000000; - margin: 0 30px; - padding: 0 10px; -} - -.serif-button { - font-family: serif; -} - -.minus-button, -.plus-button { - background-color: transparent; - border: 0; - height: 60px; - background-size: 18px 18px; - background-repeat: no-repeat; - background-position: center; -} - -.minus-button { - background-size: 24px 6px; - margin-left: 50px; - padding: 0 5px; -} - -.plus-button { - background-size: 24px 24px; - margin-right: 50px; - padding: 0 5px; -} - -#color-scheme-buttons > button { - width: 33%; - border-radius: 4px; - border: 1px solid #BFBFBF; - padding: 10px; - margin: 15px 10px; - font-size: 14px; -} - -#color-scheme-buttons > button:active, -#color-scheme-buttons > button.selected { - border: 2px solid #0A84FF; -} - -.dark-button { - color: #eeeeee; - background-color: #333333; -} - -.auto-button { - color: #000000; - background-color: transparent; -} - -.light-button { - color: #333333; - background-color: #ffffff; -} - -/*======= Toolbar icons =======*/ - -/* desktop-only controls */ -.close-button { - display: none; -} - -.style-button { - background-image: url('chrome://browser/skin/images/reader-style-icon-hdpi.png'); -} - -.minus-button { - background-image: url('chrome://browser/skin/images/reader-minus-hdpi.png'); -} - -.plus-button { - background-image: url('chrome://browser/skin/images/reader-plus-hdpi.png'); -} - -@media screen and (min-resolution: 2dppx) { - .style-button { - background-image: url('chrome://browser/skin/images/reader-style-icon-xhdpi.png'); - } - - .minus-button { - background-image: url('chrome://browser/skin/images/reader-minus-xhdpi.png'); - } - - .plus-button { - background-image: url('chrome://browser/skin/images/reader-plus-xhdpi.png'); - } -} - -@media screen and (min-resolution: 3dppx) { - .style-button { - background-image: url('chrome://browser/skin/images/reader-style-icon-xxhdpi.png'); - } - - .minus-button { - background-image: url('chrome://browser/skin/images/reader-minus-xxhdpi.png'); - } - - .plus-button { - background-image: url('chrome://browser/skin/images/reader-plus-xxhdpi.png'); - } -} - -@media screen and (min-width: 960px) { - .dropdown-popup { - width: 350px; - left: auto; - right: 0; - } -} diff --git a/mobile/android/themes/core/jar.mn b/mobile/android/themes/core/jar.mn index 560bb6f319db..c07c0de67b55 100644 --- a/mobile/android/themes/core/jar.mn +++ b/mobile/android/themes/core/jar.mn @@ -15,8 +15,6 @@ chrome.jar: skin/aboutMemory.css (aboutMemory.css) skin/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) skin/aboutReader.css (aboutReader.css) - skin/aboutReaderContent.css (aboutReaderContent.css) - skin/aboutReaderControls.css (aboutReaderControls.css) skin/aboutSupport.css (aboutSupport.css) skin/config.css (config.css) skin/defines.css (defines.css) @@ -25,8 +23,6 @@ chrome.jar: % override chrome://global/skin/about.css chrome://browser/skin/about.css % override chrome://global/skin/aboutMemory.css chrome://browser/skin/aboutMemory.css % override chrome://global/skin/aboutReader.css chrome://browser/skin/aboutReader.css -% override chrome://global/skin/aboutReaderContent.css chrome://browser/skin/aboutReaderContent.css -% override chrome://global/skin/aboutReaderControls.css chrome://browser/skin/aboutReaderControls.css % override chrome://global/skin/aboutSupport.css chrome://browser/skin/aboutSupport.css % override chrome://global/skin/netError.css chrome://browser/skin/netError.css diff --git a/toolkit/components/narrate/NarrateControls.jsm b/toolkit/components/narrate/NarrateControls.jsm index 35c2b0bccf99..c202ff627d57 100644 --- a/toolkit/components/narrate/NarrateControls.jsm +++ b/toolkit/components/narrate/NarrateControls.jsm @@ -41,13 +41,9 @@ function NarrateControls(mm, win, languagePromise) { dropdown.className = "dropdown"; dropdown.id = "narrate-dropdown"; // We need inline svg here for the animation to work (bug 908634 & 1190881). - // The style animation can't be scoped (bug 830056). // eslint-disable-next-line no-unsanitized/property dropdown.innerHTML = - localize` -
  • + localize`