зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c into b2g-inbound
This commit is contained in:
Коммит
ad89cf8ff2
|
@ -219,7 +219,7 @@ skip-if = e10s
|
|||
[browser_bug565667.js]
|
||||
run-if = toolkit == "cocoa"
|
||||
[browser_bug567306.js]
|
||||
skip-if = e10s
|
||||
skip-if = e10s # Bug XXX - Needs some massaging to run in e10s
|
||||
[browser_bug575561.js]
|
||||
[browser_bug575830.js]
|
||||
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
|
||||
|
|
|
@ -13,8 +13,8 @@ function test() {
|
|||
whenNewWindowLoaded(undefined, function (win) {
|
||||
whenDelayedStartupFinished(win, function () {
|
||||
let selectedBrowser = win.gBrowser.selectedBrowser;
|
||||
selectedBrowser.addEventListener("pageshow", function() {
|
||||
selectedBrowser.removeEventListener("pageshow", arguments.callee, true);
|
||||
selectedBrowser.addEventListener("pageshow", function pageshowListener() {
|
||||
selectedBrowser.removeEventListener("pageshow", pageshowListener, true);
|
||||
ok(true, "pageshow listener called: " + win.content.location);
|
||||
waitForFocus(function () {
|
||||
onFocus(win);
|
||||
|
@ -39,7 +39,11 @@ function onFocus(win) {
|
|||
ok(!win.gFindBarInitialized, "find bar is not yet initialized");
|
||||
let findBar = win.gFindBar;
|
||||
selectText(win.content);
|
||||
findBar.onFindCommand();
|
||||
|
||||
findBar.onFindCommand().then(onInitialized.bind(null, findBar, win));
|
||||
}
|
||||
|
||||
function onInitialized(findBar, win) {
|
||||
// When the OS supports the Find Clipboard (OSX), the find field value is
|
||||
// persisted across Fx sessions, thus not useful to test.
|
||||
if (!HasFindClipboard)
|
||||
|
|
|
@ -2,13 +2,6 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
///////////////////
|
||||
//
|
||||
// Whitelisting this test.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("");
|
||||
|
||||
var gTab = null;
|
||||
|
||||
function load(url, cb) {
|
||||
|
|
|
@ -29,7 +29,7 @@ support-files =
|
|||
[test_bug364677.html]
|
||||
[test_bug395533.html]
|
||||
[test_contextmenu.html]
|
||||
skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
|
||||
skip-if = toolkit == "gtk2" || toolkit == "gtk3" || (os == 'mac' && os_version != '10.6') # disabled on Linux due to bug 513558, on Mac after 10.6 due to bug 792304
|
||||
[test_contextmenu_input.html]
|
||||
skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
|
||||
[test_feed_discovery.html]
|
||||
|
|
|
@ -852,20 +852,14 @@ function waitForEvents(event)
|
|||
}
|
||||
}
|
||||
|
||||
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
|
||||
SpecialPowers.setBoolPref("plugins.click_to_play", true);
|
||||
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
|
||||
|
||||
if (isOSXMtnLion) {
|
||||
todo(false, "Mountain Lion doesn't like this test (bug 792304)");
|
||||
} else {
|
||||
SpecialPowers.setBoolPref("plugins.click_to_play", true);
|
||||
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
|
||||
var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=800");
|
||||
subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
|
||||
subwindow.onload = waitForEvents;
|
||||
|
||||
var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=800");
|
||||
subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
|
||||
subwindow.onload = waitForEvents;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
}
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -28,7 +28,7 @@ browser.jar:
|
|||
content/browser/preferences/languages.js
|
||||
* content/browser/preferences/main.xul
|
||||
* content/browser/preferences/main.js
|
||||
* content/browser/preferences/permissions.xul
|
||||
content/browser/preferences/permissions.xul
|
||||
* content/browser/preferences/permissions.js
|
||||
* content/browser/preferences/preferences.xul
|
||||
content/browser/preferences/preferences.js
|
||||
|
@ -48,5 +48,5 @@ browser.jar:
|
|||
content/browser/preferences/search.js
|
||||
* content/browser/preferences/tabs.xul
|
||||
* content/browser/preferences/tabs.js
|
||||
* content/browser/preferences/translation.xul
|
||||
content/browser/preferences/translation.xul
|
||||
content/browser/preferences/translation.js
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
# 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/.
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
# 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/.
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
|
||||
|
|
|
@ -2104,8 +2104,7 @@ NetworkDetailsView.prototype = {
|
|||
// 2) come from a target that provides security information.
|
||||
let hasSecurityInfo = aData.securityState &&
|
||||
aData.securityState !== "insecure";
|
||||
|
||||
$("#security-tab").hidden = !hasSecurityInfo;
|
||||
this.sidebar.toggleTab(hasSecurityInfo, "security-tab", "security-tabpanel");
|
||||
|
||||
// Switch to the "Headers" tabpanel if the "Preview" previously selected
|
||||
// and this is not an HTML response or "Security" was selected but this
|
||||
|
|
|
@ -39,7 +39,8 @@ support-files =
|
|||
[browser_perf_recordings-io-01.js]
|
||||
[browser_perf_recordings-io-02.js]
|
||||
[browser_perf_recordings-io-03.js]
|
||||
[browser_perf_recordings-io-04.js]
|
||||
[browser_perf-recording-selected-01.js]
|
||||
[browser_perf-recording-selected-02.js]
|
||||
[browser_perf-recording-selected-03.js]
|
||||
[browser_perf_recordings-io-04.js]
|
||||
[browser_perf-recording-selected-04.js]
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that all components get rerendered for a profile when switching.
|
||||
*/
|
||||
|
||||
let test = Task.async(function*() {
|
||||
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||
let { $, EVENTS, PerformanceController, RecordingsView } = panel.panelWin;
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
||||
let rerender = waitForWidgetsRendered(panel);
|
||||
RecordingsView.selectedIndex = 0;
|
||||
yield rerender;
|
||||
|
||||
rerender = waitForWidgetsRendered(panel);
|
||||
RecordingsView.selectedIndex = 1;
|
||||
yield rerender;
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
|
@ -12,13 +12,12 @@ let CallTreeView = {
|
|||
*/
|
||||
initialize: function () {
|
||||
this._callTree = $(".call-tree-cells-container");
|
||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||
this._onRecordingSelected = this._onRecordingSelected.bind(this);
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
this._onLink = this._onLink.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
},
|
||||
|
@ -27,8 +26,8 @@ let CallTreeView = {
|
|||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
},
|
||||
|
@ -47,20 +46,10 @@ let CallTreeView = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Called when recording is stopped.
|
||||
* Called when recording is stopped or has been selected.
|
||||
*/
|
||||
_onRecordingStopped: function () {
|
||||
let profilerData = PerformanceController.getProfilerData();
|
||||
this.render(profilerData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a recording has been selected.
|
||||
*/
|
||||
_onRecordingSelected: function (_, recording) {
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
// If not recording, then this recording is done and we can render all of it
|
||||
// Otherwise, TODO in bug 1120699 will hide the details view altogether if
|
||||
// this is still recording.
|
||||
if (!recording.isRecording()) {
|
||||
let profilerData = recording.getProfilerData();
|
||||
this.render(profilerData);
|
||||
|
|
|
@ -12,14 +12,15 @@ let FlameGraphView = {
|
|||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: Task.async(function* () {
|
||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
|
||||
this.graph = new FlameGraph($("#flamegraph-view"));
|
||||
this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
|
||||
yield this.graph.ready();
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
}),
|
||||
|
@ -28,7 +29,8 @@ let FlameGraphView = {
|
|||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
},
|
||||
|
@ -52,11 +54,14 @@ let FlameGraphView = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Called when recording is stopped.
|
||||
* Called when recording is stopped or selected.
|
||||
*/
|
||||
_onRecordingStopped: function () {
|
||||
let profilerData = PerformanceController.getProfilerData();
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
// If not recording, then this recording is done and we can render all of it
|
||||
if (!recording.isRecording()) {
|
||||
let profilerData = recording.getProfilerData();
|
||||
this.render(profilerData);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,8 +12,7 @@ let WaterfallView = {
|
|||
*/
|
||||
initialize: Task.async(function *() {
|
||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||
this._onRecordingStopped = this._onRecordingStopped.bind(this);
|
||||
this._onRecordingSelected = this._onRecordingSelected.bind(this);
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onMarkerSelected = this._onMarkerSelected.bind(this);
|
||||
this._onResize = this._onResize.bind(this);
|
||||
|
||||
|
@ -25,8 +24,8 @@ let WaterfallView = {
|
|||
this.details.on("resize", this._onResize);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
|
||||
this.waterfall.recalculateBounds();
|
||||
}),
|
||||
|
@ -40,8 +39,8 @@ let WaterfallView = {
|
|||
this.details.off("resize", this._onResize);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -65,16 +64,9 @@ let WaterfallView = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Called when recording stops.
|
||||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStopped: function () {
|
||||
this.render();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a recording is selected.
|
||||
*/
|
||||
_onRecordingSelected: function (_, recording) {
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
if (!recording.isRecording()) {
|
||||
this.render();
|
||||
}
|
||||
|
|
|
@ -203,7 +203,9 @@ PreviewController.prototype = {
|
|||
// Resizes the canvasPreview to 0x0, essentially freeing its memory.
|
||||
// updateCanvasPreview() will detect the size mismatch as a resize event
|
||||
// the next time it is called.
|
||||
resetCanvasPreview: function () this.resizeCanvasPreview(0, 0),
|
||||
resetCanvasPreview: function () {
|
||||
this.resizeCanvasPreview(0, 0);
|
||||
},
|
||||
|
||||
resizeCanvasPreview: function (width, height) {
|
||||
this.canvasPreview.width = width;
|
||||
|
|
|
@ -199,7 +199,7 @@ browser.jar:
|
|||
skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/buttons@2x.png (downloads/buttons@2x.png)
|
||||
* skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
|
||||
skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
|
||||
skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
|
||||
|
|
|
@ -26,8 +26,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=995943
|
|||
/** Test for CAPS file:// URI prefs. **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestCompleteLog();
|
||||
if (Services.appinfo.OS == "Darwin") // See bug 1067022
|
||||
SimpleTest.expectAssertions(0, 1);
|
||||
if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1)
|
||||
SimpleTest.expectAssertions(5); // See bug 1067022
|
||||
else if (Services.appinfo.OS == "Darwin")
|
||||
SimpleTest.expectAssertions(0, 1); // See bug 1067022
|
||||
|
||||
var rootdir = Services.appinfo.OS == "WINNT" ? "file:///C:" : "file:///";
|
||||
|
||||
|
|
|
@ -319,7 +319,7 @@ private:
|
|||
/*
|
||||
* A class that represents a new script entry point.
|
||||
*/
|
||||
class AutoEntryScript : public AutoJSAPI,
|
||||
class MOZ_STACK_CLASS AutoEntryScript : public AutoJSAPI,
|
||||
protected ScriptSettingsStackEntry {
|
||||
public:
|
||||
explicit AutoEntryScript(nsIGlobalObject* aGlobalObject,
|
||||
|
@ -341,10 +341,10 @@ private:
|
|||
// is the principal of the callee function that is part of the CallArgs just a
|
||||
// bit up the stack, and which will outlive us. So we know the principal
|
||||
// can't go away until then either.
|
||||
nsIPrincipal* mWebIDLCallerPrincipal;
|
||||
nsIPrincipal* MOZ_NON_OWNING_REF mWebIDLCallerPrincipal;
|
||||
friend nsIPrincipal* GetWebIDLCallerPrincipal();
|
||||
|
||||
nsIDocShell* mDocShellForJSRunToCompletion;
|
||||
nsCOMPtr<nsIDocShell> mDocShellForJSRunToCompletion;
|
||||
|
||||
bool mIsMainThread;
|
||||
};
|
||||
|
|
|
@ -155,7 +155,9 @@ enum EventNameType {
|
|||
|
||||
struct EventNameMapping
|
||||
{
|
||||
nsIAtom* mAtom;
|
||||
// This holds pointers to nsGkAtoms members, and is therefore safe as a
|
||||
// non-owning reference.
|
||||
nsIAtom* MOZ_OWNING_REF mAtom;
|
||||
uint32_t mId;
|
||||
int32_t mType;
|
||||
mozilla::EventClassID mEventClassID;
|
||||
|
|
|
@ -680,7 +680,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(bug 904183
|
|||
[test_fileapi.html]
|
||||
skip-if = e10s
|
||||
[test_fileapi_slice.html]
|
||||
disabled = Busted on B2G, Android, E10S and now Mulet. Bug 775227.
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s #bug 775227
|
||||
[test_getElementById.html]
|
||||
[test_html_colors_quirks.html]
|
||||
[test_html_colors_standards.html]
|
||||
|
|
|
@ -22,11 +22,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=575946
|
|||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
|
||||
|
||||
if (isOSXMtnLion) {
|
||||
todo(false, "Mountain Lion doesn't like this test (bug 788999)");
|
||||
} else {
|
||||
var fileNum = 1;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
@ -136,7 +131,6 @@ var img = new Image;
|
|||
img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size + 1000));
|
||||
img.onload = imageLoadHandler;
|
||||
expectedTestCount++;
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body> </html>
|
||||
|
|
|
@ -175,7 +175,9 @@ private:
|
|||
|
||||
// For callees that know we exist, we can be a stringbuffer/length/null-flag
|
||||
// triple.
|
||||
nsStringBuffer* mStringBuffer;
|
||||
nsStringBuffer* MOZ_UNSAFE_REF("The ways in which this can be safe are "
|
||||
"documented above and enforced through "
|
||||
"assertions") mStringBuffer;
|
||||
uint32_t mLength;
|
||||
bool mIsNull;
|
||||
};
|
||||
|
|
|
@ -558,7 +558,7 @@ protected:
|
|||
uint32_t mNoListenerForEvent : 23;
|
||||
|
||||
nsAutoTObserverArray<Listener, 2> mListeners;
|
||||
dom::EventTarget* mTarget; // WEAK
|
||||
dom::EventTarget* MOZ_NON_OWNING_REF mTarget;
|
||||
nsCOMPtr<nsIAtom> mNoListenerForEventAtom;
|
||||
|
||||
friend class ELMCreationDetector;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "nsTArray.h"
|
||||
#include "MediaDecoder.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/mozalloc.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
|
@ -165,7 +166,7 @@ static_assert(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS,
|
|||
// MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration
|
||||
// less than this are ignored, as they're assumed to be the result of
|
||||
// instability in the duration estimation.
|
||||
static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
|
||||
static const uint64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
|
||||
|
||||
static TimeDuration UsecsToDuration(int64_t aUsecs) {
|
||||
return TimeDuration::FromMicroseconds(aUsecs);
|
||||
|
@ -1449,7 +1450,7 @@ void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
|
|||
AssertCurrentThreadInMonitor();
|
||||
int64_t duration = GetDuration();
|
||||
if (aDuration != duration &&
|
||||
std::abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
|
||||
mozilla::Abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
|
||||
SetDuration(aDuration);
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
|
||||
|
|
|
@ -59,6 +59,10 @@ static bool gDebug_isLoggingEnabled = false;
|
|||
static bool gDebug_isGPSLocationIgnored = false;
|
||||
static const char* kNetworkConnStateChangedTopic = "network-connection-state-changed";
|
||||
static const char* kMozSettingsChangedTopic = "mozsettings-changed";
|
||||
#ifdef MOZ_B2G_RIL
|
||||
static const char* kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
|
||||
static const char* kSettingRilDefaultServiceId = "ril.data.defaultServiceId";
|
||||
#endif
|
||||
// Both of these settings can be toggled in the Gaia Developer settings screen.
|
||||
static const char* kSettingDebugEnabled = "geolocation.debugging.enabled";
|
||||
static const char* kSettingDebugGpsIgnored = "geolocation.debugging.gps-locations-ignored";
|
||||
|
@ -289,6 +293,10 @@ GonkGPSGeolocationProvider::GonkGPSGeolocationProvider()
|
|||
#ifdef MOZ_B2G_RIL
|
||||
, mSupportsMSB(false)
|
||||
, mSupportsMSA(false)
|
||||
, mRilDataServiceId(0)
|
||||
, mNumberOfRilServices(1)
|
||||
, mObservingNetworkConnStateChange(false)
|
||||
, mObservingSettingsChange(false)
|
||||
#endif
|
||||
, mSupportsSingleShot(false)
|
||||
, mSupportsTimeInjection(false)
|
||||
|
@ -714,17 +722,24 @@ GonkGPSGeolocationProvider::SetupAGPS()
|
|||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, kNetworkConnStateChangedTopic, false);
|
||||
}
|
||||
// Request RIL date service ID for correct RadioInterface object first due to
|
||||
// multi-SIM case needs it to handle AGPS related stuffs. For single SIM, 0
|
||||
// will be returned as default RIL data service ID.
|
||||
RequestSettingValue(kSettingRilDefaultServiceId);
|
||||
}
|
||||
|
||||
void
|
||||
GonkGPSGeolocationProvider::UpdateRadioInterface()
|
||||
{
|
||||
nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetService("@mozilla.org/ril;1");
|
||||
if (ril) {
|
||||
// TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
|
||||
// MultiSIM configuration
|
||||
ril->GetRadioInterface(0 /* clientId */, getter_AddRefs(mRadioInterface));
|
||||
}
|
||||
NS_ENSURE_TRUE_VOID(ril);
|
||||
ril->GetRadioInterface(mRilDataServiceId, getter_AddRefs(mRadioInterface));
|
||||
}
|
||||
|
||||
bool
|
||||
GonkGPSGeolocationProvider::IsValidRilServiceId(uint32_t aServiceId)
|
||||
{
|
||||
return aServiceId < mNumberOfRilServices;
|
||||
}
|
||||
#endif // MOZ_B2G_RIL
|
||||
|
||||
|
@ -848,9 +863,12 @@ GonkGPSGeolocationProvider::Startup()
|
|||
// Setup an observer to watch changes to the setting.
|
||||
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
||||
if (observerService) {
|
||||
MOZ_ASSERT(!mObservingSettingsChange);
|
||||
nsresult rv = observerService->AddObserver(this, kMozSettingsChangedTopic, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("geo: Gonk GPS AddObserver failed");
|
||||
} else {
|
||||
mObservingSettingsChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -872,6 +890,9 @@ GonkGPSGeolocationProvider::Startup()
|
|||
}
|
||||
|
||||
mStarted = true;
|
||||
#ifdef MOZ_B2G_RIL
|
||||
mNumberOfRilServices = Preferences::GetUint(kPrefRilNumRadioInterfaces, 1);
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -906,11 +927,15 @@ GonkGPSGeolocationProvider::Shutdown()
|
|||
rv = obs->RemoveObserver(this, kNetworkConnStateChangedTopic);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("geo: Gonk GPS network state RemoveObserver failed");
|
||||
} else {
|
||||
mObservingNetworkConnStateChange = false;
|
||||
}
|
||||
#endif
|
||||
rv = obs->RemoveObserver(this, kMozSettingsChangedTopic);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("geo: Gonk GPS mozsettings RemoveObserver failed");
|
||||
} else {
|
||||
mObservingSettingsChange = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1043,6 +1068,15 @@ GonkGPSGeolocationProvider::Observe(nsISupports* aSubject,
|
|||
gDebug_isLoggingEnabled =
|
||||
setting.mValue.isBoolean() ? setting.mValue.toBoolean() : false;
|
||||
return NS_OK;
|
||||
} else if (setting.mKey.EqualsASCII(kSettingRilDefaultServiceId)) {
|
||||
if (!setting.mValue.isNumber() ||
|
||||
!IsValidRilServiceId(setting.mValue.toNumber())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mRilDataServiceId = setting.mValue.toNumber();
|
||||
UpdateRadioInterface();
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1071,6 +1105,32 @@ GonkGPSGeolocationProvider::Handle(const nsAString& aName,
|
|||
SetAGpsDataConn(apn);
|
||||
}
|
||||
}
|
||||
} else if (aName.EqualsASCII(kSettingRilDefaultServiceId)) {
|
||||
uint32_t id = 0;
|
||||
JSContext *cx = nsContentUtils::GetCurrentJSContext();
|
||||
NS_ENSURE_TRUE(cx, NS_OK);
|
||||
if (!JS::ToUint32(cx, aResult, &id)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsValidRilServiceId(id)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mRilDataServiceId = id;
|
||||
UpdateRadioInterface();
|
||||
|
||||
MOZ_ASSERT(!mObservingNetworkConnStateChange);
|
||||
|
||||
// Now we know which service ID to deal with, observe necessary topic then
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obs, NS_OK);
|
||||
|
||||
if (NS_FAILED(obs->AddObserver(this, kNetworkConnStateChangedTopic, false))) {
|
||||
NS_WARNING("Failed to add network state changed observer!");
|
||||
} else {
|
||||
mObservingNetworkConnStateChange = true;
|
||||
}
|
||||
}
|
||||
#endif // MOZ_B2G_RIL
|
||||
return NS_OK;
|
||||
|
|
|
@ -82,6 +82,8 @@ private:
|
|||
void InjectLocation(double latitude, double longitude, float accuracy);
|
||||
void RequestSettingValue(const char* aKey);
|
||||
#ifdef MOZ_B2G_RIL
|
||||
void UpdateRadioInterface();
|
||||
bool IsValidRilServiceId(uint32_t aServiceId);
|
||||
void SetupAGPS();
|
||||
int32_t GetDataConnectionState();
|
||||
void SetAGpsDataConn(nsAString& aApn);
|
||||
|
@ -101,6 +103,13 @@ private:
|
|||
#ifdef MOZ_B2G_RIL
|
||||
bool mSupportsMSB;
|
||||
bool mSupportsMSA;
|
||||
uint32_t mRilDataServiceId;
|
||||
// mNumberOfRilServices indicates how many SIM slots supported on device, and
|
||||
// RadioInterfaceLayer.js takes responsibility to set up the corresponding
|
||||
// preference value.
|
||||
uint32_t mNumberOfRilServices;
|
||||
bool mObservingNetworkConnStateChange;
|
||||
bool mObservingSettingsChange;
|
||||
#endif
|
||||
bool mSupportsSingleShot;
|
||||
bool mSupportsTimeInjection;
|
||||
|
|
|
@ -2037,6 +2037,12 @@ public:
|
|||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (!mWorkerPrivate ||
|
||||
!mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats, aAnonymize)) {
|
||||
// Returning NS_OK here will effectively report 0 memory.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
path.AppendLiteral("explicit/workers/workers(");
|
||||
if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
|
||||
path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
|
||||
|
@ -2056,12 +2062,6 @@ public:
|
|||
path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
|
||||
|
||||
TryToMapAddon(path);
|
||||
|
||||
if (!mWorkerPrivate ||
|
||||
!mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats, aAnonymize)) {
|
||||
// Returning NS_OK here will effectively report 0 memory.
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, path,
|
||||
|
|
|
@ -39,32 +39,6 @@
|
|||
q: cite
|
||||
*/
|
||||
|
||||
/* Here is how to open a channel for testing
|
||||
(from embed/qa/testembed/Tests.cpp):
|
||||
|
||||
nsCOMPtr<nsIChannel> theChannel;
|
||||
nsCString uri;
|
||||
nsCOMPtr<nsIURI> theURI;
|
||||
rv = NS_NewURI(getter_AddRefs(theURI), theSpec);
|
||||
if (!theURI)
|
||||
error;
|
||||
rv = NS_OpenURI(getter_AddRefs(theChannel), theURI, nullptr, theLoadGroup);
|
||||
if (!theChannel)
|
||||
error;
|
||||
nsCOMPtr<nsILoadGroup> theLoadGroup(do_CreateInstance(NS_LOADGROUP_CONTRACTID));
|
||||
if (!theLoadGroup)
|
||||
error;
|
||||
nsCOMPtr<nsIStreamListener> listener(static_cast<nsIStreamListener*>(qaBrowserImpl));
|
||||
//nsCOMPtr<nsIWeakReference> thisListener(do_GetWeakReference(listener));
|
||||
//qaWebBrowser->AddWebBrowserListener(thisListener, NS_GET_IID(nsIStreamListener));
|
||||
|
||||
// this calls nsIStreamListener::OnDataAvailable()
|
||||
rv = theChannel->AsyncOpen(listener, nullptr);
|
||||
|
||||
nsCOMPtr<nsIRequest> theRequest = do_QueryInterface(theChannel);
|
||||
// Now we can do things on nsIRequest (like what?)
|
||||
*/
|
||||
|
||||
#include "nsHTMLURIRefObject.h"
|
||||
|
||||
#include "mozilla/mozalloc.h"
|
||||
|
|
|
@ -234,6 +234,7 @@ private:
|
|||
|
||||
DECL_GFX_PREF(Once, "image.cache.timeweight", ImageCacheTimeWeight, int32_t, 500);
|
||||
DECL_GFX_PREF(Once, "image.cache.size", ImageCacheSize, int32_t, 5*1024*1024);
|
||||
DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.high_quality_downscaling.enabled", ImageHQDownscalingEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.high_quality_downscaling.min_factor", ImageHQDownscalingMinFactor, uint32_t, 1000);
|
||||
DECL_GFX_PREF(Live, "image.high_quality_upscaling.max_size", ImageHQUpscalingMaxSize, uint32_t, 20971520);
|
||||
|
|
|
@ -638,12 +638,12 @@ nsICODecoder::NeedsNewFrame() const
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsICODecoder::AllocateFrame()
|
||||
nsICODecoder::AllocateFrame(const nsIntSize& aTargetSize /* = nsIntSize() */)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (mContainedDecoder) {
|
||||
rv = mContainedDecoder->AllocateFrame();
|
||||
rv = mContainedDecoder->AllocateFrame(aTargetSize);
|
||||
mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
|
||||
mProgress |= mContainedDecoder->TakeProgress();
|
||||
mInvalidRect.Union(mContainedDecoder->TakeInvalidRect());
|
||||
|
@ -652,7 +652,7 @@ nsICODecoder::AllocateFrame()
|
|||
|
||||
// Grab a strong ref that we'll later hand over to the contained decoder. This
|
||||
// lets us avoid creating a RawAccessFrameRef off-main-thread.
|
||||
rv = Decoder::AllocateFrame();
|
||||
rv = Decoder::AllocateFrame(aTargetSize);
|
||||
mRefForContainedDecoder = GetCurrentFrameRef();
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ public:
|
|||
|
||||
virtual void WriteInternal(const char* aBuffer, uint32_t aCount) MOZ_OVERRIDE;
|
||||
virtual void FinishInternal() MOZ_OVERRIDE;
|
||||
virtual nsresult AllocateFrame() MOZ_OVERRIDE;
|
||||
virtual nsresult AllocateFrame(const nsIntSize& aTargetSize
|
||||
/* = nsIntSize() */) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual bool NeedsNewFrame() const MOZ_OVERRIDE;
|
||||
|
|
|
@ -69,7 +69,7 @@ native nsIntSizeByVal(nsIntSize);
|
|||
*
|
||||
* Internally, imgIContainer also manages animation of images.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(14ea6fa5-183e-4409-ac88-110bd2e05292)]
|
||||
[scriptable, builtinclass, uuid(4adb4c92-284d-4f5a-85e7-e924ec57510f)]
|
||||
interface imgIContainer : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -345,11 +345,27 @@ interface imgIContainer : nsISupports
|
|||
void requestDecode();
|
||||
|
||||
/*
|
||||
* This is equivalent to requestDecode() but it also decodes some of the
|
||||
* image.
|
||||
* This is equivalent to requestDecode() but it also synchronously decodes
|
||||
* images that can be decoded "quickly" according to some heuristic.
|
||||
*/
|
||||
[noscript] void startDecoding();
|
||||
|
||||
/*
|
||||
* This method is equivalent to requestDecode(), but enables the caller to
|
||||
* provide more detailed information about the decode request.
|
||||
*
|
||||
* @param aSize The size to which the image should be scaled while decoding,
|
||||
* if possible. If the image cannot be scaled to this size while
|
||||
* being decoded, it will be decoded at its intrinsic size.
|
||||
* @param aFlags Flags of the FLAG_* variety. Only the decode flags
|
||||
* (FLAG_DECODE_*) and FLAG_SYNC_DECODE (which will
|
||||
* synchronously decode images that can be decoded "quickly",
|
||||
* just like startDecoding() does) are accepted; others will be
|
||||
* ignored.
|
||||
*/
|
||||
[noscript] void requestDecodeForSize([const] in nsIntSize aSize,
|
||||
in uint32_t aFlags);
|
||||
|
||||
/*
|
||||
* Returns true if no more decoding can be performed on this image. Images
|
||||
* with errors return true since they cannot be decoded any further. Note that
|
||||
|
|
|
@ -328,11 +328,18 @@ Decoder::FinishSharedDecoder()
|
|||
}
|
||||
|
||||
nsresult
|
||||
Decoder::AllocateFrame()
|
||||
Decoder::AllocateFrame(const nsIntSize& aTargetSize /* = nsIntSize() */)
|
||||
{
|
||||
MOZ_ASSERT(mNeedsNewFrame);
|
||||
|
||||
nsIntSize targetSize = aTargetSize;
|
||||
if (targetSize == nsIntSize()) {
|
||||
MOZ_ASSERT(HasSize());
|
||||
targetSize = mImageMetadata.GetSize();
|
||||
}
|
||||
|
||||
mCurrentFrame = EnsureFrame(mNewFrameData.mFrameNum,
|
||||
targetSize,
|
||||
mNewFrameData.mFrameRect,
|
||||
mDecodeFlags,
|
||||
mNewFrameData.mFormat,
|
||||
|
@ -366,6 +373,7 @@ Decoder::AllocateFrame()
|
|||
|
||||
RawAccessFrameRef
|
||||
Decoder::EnsureFrame(uint32_t aFrameNum,
|
||||
const nsIntSize& aTargetSize,
|
||||
const nsIntRect& aFrameRect,
|
||||
uint32_t aDecodeFlags,
|
||||
SurfaceFormat aFormat,
|
||||
|
@ -383,8 +391,8 @@ Decoder::EnsureFrame(uint32_t aFrameNum,
|
|||
|
||||
// Adding a frame that doesn't already exist. This is the normal case.
|
||||
if (aFrameNum == mFrameCount) {
|
||||
return InternalAddFrame(aFrameNum, aFrameRect, aDecodeFlags, aFormat,
|
||||
aPaletteDepth, aPreviousFrame);
|
||||
return InternalAddFrame(aFrameNum, aTargetSize, aFrameRect, aDecodeFlags,
|
||||
aFormat, aPaletteDepth, aPreviousFrame);
|
||||
}
|
||||
|
||||
// We're replacing a frame. It must be the first frame; there's no reason to
|
||||
|
@ -427,6 +435,7 @@ Decoder::EnsureFrame(uint32_t aFrameNum,
|
|||
|
||||
RawAccessFrameRef
|
||||
Decoder::InternalAddFrame(uint32_t aFrameNum,
|
||||
const nsIntSize& aTargetSize,
|
||||
const nsIntRect& aFrameRect,
|
||||
uint32_t aDecodeFlags,
|
||||
SurfaceFormat aFormat,
|
||||
|
@ -438,15 +447,13 @@ Decoder::InternalAddFrame(uint32_t aFrameNum,
|
|||
return RawAccessFrameRef();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mImageMetadata.HasSize());
|
||||
nsIntSize imageSize(mImageMetadata.GetWidth(), mImageMetadata.GetHeight());
|
||||
if (imageSize.width <= 0 || imageSize.height <= 0 ||
|
||||
if (aTargetSize.width <= 0 || aTargetSize.height <= 0 ||
|
||||
aFrameRect.width <= 0 || aFrameRect.height <= 0) {
|
||||
NS_WARNING("Trying to add frame with zero or negative size");
|
||||
return RawAccessFrameRef();
|
||||
}
|
||||
|
||||
if (!SurfaceCache::CanHold(imageSize.ToIntSize())) {
|
||||
if (!SurfaceCache::CanHold(aTargetSize.ToIntSize())) {
|
||||
NS_WARNING("Trying to add frame that's too large for the SurfaceCache");
|
||||
return RawAccessFrameRef();
|
||||
}
|
||||
|
@ -454,7 +461,7 @@ Decoder::InternalAddFrame(uint32_t aFrameNum,
|
|||
nsRefPtr<imgFrame> frame = new imgFrame();
|
||||
bool nonPremult =
|
||||
aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
|
||||
if (NS_FAILED(frame->InitForDecoder(imageSize, aFrameRect, aFormat,
|
||||
if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat,
|
||||
aPaletteDepth, nonPremult))) {
|
||||
NS_WARNING("imgFrame::Init should succeed");
|
||||
return RawAccessFrameRef();
|
||||
|
@ -468,7 +475,7 @@ Decoder::InternalAddFrame(uint32_t aFrameNum,
|
|||
|
||||
InsertOutcome outcome =
|
||||
SurfaceCache::Insert(frame, ImageKey(mImage.get()),
|
||||
RasterSurfaceKey(imageSize.ToIntSize(),
|
||||
RasterSurfaceKey(aTargetSize.ToIntSize(),
|
||||
aDecodeFlags,
|
||||
aFrameNum),
|
||||
Lifetime::Persistent);
|
||||
|
@ -599,12 +606,15 @@ Decoder::PostFrameStop(Opacity aFrameOpacity /* = Opacity::TRANSPARENT */,
|
|||
// If we're not sending partial invalidations, then we send an invalidation
|
||||
// here when the first frame is complete.
|
||||
if (!mSendPartialInvalidations && !mIsAnimated) {
|
||||
mInvalidRect.UnionRect(mInvalidRect, mCurrentFrame->GetRect());
|
||||
mInvalidRect.UnionRect(mInvalidRect,
|
||||
nsIntRect(nsIntPoint(0, 0), GetSize()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Decoder::PostInvalidation(nsIntRect& aRect)
|
||||
Decoder::PostInvalidation(const nsIntRect& aRect,
|
||||
const Maybe<nsIntRect>& aRectAtTargetSize
|
||||
/* = Nothing() */)
|
||||
{
|
||||
// We should be mid-frame
|
||||
NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
|
||||
|
@ -614,7 +624,7 @@ Decoder::PostInvalidation(nsIntRect& aRect)
|
|||
// or we're past the first frame.
|
||||
if (mSendPartialInvalidations && !mIsAnimated) {
|
||||
mInvalidRect.UnionRect(mInvalidRect, aRect);
|
||||
mCurrentFrame->ImageUpdated(aRect);
|
||||
mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,24 @@ public:
|
|||
mSizeDecode = aSizeDecode;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this decoder supports downscale-during-decode, sets the target size that
|
||||
* this image should be decoded to.
|
||||
*
|
||||
* If this decoder *doesn't* support downscale-during-decode, returns
|
||||
* NS_ERROR_NOT_AVAILABLE. If the provided size is unacceptable, returns
|
||||
* another error.
|
||||
*
|
||||
* Returning NS_OK from this method is a promise that the decoder will decode
|
||||
* the image to the requested target size unless it encounters an error.
|
||||
*
|
||||
* This must be called before Init() is called.
|
||||
*/
|
||||
virtual nsresult SetTargetSize(const nsIntSize& aSize)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether should send partial invalidations.
|
||||
*
|
||||
|
@ -233,6 +251,12 @@ public:
|
|||
PostSize(aSize.width, aSize.height, aOrientation);
|
||||
}
|
||||
|
||||
nsIntSize GetSize() const
|
||||
{
|
||||
MOZ_ASSERT(HasSize());
|
||||
return mImageMetadata.GetSize();
|
||||
}
|
||||
|
||||
// Use HistogramCount as an invalid Histogram ID
|
||||
virtual Telemetry::ID SpeedHistogram() { return Telemetry::HistogramCount; }
|
||||
|
||||
|
@ -259,7 +283,7 @@ public:
|
|||
|
||||
// Try to allocate a frame as described in mNewFrameData and return the
|
||||
// status code from that attempt. Clears mNewFrameData.
|
||||
virtual nsresult AllocateFrame();
|
||||
virtual nsresult AllocateFrame(const nsIntSize& aTargetSize = nsIntSize());
|
||||
|
||||
already_AddRefed<imgFrame> GetCurrentFrame()
|
||||
{
|
||||
|
@ -332,9 +356,19 @@ protected:
|
|||
int32_t aTimeout = 0,
|
||||
BlendMethod aBlendMethod = BlendMethod::OVER);
|
||||
|
||||
// Called by the decoders when they have a region to invalidate. We may not
|
||||
// actually pass these invalidations on right away.
|
||||
void PostInvalidation(nsIntRect& aRect);
|
||||
/**
|
||||
* Called by the decoders when they have a region to invalidate. We may not
|
||||
* actually pass these invalidations on right away.
|
||||
*
|
||||
* @param aRect The invalidation rect in the coordinate system of the unscaled
|
||||
* image (that is, the image at its intrinsic size).
|
||||
* @param aRectAtTargetSize If not Nothing(), the invalidation rect in the
|
||||
* coordinate system of the scaled image (that is,
|
||||
* the image at our target decoding size). This must
|
||||
* be supplied if we're downscaling during decode.
|
||||
*/
|
||||
void PostInvalidation(const nsIntRect& aRect,
|
||||
const Maybe<nsIntRect>& aRectAtTargetSize = Nothing());
|
||||
|
||||
// Called by the decoders when they have successfully decoded the image. This
|
||||
// may occur as the result of the decoder getting to the appropriate point in
|
||||
|
@ -361,9 +395,14 @@ protected:
|
|||
* It is not possible to create sparse frame arrays; you can only append
|
||||
* frames to the current frame array, or if there is only one frame in the
|
||||
* array, replace that frame.
|
||||
* @aTargetSize specifies the target size we're decoding to. If we're not
|
||||
* downscaling during decode, this will always be the same as the image's
|
||||
* intrinsic size.
|
||||
*
|
||||
* If a non-paletted frame is desired, pass 0 for aPaletteDepth.
|
||||
*/
|
||||
RawAccessFrameRef EnsureFrame(uint32_t aFrameNum,
|
||||
const nsIntSize& aTargetSize,
|
||||
const nsIntRect& aFrameRect,
|
||||
uint32_t aDecodeFlags,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
|
@ -371,6 +410,7 @@ protected:
|
|||
imgFrame* aPreviousFrame);
|
||||
|
||||
RawAccessFrameRef InternalAddFrame(uint32_t aFrameNum,
|
||||
const nsIntSize& aTargetSize,
|
||||
const nsIntRect& aFrameRect,
|
||||
uint32_t aDecodeFlags,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
|
|
|
@ -255,6 +255,12 @@ DynamicImage::StartDecoding()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DynamicImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
DynamicImage::IsDecoded()
|
||||
{
|
||||
|
|
|
@ -52,11 +52,16 @@ public:
|
|||
* multipart/x-mixed-replace image parts fall into this category.) If this
|
||||
* flag is set, INIT_FLAG_DISCARDABLE and INIT_FLAG_DECODE_ON_DRAW must not be
|
||||
* set.
|
||||
*
|
||||
* INIT_FLAG_DOWNSCALE_DURING_DECODE: The container should attempt to
|
||||
* downscale images during decoding instead of decoding them to their
|
||||
* intrinsic size.
|
||||
*/
|
||||
static const uint32_t INIT_FLAG_NONE = 0x0;
|
||||
static const uint32_t INIT_FLAG_DISCARDABLE = 0x1;
|
||||
static const uint32_t INIT_FLAG_DECODE_ON_DRAW = 0x2;
|
||||
static const uint32_t INIT_FLAG_TRANSIENT = 0x4;
|
||||
static const uint32_t INIT_FLAG_DOWNSCALE_DURING_DECODE = 0x8;
|
||||
|
||||
/**
|
||||
* Creates a new image container.
|
||||
|
|
|
@ -31,14 +31,22 @@ namespace image {
|
|||
ImageFactory::Initialize()
|
||||
{ }
|
||||
|
||||
static bool
|
||||
ShouldDownscaleDuringDecode(const nsCString& aMimeType)
|
||||
{
|
||||
// Not enabled for anything yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
ComputeImageFlags(ImageURL* uri, bool isMultiPart)
|
||||
ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// We default to the static globals.
|
||||
bool isDiscardable = gfxPrefs::ImageMemDiscardable();
|
||||
bool doDecodeOnDraw = gfxPrefs::ImageMemDecodeOnDraw();
|
||||
bool doDownscaleDuringDecode = gfxPrefs::ImageDownscaleDuringDecodeEnabled();
|
||||
|
||||
// We want UI to be as snappy as possible and not to flicker. Disable
|
||||
// discarding and decode-on-draw for chrome URLS.
|
||||
|
@ -56,10 +64,15 @@ ComputeImageFlags(ImageURL* uri, bool isMultiPart)
|
|||
isDiscardable = doDecodeOnDraw = false;
|
||||
}
|
||||
|
||||
// Downscale-during-decode is only enabled for certain content types.
|
||||
if (doDownscaleDuringDecode && !ShouldDownscaleDuringDecode(aMimeType)) {
|
||||
doDownscaleDuringDecode = false;
|
||||
}
|
||||
|
||||
// For multipart/x-mixed-replace, we basically want a direct channel to the
|
||||
// decoder. Disable both for this case as well.
|
||||
// decoder. Disable everything for this case.
|
||||
if (isMultiPart) {
|
||||
isDiscardable = doDecodeOnDraw = false;
|
||||
isDiscardable = doDecodeOnDraw = doDownscaleDuringDecode = false;
|
||||
}
|
||||
|
||||
// We have all the information we need.
|
||||
|
@ -73,6 +86,9 @@ ComputeImageFlags(ImageURL* uri, bool isMultiPart)
|
|||
if (isMultiPart) {
|
||||
imageFlags |= Image::INIT_FLAG_TRANSIENT;
|
||||
}
|
||||
if (doDownscaleDuringDecode) {
|
||||
imageFlags |= Image::INIT_FLAG_DOWNSCALE_DURING_DECODE;
|
||||
}
|
||||
|
||||
return imageFlags;
|
||||
}
|
||||
|
@ -89,7 +105,7 @@ ImageFactory::CreateImage(nsIRequest* aRequest,
|
|||
"Pref observers should have been initialized already");
|
||||
|
||||
// Compute the image's initialization flags.
|
||||
uint32_t imageFlags = ComputeImageFlags(aURI, aIsMultiPart);
|
||||
uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart);
|
||||
|
||||
// Select the type of image to create based on MIME type.
|
||||
if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) {
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
|
||||
int32_t GetWidth() const { return mSize->width; }
|
||||
int32_t GetHeight() const { return mSize->height; }
|
||||
nsIntSize GetSize() const { return *mSize; }
|
||||
Orientation GetOrientation() const { return *mOrientation; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -224,6 +224,12 @@ ImageWrapper::StartDecoding()
|
|||
return mInnerImage->StartDecoding();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ImageWrapper::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
|
||||
{
|
||||
return mInnerImage->RequestDecodeForSize(aSize, aFlags);
|
||||
}
|
||||
|
||||
bool
|
||||
ImageWrapper::IsDecoded()
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "gfxContext.h"
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
@ -315,14 +316,21 @@ RasterImage::Init(const char* aMimeType,
|
|||
// transient images.
|
||||
MOZ_ASSERT(!(aFlags & INIT_FLAG_TRANSIENT) ||
|
||||
(!(aFlags & INIT_FLAG_DISCARDABLE) &&
|
||||
!(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
|
||||
"Transient images can't be discardable or decode-on-draw");
|
||||
!(aFlags & INIT_FLAG_DECODE_ON_DRAW) &&
|
||||
!(aFlags & INIT_FLAG_DOWNSCALE_DURING_DECODE)),
|
||||
"Illegal init flags for transient image");
|
||||
|
||||
// Store initialization data
|
||||
mSourceDataMimeType.Assign(aMimeType);
|
||||
mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
|
||||
mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
|
||||
mTransient = !!(aFlags & INIT_FLAG_TRANSIENT);
|
||||
mDownscaleDuringDecode = !!(aFlags & INIT_FLAG_DOWNSCALE_DURING_DECODE);
|
||||
|
||||
#ifndef MOZ_ENABLE_SKIA
|
||||
// Downscale-during-decode requires Skia.
|
||||
mDownscaleDuringDecode = false;
|
||||
#endif
|
||||
|
||||
// Lock this image's surfaces in the SurfaceCache if we're not discardable.
|
||||
if (!mDiscardable) {
|
||||
|
@ -330,8 +338,7 @@ RasterImage::Init(const char* aMimeType,
|
|||
}
|
||||
|
||||
// Create the initial size decoder.
|
||||
nsresult rv = Decode(DecodeStrategy::ASYNC, DECODE_FLAGS_DEFAULT,
|
||||
/* aDoSizeDecode = */ true);
|
||||
nsresult rv = Decode(DecodeStrategy::ASYNC, Nothing(), DECODE_FLAGS_DEFAULT);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -463,7 +470,7 @@ RasterImage::GetType()
|
|||
|
||||
DrawableFrameRef
|
||||
RasterImage::LookupFrameInternal(uint32_t aFrameNum,
|
||||
const nsIntSize& aSize,
|
||||
const IntSize& aSize,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
if (!mAnim) {
|
||||
|
@ -478,10 +485,30 @@ RasterImage::LookupFrameInternal(uint32_t aFrameNum,
|
|||
return mAnim->GetCompositedFrame(aFrameNum);
|
||||
}
|
||||
|
||||
Maybe<uint32_t> alternateFlags;
|
||||
if (IsOpaque()) {
|
||||
// If we're opaque, we can always substitute a frame that was decoded with a
|
||||
// different decode flag for premultiplied alpha, because that can only
|
||||
// matter for frames with transparency.
|
||||
alternateFlags = Some(aFlags ^ FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
|
||||
}
|
||||
|
||||
// We don't want any substitution for sync decodes (except the premultiplied
|
||||
// alpha optimization above), so we use SurfaceCache::Lookup in this case.
|
||||
if (aFlags & FLAG_SYNC_DECODE) {
|
||||
return SurfaceCache::Lookup(ImageKey(this),
|
||||
RasterSurfaceKey(aSize.ToIntSize(),
|
||||
RasterSurfaceKey(aSize,
|
||||
DecodeFlags(aFlags),
|
||||
aFrameNum));
|
||||
aFrameNum),
|
||||
alternateFlags);
|
||||
}
|
||||
|
||||
// We'll return the best match we can find to the requested frame.
|
||||
return SurfaceCache::LookupBestMatch(ImageKey(this),
|
||||
RasterSurfaceKey(aSize,
|
||||
DecodeFlags(aFlags),
|
||||
aFrameNum),
|
||||
alternateFlags);
|
||||
}
|
||||
|
||||
DrawableFrameRef
|
||||
|
@ -492,31 +519,33 @@ RasterImage::LookupFrame(uint32_t aFrameNum,
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
DrawableFrameRef ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
|
||||
IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags)
|
||||
? aSize.ToIntSize()
|
||||
: mSize.ToIntSize();
|
||||
|
||||
if (!ref && IsOpaque() && aFrameNum == 0) {
|
||||
// We can use non-premultiplied alpha frames when premultipled alpha is
|
||||
// requested, or vice versa, if this image is opaque. Try again with the bit
|
||||
// toggled.
|
||||
ref = LookupFrameInternal(aFrameNum, aSize,
|
||||
aFlags ^ FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
|
||||
DrawableFrameRef ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
|
||||
|
||||
if (!ref && !mHasSize) {
|
||||
// We can't request a decode without knowing our intrinsic size. Give up.
|
||||
return DrawableFrameRef();
|
||||
}
|
||||
|
||||
if (!ref) {
|
||||
if (!ref || ref->GetImageSize() != requestedSize) {
|
||||
// The OS threw this frame away. We need to redecode if we can.
|
||||
MOZ_ASSERT(!mAnim, "Animated frames should be locked");
|
||||
|
||||
WantDecodedFrames(aFlags, aShouldSyncNotify);
|
||||
WantDecodedFrames(ThebesIntSize(requestedSize), aFlags, aShouldSyncNotify);
|
||||
|
||||
// If we were able to sync decode, we should already have the frame. If we
|
||||
// had to decode asynchronously, maybe we've gotten lucky.
|
||||
ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
|
||||
// If we can sync decode, we should already have the frame.
|
||||
if ((aFlags & FLAG_SYNC_DECODE) && aShouldSyncNotify) {
|
||||
ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref) {
|
||||
// We didn't successfully redecode, so just fail.
|
||||
// We still weren't able to get a frame. Give up.
|
||||
return DrawableFrameRef();
|
||||
}
|
||||
}
|
||||
|
||||
if (ref->GetCompositingFailed()) {
|
||||
return DrawableFrameRef();
|
||||
|
@ -1122,8 +1151,7 @@ RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus,
|
|||
// We need to guarantee that we've gotten the image's size, or at least
|
||||
// determined that we won't be able to get it, before we deliver the load
|
||||
// event. That means we have to do a synchronous size decode here.
|
||||
Decode(DecodeStrategy::SYNC_IF_POSSIBLE, DECODE_FLAGS_DEFAULT,
|
||||
/* aDoSizeDecode = */ true);
|
||||
Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Nothing(), DECODE_FLAGS_DEFAULT);
|
||||
}
|
||||
|
||||
// Determine our final status, giving precedence to Necko failure codes. We
|
||||
|
@ -1249,13 +1277,16 @@ RasterImage::CanDiscard() {
|
|||
|
||||
// Sets up a decoder for this image.
|
||||
already_AddRefed<Decoder>
|
||||
RasterImage::CreateDecoder(bool aDoSizeDecode, uint32_t aFlags)
|
||||
RasterImage::CreateDecoder(const Maybe<nsIntSize>& aSize, uint32_t aFlags)
|
||||
{
|
||||
// Make sure we actually get size before doing a full decode.
|
||||
if (aDoSizeDecode) {
|
||||
MOZ_ASSERT(!mHasSize, "Should not do unnecessary size decodes");
|
||||
} else {
|
||||
if (aSize) {
|
||||
MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!");
|
||||
MOZ_ASSERT(mDownscaleDuringDecode || *aSize == mSize,
|
||||
"Can only decode to our intrinsic size if we're not allowed to "
|
||||
"downscale-during-decode");
|
||||
} else {
|
||||
MOZ_ASSERT(!mHasSize, "Should not do unnecessary size decodes");
|
||||
}
|
||||
|
||||
// Figure out which decoder we want.
|
||||
|
@ -1296,27 +1327,38 @@ RasterImage::CreateDecoder(bool aDoSizeDecode, uint32_t aFlags)
|
|||
MOZ_ASSERT(decoder, "Should have a decoder now");
|
||||
|
||||
// Initialize the decoder.
|
||||
decoder->SetSizeDecode(aDoSizeDecode);
|
||||
decoder->SetSizeDecode(!aSize);
|
||||
decoder->SetSendPartialInvalidations(!mHasBeenDecoded);
|
||||
decoder->SetImageIsTransient(mTransient);
|
||||
decoder->SetDecodeFlags(DecodeFlags(aFlags));
|
||||
if (!aDoSizeDecode) {
|
||||
if (aSize) {
|
||||
// We already have the size; tell the decoder so it can preallocate a
|
||||
// frame. By default, we create an ARGB frame with no offset. If decoders
|
||||
// need a different type, they need to ask for it themselves.
|
||||
// XXX(seth): Note that we call SetSize() and NeedNewFrame() with the
|
||||
// image's intrinsic size, but AllocateFrame with our target size.
|
||||
decoder->SetSize(mSize, mOrientation);
|
||||
decoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
|
||||
decoder->NeedNewFrame(0, 0, 0, aSize->width, aSize->height,
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
decoder->AllocateFrame();
|
||||
decoder->AllocateFrame(*aSize);
|
||||
}
|
||||
decoder->SetIterator(mSourceBuffer->Iterator());
|
||||
|
||||
// Set a target size for downscale-during-decode if applicable.
|
||||
if (mDownscaleDuringDecode && aSize && *aSize != mSize) {
|
||||
DebugOnly<nsresult> rv = decoder->SetTargetSize(*aSize);
|
||||
MOZ_ASSERT(nsresult(rv) != NS_ERROR_NOT_AVAILABLE,
|
||||
"We're downscale-during-decode but decoder doesn't support it?");
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
|
||||
}
|
||||
|
||||
decoder->Init();
|
||||
|
||||
if (NS_FAILED(decoder->GetDecoderError())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!aDoSizeDecode) {
|
||||
if (!aSize) {
|
||||
Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
|
||||
mDecodeCount++;
|
||||
Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
|
||||
|
@ -1336,12 +1378,13 @@ RasterImage::CreateDecoder(bool aDoSizeDecode, uint32_t aFlags)
|
|||
}
|
||||
|
||||
void
|
||||
RasterImage::WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify)
|
||||
RasterImage::WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
|
||||
bool aShouldSyncNotify)
|
||||
{
|
||||
if (aShouldSyncNotify) {
|
||||
// We can sync notify, which means we can also sync decode.
|
||||
if (aFlags & FLAG_SYNC_DECODE) {
|
||||
Decode(DecodeStrategy::SYNC_IF_POSSIBLE, aFlags);
|
||||
Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Some(aSize), aFlags);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1349,12 +1392,12 @@ RasterImage::WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify)
|
|||
// case that we're redecoding an image (see bug 845147).
|
||||
Decode(mHasBeenDecoded ? DecodeStrategy::ASYNC
|
||||
: DecodeStrategy::SYNC_FOR_SMALL_IMAGES,
|
||||
aFlags);
|
||||
Some(aSize), aFlags);
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't sync notify, so do an async decode.
|
||||
Decode(DecodeStrategy::ASYNC, aFlags);
|
||||
Decode(DecodeStrategy::ASYNC, Some(aSize), aFlags);
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
|
@ -1362,24 +1405,7 @@ RasterImage::WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify)
|
|||
NS_IMETHODIMP
|
||||
RasterImage::RequestDecode()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mError) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!mHasSize) {
|
||||
mWantFullDecode = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Look up the first frame of the image, which will implicitly start decoding
|
||||
// if it's not available right now.
|
||||
// XXX(seth): Passing false for aShouldSyncNotify here has the effect of
|
||||
// decoding asynchronously, but that's not obvious from the argument name.
|
||||
// This API needs to be reworked.
|
||||
LookupFrame(0, mSize, DECODE_FLAGS_DEFAULT, /* aShouldSyncNotify = */ false);
|
||||
|
||||
return NS_OK;
|
||||
return RequestDecodeForSize(mSize, DECODE_FLAGS_DEFAULT);
|
||||
}
|
||||
|
||||
/* void startDecode() */
|
||||
|
@ -1391,20 +1417,38 @@ RasterImage::StartDecoding()
|
|||
NS_NewRunnableMethod(this, &RasterImage::StartDecoding));
|
||||
}
|
||||
|
||||
return RequestDecodeForSize(mSize, FLAG_SYNC_DECODE);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RasterImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mError) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mHasSize) {
|
||||
mWantFullDecode = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Fall back to our intrinsic size if we don't support
|
||||
// downscale-during-decode.
|
||||
nsIntSize targetSize = mDownscaleDuringDecode ? aSize : mSize;
|
||||
|
||||
// Sync decode small images if requested.
|
||||
bool shouldSyncDecodeSmallImages = aFlags & FLAG_SYNC_DECODE;
|
||||
|
||||
// Look up the first frame of the image, which will implicitly start decoding
|
||||
// if it's not available right now.
|
||||
// XXX(seth): Passing true for aShouldSyncNotify here has the effect of
|
||||
// synchronously decoding small images, but that's not obvious from the
|
||||
// argument name. This API needs to be reworked.
|
||||
LookupFrame(0, mSize, DECODE_FLAGS_DEFAULT, /* aShouldSyncNotify = */ true);
|
||||
// synchronously decoding small images, while passing false has the effect of
|
||||
// decoding asynchronously, but that's not obvious from the argument name.
|
||||
// This API needs to be reworked.
|
||||
LookupFrame(0, targetSize, DecodeFlags(aFlags),
|
||||
/* aShouldSyncNotify = */ shouldSyncDecodeSmallImages);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1418,29 +1462,30 @@ RasterImage::IsDecoded()
|
|||
|
||||
NS_IMETHODIMP
|
||||
RasterImage::Decode(DecodeStrategy aStrategy,
|
||||
uint32_t aFlags,
|
||||
bool aDoSizeDecode /* = false */)
|
||||
const Maybe<nsIntSize>& aSize,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
MOZ_ASSERT(aDoSizeDecode || NS_IsMainThread());
|
||||
MOZ_ASSERT(!aSize || NS_IsMainThread());
|
||||
|
||||
if (mError) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// If we don't have a size yet, we can't do any other decoding.
|
||||
if (!mHasSize && !aDoSizeDecode) {
|
||||
if (!mHasSize && aSize) {
|
||||
mWantFullDecode = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Create a decoder.
|
||||
nsRefPtr<Decoder> decoder = CreateDecoder(aDoSizeDecode, aFlags);
|
||||
nsRefPtr<Decoder> decoder = CreateDecoder(aSize, aFlags);
|
||||
if (!decoder) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!aDoSizeDecode) {
|
||||
// Send out early notifications right away.
|
||||
if (aSize) {
|
||||
// This isn't a size decode (which doesn't send any early notifications), so
|
||||
// send out notifications right away.
|
||||
NotifyProgress(decoder->TakeProgress(),
|
||||
decoder->TakeInvalidRect(),
|
||||
decoder->GetDecodeFlags());
|
||||
|
@ -1495,6 +1540,11 @@ RasterImage::CanScale(GraphicsFilter aFilter,
|
|||
return false;
|
||||
}
|
||||
|
||||
// We don't HQ scale images that we can downscale during decode.
|
||||
if (mDownscaleDuringDecode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't use the scaler for animated or transient images to avoid doing a
|
||||
// bunch of work on an image that just gets thrown away.
|
||||
if (mAnim || mTransient) {
|
||||
|
@ -1530,6 +1580,40 @@ RasterImage::CanScale(GraphicsFilter aFilter,
|
|||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
RasterImage::CanDownscaleDuringDecode(const nsIntSize& aSize, uint32_t aFlags)
|
||||
{
|
||||
// Check basic requirements: downscale-during-decode is enabled for this
|
||||
// image, we have all the source data and know our size, the flags allow us to
|
||||
// do it, and a 'good' filter is being used.
|
||||
if (!mDownscaleDuringDecode || !mHasSize ||
|
||||
!(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't downscale animated images during decode.
|
||||
if (mAnim) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Never upscale.
|
||||
if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Zero or negative width or height is unacceptable.
|
||||
if (aSize.width < 1 || aSize.height < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// There's no point in scaling if we can't store the result.
|
||||
if (!SurfaceCache::CanHold(aSize.ToIntSize())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::NotifyNewScaledFrame()
|
||||
{
|
||||
|
@ -1654,13 +1738,14 @@ RasterImage::Draw(gfxContext* aContext,
|
|||
mProgressTracker->OnUnlockedDraw();
|
||||
}
|
||||
|
||||
// XXX(seth): For now, we deliberately don't look up a frame of size aSize
|
||||
// (though DrawWithPreDownscaleIfNeeded will do so later). It doesn't make
|
||||
// sense to do so until we support downscale-during-decode. Right now we need
|
||||
// to make sure that we always touch an mSize-sized frame so that we have
|
||||
// something to HQ scale.
|
||||
// If we're not using GraphicsFilter::FILTER_GOOD, we shouldn't high-quality
|
||||
// scale or downscale during decode.
|
||||
uint32_t flags = aFilter == GraphicsFilter::FILTER_GOOD
|
||||
? aFlags
|
||||
: aFlags & ~FLAG_HIGH_QUALITY_SCALING;
|
||||
|
||||
DrawableFrameRef ref =
|
||||
LookupFrame(GetRequestedFrameIndex(aWhichFrame), mSize, aFlags);
|
||||
LookupFrame(GetRequestedFrameIndex(aWhichFrame), aSize, flags);
|
||||
if (!ref) {
|
||||
// Getting the frame (above) touches the image and kicks off decoding.
|
||||
if (mDrawStartTime.IsNull()) {
|
||||
|
@ -1673,7 +1758,7 @@ RasterImage::Draw(gfxContext* aContext,
|
|||
ref->IsImageComplete();
|
||||
|
||||
DrawWithPreDownscaleIfNeeded(Move(ref), aContext, aSize,
|
||||
aRegion, aFilter, aFlags);
|
||||
aRegion, aFilter, flags);
|
||||
|
||||
if (shouldRecordTelemetry) {
|
||||
TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
|
||||
|
@ -1938,7 +2023,10 @@ RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
|
|||
|
||||
nsIntSize destSize(ceil(aDest.width), ceil(aDest.height));
|
||||
|
||||
if (CanScale(aFilter, destSize, aFlags)) {
|
||||
if (aFilter == GraphicsFilter::FILTER_GOOD &&
|
||||
CanDownscaleDuringDecode(destSize, aFlags)) {
|
||||
return destSize;
|
||||
} else if (CanScale(aFilter, destSize, aFlags)) {
|
||||
DrawableFrameRef frameRef =
|
||||
SurfaceCache::Lookup(ImageKey(this),
|
||||
RasterSurfaceKey(destSize.ToIntSize(),
|
||||
|
|
|
@ -296,7 +296,7 @@ private:
|
|||
bool aShouldSyncNotify = true);
|
||||
|
||||
DrawableFrameRef LookupFrameInternal(uint32_t aFrameNum,
|
||||
const nsIntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
uint32_t aFlags);
|
||||
DrawableFrameRef LookupFrame(uint32_t aFrameNum,
|
||||
const nsIntSize& aSize,
|
||||
|
@ -325,12 +325,26 @@ private:
|
|||
// Decoding.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
already_AddRefed<Decoder> CreateDecoder(bool aDoSizeDecode, uint32_t aFlags);
|
||||
/**
|
||||
* Creates and runs a decoder, either synchronously or asynchronously
|
||||
* according to @aStrategy. Passes the provided target size @aSize and decode
|
||||
* flags @aFlags to CreateDecoder. If a size decode is desired, pass Nothing
|
||||
* for @aSize.
|
||||
*/
|
||||
NS_IMETHOD Decode(DecodeStrategy aStrategy,
|
||||
const Maybe<nsIntSize>& aSize,
|
||||
uint32_t aFlags);
|
||||
|
||||
void WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
|
||||
/**
|
||||
* Creates a new decoder with a target size of @aSize and decode flags
|
||||
* specified by @aFlags. If a size decode is desired, pass Nothing() for
|
||||
* @aSize.
|
||||
*/
|
||||
already_AddRefed<Decoder> CreateDecoder(const Maybe<nsIntSize>& aSize,
|
||||
uint32_t aFlags);
|
||||
|
||||
NS_IMETHOD Decode(DecodeStrategy aStrategy, uint32_t aFlags,
|
||||
bool aDoSizeDecode = false);
|
||||
void WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
|
||||
bool aShouldSyncNotify);
|
||||
|
||||
private: // data
|
||||
nsIntSize mSize;
|
||||
|
@ -378,6 +392,7 @@ private: // data
|
|||
bool mDiscardable:1; // Is container discardable?
|
||||
bool mHasSourceData:1; // Do we have source data?
|
||||
bool mHasBeenDecoded:1; // Decoded at least once?
|
||||
bool mDownscaleDuringDecode:1;
|
||||
|
||||
// Whether we're waiting to start animation. If we get a StartAnimation() call
|
||||
// but we don't yet have more than one frame, mPendingAnimation is set so that
|
||||
|
@ -408,6 +423,9 @@ private: // data
|
|||
// Determines whether we can perform an HQ scale with the given parameters.
|
||||
bool CanScale(GraphicsFilter aFilter, const nsIntSize& aSize, uint32_t aFlags);
|
||||
|
||||
// Determines whether we can downscale during decode with the given parameters.
|
||||
bool CanDownscaleDuringDecode(const nsIntSize& aSize, uint32_t aFlags);
|
||||
|
||||
// Called by the HQ scaler when a new scaled frame is ready.
|
||||
void NotifyNewScaledFrame();
|
||||
|
||||
|
|
|
@ -248,6 +248,23 @@ public:
|
|||
return surface.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<CachedSurface>
|
||||
LookupBestMatch(const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<uint32_t>& aAlternateFlags)
|
||||
{
|
||||
// Try for a perfect match first.
|
||||
nsRefPtr<CachedSurface> surface;
|
||||
mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface));
|
||||
if (surface) {
|
||||
return surface.forget();
|
||||
}
|
||||
|
||||
// There's no perfect match, so find the best match we can.
|
||||
MatchContext matchContext(aSurfaceKey, aAlternateFlags);
|
||||
ForEach(TryToImproveMatch, &matchContext);
|
||||
return matchContext.mBestMatch.forget();
|
||||
}
|
||||
|
||||
void ForEach(SurfaceTable::EnumReadFunction aFunction, void* aData)
|
||||
{
|
||||
mSurfaces.EnumerateRead(aFunction, aData);
|
||||
|
@ -257,6 +274,75 @@ public:
|
|||
bool IsLocked() const { return mLocked; }
|
||||
|
||||
private:
|
||||
struct MatchContext
|
||||
{
|
||||
MatchContext(const SurfaceKey& aIdealKey,
|
||||
const Maybe<uint32_t>& aAlternateFlags)
|
||||
: mIdealKey(aIdealKey)
|
||||
, mAlternateFlags(aAlternateFlags)
|
||||
{ }
|
||||
|
||||
const SurfaceKey& mIdealKey;
|
||||
const Maybe<uint32_t> mAlternateFlags;
|
||||
nsRefPtr<CachedSurface> mBestMatch;
|
||||
};
|
||||
|
||||
static PLDHashOperator TryToImproveMatch(const SurfaceKey& aSurfaceKey,
|
||||
CachedSurface* aSurface,
|
||||
void* aContext)
|
||||
{
|
||||
auto context = static_cast<MatchContext*>(aContext);
|
||||
const SurfaceKey& idealKey = context->mIdealKey;
|
||||
|
||||
// Matching the animation time and SVG context is required.
|
||||
if (aSurfaceKey.AnimationTime() != idealKey.AnimationTime() ||
|
||||
aSurfaceKey.SVGContext() != idealKey.SVGContext()) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Matching the flags is required, but we can match the alternate flags as
|
||||
// well if some were provided.
|
||||
if (aSurfaceKey.Flags() != idealKey.Flags() &&
|
||||
Some(aSurfaceKey.Flags()) != context->mAlternateFlags) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Anything is better than nothing! (Within the constraints we just
|
||||
// checked, of course.)
|
||||
if (!context->mBestMatch) {
|
||||
context->mBestMatch = aSurface;
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(context->mBestMatch, "Should have a current best match");
|
||||
SurfaceKey bestMatchKey = context->mBestMatch->GetSurfaceKey();
|
||||
|
||||
// Compare sizes. We use an area-based heuristic here instead of computing a
|
||||
// truly optimal answer, since it seems very unlikely to make a difference
|
||||
// for realistic sizes.
|
||||
int64_t idealArea = idealKey.Size().width * idealKey.Size().height;
|
||||
int64_t surfaceArea = aSurfaceKey.Size().width * aSurfaceKey.Size().height;
|
||||
int64_t bestMatchArea =
|
||||
bestMatchKey.Size().width * bestMatchKey.Size().height;
|
||||
|
||||
// If the best match is smaller than the ideal size, prefer bigger sizes.
|
||||
if (bestMatchArea < idealArea) {
|
||||
if (surfaceArea > bestMatchArea) {
|
||||
context->mBestMatch = aSurface;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Other, prefer sizes closer to the ideal size, but still not smaller.
|
||||
if (idealArea <= surfaceArea && surfaceArea < bestMatchArea) {
|
||||
context->mBestMatch = aSurface;
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// This surface isn't an improvement over the current best match.
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
SurfaceTable mSurfaces;
|
||||
bool mLocked;
|
||||
};
|
||||
|
@ -453,6 +539,45 @@ public:
|
|||
return ref;
|
||||
}
|
||||
|
||||
DrawableFrameRef LookupBestMatch(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<uint32_t>& aAlternateFlags)
|
||||
{
|
||||
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
|
||||
if (!cache)
|
||||
return DrawableFrameRef(); // No cached surfaces for this image.
|
||||
|
||||
// Repeatedly look up the best match, trying again if the resulting surface
|
||||
// has been freed by the operating system, until we can either lock a
|
||||
// surface for drawing or there are no matching surfaces left.
|
||||
// XXX(seth): This is O(N^2), but N is expected to be very small. If we
|
||||
// encounter a performance problem here we can revisit this.
|
||||
|
||||
nsRefPtr<CachedSurface> surface;
|
||||
DrawableFrameRef ref;
|
||||
while (true) {
|
||||
surface = cache->LookupBestMatch(aSurfaceKey, aAlternateFlags);
|
||||
if (!surface) {
|
||||
return DrawableFrameRef(); // Lookup in the per-image cache missed.
|
||||
}
|
||||
|
||||
ref = surface->DrawableRef();
|
||||
if (ref) {
|
||||
break;
|
||||
}
|
||||
|
||||
// The surface was released by the operating system. Remove the cache
|
||||
// entry as well.
|
||||
Remove(surface);
|
||||
}
|
||||
|
||||
if (!surface->IsLocked()) {
|
||||
mExpirationTracker.MarkUsed(surface);
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
void RemoveSurface(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
|
@ -788,14 +913,36 @@ SurfaceCache::Shutdown()
|
|||
|
||||
/* static */ DrawableFrameRef
|
||||
SurfaceCache::Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<uint32_t>& aAlternateFlags /* = Nothing() */)
|
||||
{
|
||||
if (!sInstance) {
|
||||
return DrawableFrameRef();
|
||||
}
|
||||
|
||||
MutexAutoLock lock(sInstance->GetMutex());
|
||||
return sInstance->Lookup(aImageKey, aSurfaceKey);
|
||||
|
||||
DrawableFrameRef ref = sInstance->Lookup(aImageKey, aSurfaceKey);
|
||||
if (!ref && aAlternateFlags) {
|
||||
ref = sInstance->Lookup(aImageKey,
|
||||
aSurfaceKey.WithNewFlags(*aAlternateFlags));
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
/* static */ DrawableFrameRef
|
||||
SurfaceCache::LookupBestMatch(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<uint32_t>& aAlternateFlags
|
||||
/* = Nothing() */)
|
||||
{
|
||||
if (!sInstance) {
|
||||
return DrawableFrameRef();
|
||||
}
|
||||
|
||||
MutexAutoLock lock(sInstance->GetMutex());
|
||||
return sInstance->LookupBestMatch(aImageKey, aSurfaceKey, aAlternateFlags);
|
||||
}
|
||||
|
||||
/* static */ InsertOutcome
|
||||
|
|
|
@ -63,6 +63,14 @@ public:
|
|||
}
|
||||
|
||||
IntSize Size() const { return mSize; }
|
||||
Maybe<SVGImageContext> SVGContext() const { return mSVGContext; }
|
||||
float AnimationTime() const { return mAnimationTime; }
|
||||
uint32_t Flags() const { return mFlags; }
|
||||
|
||||
SurfaceKey WithNewFlags(uint32_t aFlags) const
|
||||
{
|
||||
return SurfaceKey(mSize, mSVGContext, mAnimationTime, aFlags);
|
||||
}
|
||||
|
||||
private:
|
||||
SurfaceKey(const IntSize& aSize,
|
||||
|
@ -171,12 +179,41 @@ struct SurfaceCache
|
|||
*
|
||||
* @param aImageKey Key data identifying which image the surface belongs to.
|
||||
* @param aSurfaceKey Key data which uniquely identifies the requested surface.
|
||||
* @param aAlternateFlags If not Nothing(), a different set of flags than the
|
||||
* ones specified in @aSurfaceKey which are also
|
||||
* acceptable to the caller. This is more efficient
|
||||
* than calling Lookup() twice, which requires taking a
|
||||
* lock each time.
|
||||
*
|
||||
* @return a DrawableFrameRef to the imgFrame wrapping the requested surface,
|
||||
* or an empty DrawableFrameRef if not found.
|
||||
*/
|
||||
static DrawableFrameRef Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey);
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<uint32_t>& aAlternateFlags
|
||||
= Nothing());
|
||||
|
||||
/**
|
||||
* Looks up the best matching surface in the cache and returns a drawable
|
||||
* reference to the imgFrame containing it.
|
||||
*
|
||||
* Returned surfaces may vary from the requested surface only in terms of
|
||||
* size, unless @aAlternateFlags is specified.
|
||||
*
|
||||
* @param aImageKey Key data identifying which image the surface belongs to.
|
||||
* @param aSurfaceKey Key data which identifies the ideal surface to return.
|
||||
* @param aAlternateFlags If not Nothing(), a different set of flags than the
|
||||
* ones specified in @aSurfaceKey which are also
|
||||
* acceptable to the caller. This is much more
|
||||
* efficient than calling LookupBestMatch() twice.
|
||||
*
|
||||
* @return a DrawableFrameRef to the imgFrame wrapping a surface similar to
|
||||
* the requested surface, or an empty DrawableFrameRef if not found.
|
||||
*/
|
||||
static DrawableFrameRef LookupBestMatch(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<uint32_t>& aAlternateFlags
|
||||
= Nothing());
|
||||
|
||||
/**
|
||||
* Insert a surface into the cache. If a surface with the same ImageKey and
|
||||
|
|
|
@ -899,6 +899,15 @@ VectorImage::StartDecoding()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
VectorImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
|
||||
{
|
||||
// Nothing to do for SVG images, though in theory we could rasterize to the
|
||||
// provided size ahead of time if we supported off-main-thread SVG
|
||||
// rasterization...
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
VectorImage::IsDecoded()
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ load 256-height.ico
|
|||
HTTP load delayedframe.sjs
|
||||
|
||||
asserts(0-1) load 681190.html # asserts can't create such a big surface
|
||||
skip-if(Android&&smallScreen) skip-if(B2G) load 694165-1.xhtml # nexus-s Android 2.3.6, bug 876275 for B2G on a VM
|
||||
skip-if(Android&&smallScreen) skip-if(B2G) skip-if(OSX==1010&&isDebugBuild) load 694165-1.xhtml # nexus-s Android 2.3.6, bug 876275 for B2G on a VM; bug 1123195 for OS X 10.10 debug
|
||||
load 732319-1.html
|
||||
load 844403-1.html
|
||||
|
||||
|
|
|
@ -462,7 +462,7 @@ struct JSClass {
|
|||
// the beginning of every global object's slots for use by the
|
||||
// application.
|
||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 4
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 30)
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 31)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
|
|
|
@ -31,6 +31,7 @@ using mozilla::FloorLog2;
|
|||
|
||||
namespace js {
|
||||
extern const JSFunctionSpec Float32x4Methods[];
|
||||
extern const JSFunctionSpec Float64x2Methods[];
|
||||
extern const JSFunctionSpec Int32x4Methods[];
|
||||
}
|
||||
|
||||
|
@ -65,6 +66,7 @@ js::IsVectorObject(HandleValue v)
|
|||
|
||||
template bool js::IsVectorObject<Int32x4>(HandleValue v);
|
||||
template bool js::IsVectorObject<Float32x4>(HandleValue v);
|
||||
template bool js::IsVectorObject<Float64x2>(HandleValue v);
|
||||
|
||||
template<typename V>
|
||||
bool
|
||||
|
@ -124,6 +126,9 @@ static bool type##Lane##lane(JSContext *cx, unsigned argc, Value *vp) { \
|
|||
FOUR_LANES_ACCESSOR(Int32x4);
|
||||
FOUR_LANES_ACCESSOR(Float32x4);
|
||||
#undef FOUR_LANES_ACCESSOR
|
||||
|
||||
LANE_ACCESSOR(Float64x2, 0);
|
||||
LANE_ACCESSOR(Float64x2, 1);
|
||||
#undef LANE_ACCESSOR
|
||||
|
||||
template<typename SimdType>
|
||||
|
@ -168,6 +173,7 @@ static bool type##SignMask(JSContext *cx, unsigned argc, Value *vp) { \
|
|||
return SignMask<type>(cx, argc, vp); \
|
||||
}
|
||||
SIGN_MASK(Float32x4);
|
||||
SIGN_MASK(Float64x2);
|
||||
SIGN_MASK(Int32x4);
|
||||
#undef SIGN_MASK
|
||||
|
||||
|
@ -201,6 +207,13 @@ class Float32x4Defn {
|
|||
static const JSPropertySpec TypedObjectProperties[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
};
|
||||
class Float64x2Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_FLOAT64;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSPropertySpec TypedObjectProperties[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
};
|
||||
} // namespace js
|
||||
|
||||
const JSFunctionSpec js::Float32x4Defn::TypeDescriptorMethods[] = {
|
||||
|
@ -224,6 +237,25 @@ const JSFunctionSpec js::Float32x4Defn::TypedObjectMethods[] = {
|
|||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec js::Float64x2Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSPropertySpec js::Float64x2Defn::TypedObjectProperties[] = {
|
||||
JS_PSG("x", Float64x2Lane0, JSPROP_PERMANENT),
|
||||
JS_PSG("y", Float64x2Lane1, JSPROP_PERMANENT),
|
||||
JS_PSG("signMask", Float64x2SignMask, JSPROP_PERMANENT),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec js::Float64x2Defn::TypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec js::Int32x4Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
|
@ -268,6 +300,7 @@ CreateSimdClass(JSContext *cx, Handle<GlobalObject*> global, HandlePropertyName
|
|||
typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(type)));
|
||||
typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false));
|
||||
typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
|
||||
typeDescr->initReservedSlot(JS_DESCR_SLOT_LANES, Int32Value(SimdTypeDescr::lanes(type)));
|
||||
|
||||
if (!CreateUserSizeAndAlignmentProperties(cx, typeDescr))
|
||||
return nullptr;
|
||||
|
@ -298,6 +331,18 @@ CreateSimdClass(JSContext *cx, Handle<GlobalObject*> global, HandlePropertyName
|
|||
return typeDescr;
|
||||
}
|
||||
|
||||
const char*
|
||||
SimdTypeToMinimumLanesNumber(SimdTypeDescr &descr) {
|
||||
switch (descr.type()) {
|
||||
case SimdTypeDescr::TYPE_INT32:
|
||||
case SimdTypeDescr::TYPE_FLOAT32:
|
||||
return "3";
|
||||
case SimdTypeDescr::TYPE_FLOAT64:
|
||||
return "1";
|
||||
}
|
||||
MOZ_CRASH("Unexpected SIMD type description.");
|
||||
}
|
||||
|
||||
bool
|
||||
SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
|
@ -336,6 +381,13 @@ SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case SimdTypeDescr::TYPE_FLOAT64: {
|
||||
double *mem = reinterpret_cast<double*>(result->typedMem());
|
||||
for (unsigned i = 0; i < 2; i++) {
|
||||
if (!ToNumber(cx, args[i], &mem[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
args.rval().setObject(*result);
|
||||
return true;
|
||||
|
@ -386,6 +438,23 @@ SIMDObject::initClass(JSContext *cx, Handle<GlobalObject *> global)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// float64x2
|
||||
RootedObject float64x2Object(cx);
|
||||
float64x2Object = CreateSimdClass<Float64x2Defn>(cx, global,
|
||||
cx->names().float64x2);
|
||||
if (!float64x2Object)
|
||||
return nullptr;
|
||||
|
||||
// Define float64x2 functions and install as a property of the SIMD object.
|
||||
RootedValue float64x2Value(cx, ObjectValue(*float64x2Object));
|
||||
if (!JS_DefineFunctions(cx, float64x2Object, Float64x2Methods) ||
|
||||
!DefineProperty(cx, SIMD, cx->names().float64x2,
|
||||
float64x2Value, nullptr, nullptr,
|
||||
JSPROP_READONLY | JSPROP_PERMANENT))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// int32x4
|
||||
RootedObject int32x4Object(cx);
|
||||
int32x4Object = CreateSimdClass<Int32x4Defn>(cx, global,
|
||||
|
@ -411,6 +480,7 @@ SIMDObject::initClass(JSContext *cx, Handle<GlobalObject *> global)
|
|||
|
||||
global->setConstructor(JSProto_SIMD, SIMDValue);
|
||||
global->setFloat32x4TypeDescr(*float32x4Object);
|
||||
global->setFloat64x2TypeDescr(*float64x2Object);
|
||||
global->setInt32x4TypeDescr(*int32x4Object);
|
||||
return SIMD;
|
||||
}
|
||||
|
@ -441,6 +511,7 @@ js::CreateSimd(JSContext *cx, typename V::Elem *data)
|
|||
}
|
||||
|
||||
template JSObject *js::CreateSimd<Float32x4>(JSContext *cx, Float32x4::Elem *data);
|
||||
template JSObject *js::CreateSimd<Float64x2>(JSContext *cx, Float64x2::Elem *data);
|
||||
template JSObject *js::CreateSimd<Int32x4>(JSContext *cx, Int32x4::Elem *data);
|
||||
|
||||
namespace js {
|
||||
|
@ -767,8 +838,10 @@ CompareFunc(JSContext *cx, unsigned argc, Value *vp)
|
|||
int32_t result[Int32x4::lanes];
|
||||
InElem *left = TypedObjectMemory<InElem *>(args[0]);
|
||||
InElem *right = TypedObjectMemory<InElem *>(args[1]);
|
||||
for (unsigned i = 0; i < Int32x4::lanes; i++)
|
||||
result[i] = Op<InElem>::apply(left[i], right[i]);
|
||||
for (unsigned i = 0; i < Int32x4::lanes; i++) {
|
||||
unsigned j = (i * In::lanes) / Int32x4::lanes;
|
||||
result[i] = Op<InElem>::apply(left[j], right[j]);
|
||||
}
|
||||
|
||||
return StoreResult<Int32x4>(cx, args, result);
|
||||
}
|
||||
|
@ -787,7 +860,8 @@ FuncConvert(JSContext *cx, unsigned argc, Value *vp)
|
|||
Elem *val = TypedObjectMemory<Elem *>(args[0]);
|
||||
RetElem result[Vret::lanes];
|
||||
for (unsigned i = 0; i < Vret::lanes; i++)
|
||||
result[i] = ConvertScalar<RetElem>(val[i]);
|
||||
result[i] = i < V::lanes ? ConvertScalar<RetElem>(val[i]) : 0;
|
||||
|
||||
return StoreResult<Vret>(cx, args, result);
|
||||
}
|
||||
|
||||
|
@ -858,27 +932,29 @@ Int32x4Bool(JSContext *cx, unsigned argc, Value *vp)
|
|||
return StoreResult<Int32x4>(cx, args, result);
|
||||
}
|
||||
|
||||
template<typename In>
|
||||
static bool
|
||||
Float32x4Clamp(JSContext *cx, unsigned argc, Value *vp)
|
||||
Clamp(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
typedef typename In::Elem InElem;
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 3 || !IsVectorObject<Float32x4>(args[0]) ||
|
||||
!IsVectorObject<Float32x4>(args[1]) || !IsVectorObject<Float32x4>(args[2]))
|
||||
if (args.length() != 3 || !IsVectorObject<In>(args[0]) ||
|
||||
!IsVectorObject<In>(args[1]) || !IsVectorObject<In>(args[2]))
|
||||
{
|
||||
return ErrorBadArgs(cx);
|
||||
}
|
||||
|
||||
float *val = TypedObjectMemory<float *>(args[0]);
|
||||
float *lowerLimit = TypedObjectMemory<float *>(args[1]);
|
||||
float *upperLimit = TypedObjectMemory<float *>(args[2]);
|
||||
InElem *val = TypedObjectMemory<InElem *>(args[0]);
|
||||
InElem *lowerLimit = TypedObjectMemory<InElem *>(args[1]);
|
||||
InElem *upperLimit = TypedObjectMemory<InElem *>(args[2]);
|
||||
|
||||
float result[Float32x4::lanes];
|
||||
for (unsigned i = 0; i < Float32x4::lanes; i++) {
|
||||
InElem result[In::lanes];
|
||||
for (unsigned i = 0; i < In::lanes; i++) {
|
||||
result[i] = val[i] < lowerLimit[i] ? lowerLimit[i] : val[i];
|
||||
result[i] = result[i] > upperLimit[i] ? upperLimit[i] : result[i];
|
||||
}
|
||||
|
||||
return StoreResult<Float32x4>(cx, args, result);
|
||||
return StoreResult<In>(cx, args, result);
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
|
@ -987,8 +1063,7 @@ Load(JSContext *cx, unsigned argc, Value *vp)
|
|||
return false;
|
||||
|
||||
Elem *dest = reinterpret_cast<Elem*>(result->typedMem());
|
||||
for (unsigned i = 0; i < NumElem; i++)
|
||||
dest[i] = typedArrayData[i];
|
||||
memcpy(dest, typedArrayData, sizeof(Elem) * NumElem);
|
||||
|
||||
args.rval().setObject(*result);
|
||||
return true;
|
||||
|
@ -1012,8 +1087,7 @@ Store(JSContext *cx, unsigned argc, Value *vp)
|
|||
return ErrorBadArgs(cx);
|
||||
|
||||
Elem *src = TypedObjectMemory<Elem*>(args[2]);
|
||||
for (unsigned i = 0; i < NumElem; i++)
|
||||
typedArrayData[i] = src[i];
|
||||
memcpy(typedArrayData, src, sizeof(Elem) * NumElem);
|
||||
|
||||
args.rval().setObject(args[2].toObject());
|
||||
return true;
|
||||
|
@ -1026,7 +1100,16 @@ js::simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp) \
|
|||
return Func(cx, argc, vp); \
|
||||
}
|
||||
FLOAT32X4_FUNCTION_LIST(DEFINE_SIMD_FLOAT32X4_FUNCTION)
|
||||
#undef DEFINE_SIMD_FLOAT32x4_FUNCTION
|
||||
#undef DEFINE_SIMD_FLOAT32X4_FUNCTION
|
||||
|
||||
#define DEFINE_SIMD_FLOAT64X2_FUNCTION(Name, Func, Operands, Flags) \
|
||||
bool \
|
||||
js::simd_float64x2_##Name(JSContext *cx, unsigned argc, Value *vp) \
|
||||
{ \
|
||||
return Func(cx, argc, vp); \
|
||||
}
|
||||
FLOAT64X2_FUNCTION_LIST(DEFINE_SIMD_FLOAT64X2_FUNCTION)
|
||||
#undef DEFINE_SIMD_FLOAT64X2_FUNCTION
|
||||
|
||||
#define DEFINE_SIMD_INT32X4_FUNCTION(Name, Func, Operands, Flags) \
|
||||
bool \
|
||||
|
@ -1045,6 +1128,14 @@ const JSFunctionSpec js::Float32x4Methods[] = {
|
|||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec js::Float64x2Methods[] = {
|
||||
#define SIMD_FLOAT64X2_FUNCTION_ITEM(Name, Func, Operands, Flags) \
|
||||
JS_FN(#Name, js::simd_float64x2_##Name, Operands, Flags),
|
||||
FLOAT64X2_FUNCTION_LIST(SIMD_FLOAT64X2_FUNCTION_ITEM)
|
||||
#undef SIMD_FLOAT64X2_FUNCTION_ITEM
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec js::Int32x4Methods[] = {
|
||||
#define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags) \
|
||||
JS_FN(#Name, js::simd_int32x4_##Name, Operands, Flags),
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#define FLOAT32X4_UNARY_FUNCTION_LIST(V) \
|
||||
V(abs, (UnaryFunc<Float32x4, Abs, Float32x4>), 1, 0) \
|
||||
V(fromFloat64x2, (FuncConvert<Float64x2, Float32x4> ), 1, 0) \
|
||||
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Float32x4>), 1, 0) \
|
||||
V(fromInt32x4, (FuncConvert<Int32x4, Float32x4> ), 1, 0) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float32x4>), 1, 0) \
|
||||
V(neg, (UnaryFunc<Float32x4, Neg, Float32x4>), 1, 0) \
|
||||
|
@ -64,7 +66,7 @@
|
|||
|
||||
#define FLOAT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||
V(bitselect, BitSelect<Float32x4>, 3, 0) \
|
||||
V(clamp, Float32x4Clamp, 3, 0) \
|
||||
V(clamp, Clamp<Float32x4>, 3, 0) \
|
||||
V(select, Select<Float32x4>, 3, 0)
|
||||
|
||||
#define FLOAT32X4_SHUFFLE_FUNCTION_LIST(V) \
|
||||
|
@ -77,9 +79,60 @@
|
|||
FLOAT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||
FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
#define FLOAT64X2_UNARY_FUNCTION_LIST(V) \
|
||||
V(abs, (UnaryFunc<Float64x2, Abs, Float64x2>), 1, 0) \
|
||||
V(fromFloat32x4, (FuncConvert<Float32x4, Float64x2> ), 1, 0) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Float64x2>), 1, 0) \
|
||||
V(fromInt32x4, (FuncConvert<Int32x4, Float64x2> ), 1, 0) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float64x2>), 1, 0) \
|
||||
V(neg, (UnaryFunc<Float64x2, Neg, Float64x2>), 1, 0) \
|
||||
V(reciprocal, (UnaryFunc<Float64x2, Rec, Float64x2>), 1, 0) \
|
||||
V(reciprocalSqrt, (UnaryFunc<Float64x2, RecSqrt, Float64x2>), 1, 0) \
|
||||
V(splat, (FuncSplat<Float64x2>), 1, 0) \
|
||||
V(sqrt, (UnaryFunc<Float64x2, Sqrt, Float64x2>), 1, 0)
|
||||
|
||||
#define FLOAT64X2_BINARY_FUNCTION_LIST(V) \
|
||||
V(add, (BinaryFunc<Float64x2, Add, Float64x2>), 2, 0) \
|
||||
V(div, (BinaryFunc<Float64x2, Div, Float64x2>), 2, 0) \
|
||||
V(equal, (CompareFunc<Float64x2, Equal>), 2, 0) \
|
||||
V(greaterThan, (CompareFunc<Float64x2, GreaterThan>), 2, 0) \
|
||||
V(greaterThanOrEqual, (CompareFunc<Float64x2, GreaterThanOrEqual>), 2, 0) \
|
||||
V(lessThan, (CompareFunc<Float64x2, LessThan>), 2, 0) \
|
||||
V(lessThanOrEqual, (CompareFunc<Float64x2, LessThanOrEqual>), 2, 0) \
|
||||
V(load, (Load<Float64x2, 2>), 2, 0) \
|
||||
V(loadX, (Load<Float64x2, 1>), 2, 0) \
|
||||
V(max, (BinaryFunc<Float64x2, Maximum, Float64x2>), 2, 0) \
|
||||
V(maxNum, (BinaryFunc<Float64x2, MaxNum, Float64x2>), 2, 0) \
|
||||
V(min, (BinaryFunc<Float64x2, Minimum, Float64x2>), 2, 0) \
|
||||
V(minNum, (BinaryFunc<Float64x2, MinNum, Float64x2>), 2, 0) \
|
||||
V(mul, (BinaryFunc<Float64x2, Mul, Float64x2>), 2, 0) \
|
||||
V(notEqual, (CompareFunc<Float64x2, NotEqual>), 2, 0) \
|
||||
V(store, (Store<Float64x2, 2>), 3, 0) \
|
||||
V(storeX, (Store<Float64x2, 1>), 3, 0) \
|
||||
V(sub, (BinaryFunc<Float64x2, Sub, Float64x2>), 2, 0) \
|
||||
V(withX, (FuncWith<Float64x2, WithX>), 2, 0) \
|
||||
V(withY, (FuncWith<Float64x2, WithY>), 2, 0)
|
||||
|
||||
#define FLOAT64X2_TERNARY_FUNCTION_LIST(V) \
|
||||
V(bitselect, BitSelect<Float64x2>, 3, 0) \
|
||||
V(clamp, Clamp<Float64x2>, 3, 0) \
|
||||
V(select, Select<Float64x2>, 3, 0)
|
||||
|
||||
#define FLOAT64X2_SHUFFLE_FUNCTION_LIST(V) \
|
||||
V(swizzle, Swizzle<Float64x2>, 2, 0) \
|
||||
V(shuffle, Shuffle<Float64x2>, 3, 0)
|
||||
|
||||
#define FLOAT64X2_FUNCTION_LIST(V) \
|
||||
FLOAT64X2_UNARY_FUNCTION_LIST(V) \
|
||||
FLOAT64X2_BINARY_FUNCTION_LIST(V) \
|
||||
FLOAT64X2_TERNARY_FUNCTION_LIST(V) \
|
||||
FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
#define INT32X4_UNARY_FUNCTION_LIST(V) \
|
||||
V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1, 0) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1, 0) \
|
||||
V(fromFloat64x2, (FuncConvert<Float64x2, Int32x4>), 1, 0) \
|
||||
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int32x4>), 1, 0) \
|
||||
V(neg, (UnaryFunc<Int32x4, Neg, Int32x4>), 1, 0) \
|
||||
V(not, (UnaryFunc<Int32x4, Not, Int32x4>), 1, 0) \
|
||||
V(splat, (FuncSplat<Int32x4>), 0, 0)
|
||||
|
@ -212,6 +265,26 @@ struct Float32x4 {
|
|||
}
|
||||
};
|
||||
|
||||
struct Float64x2 {
|
||||
typedef double Elem;
|
||||
static const unsigned lanes = 2;
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_FLOAT64;
|
||||
|
||||
static TypeDescr &GetTypeDescr(GlobalObject &global) {
|
||||
return global.float64x2TypeDescr().as<TypeDescr>();
|
||||
}
|
||||
static Elem toType(Elem a) {
|
||||
return a;
|
||||
}
|
||||
static bool toType(JSContext *cx, JS::HandleValue v, Elem *out) {
|
||||
*out = v.toNumber();
|
||||
return true;
|
||||
}
|
||||
static void setReturn(CallArgs &args, Elem value) {
|
||||
args.rval().setDouble(JS::CanonicalizeNaN(value));
|
||||
}
|
||||
};
|
||||
|
||||
struct Int32x4 {
|
||||
typedef int32_t Elem;
|
||||
static const unsigned lanes = 4;
|
||||
|
@ -246,6 +319,12 @@ simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp);
|
|||
FLOAT32X4_FUNCTION_LIST(DECLARE_SIMD_FLOAT32X4_FUNCTION)
|
||||
#undef DECLARE_SIMD_FLOAT32X4_FUNCTION
|
||||
|
||||
#define DECLARE_SIMD_FLOAT64X2_FUNCTION(Name, Func, Operands, Flags) \
|
||||
extern bool \
|
||||
simd_float64x2_##Name(JSContext *cx, unsigned argc, Value *vp);
|
||||
FLOAT64X2_FUNCTION_LIST(DECLARE_SIMD_FLOAT64X2_FUNCTION)
|
||||
#undef DECLARE_SIMD_FLOAT64X2_FUNCTION
|
||||
|
||||
#define DECLARE_SIMD_INT32x4_FUNCTION(Name, Func, Operands, Flags) \
|
||||
extern bool \
|
||||
simd_int32x4_##Name(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
|
|
@ -406,18 +406,25 @@ js::ReferenceTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
|
|||
}
|
||||
|
||||
/***************************************************************************
|
||||
* X4 type objects
|
||||
* SIMD type objects
|
||||
*
|
||||
* Note: these are partially defined in SIMD.cpp
|
||||
*/
|
||||
|
||||
static const int32_t SimdSizes[] = {
|
||||
#define SIMD_SIZE(_kind, _type, _name) \
|
||||
sizeof(_type) * 4,
|
||||
#define SIMD_SIZE(_kind, _type, _name, _lanes) \
|
||||
sizeof(_type) * _lanes,
|
||||
JS_FOR_EACH_SIMD_TYPE_REPR(SIMD_SIZE) 0
|
||||
#undef SIMD_SIZE
|
||||
};
|
||||
|
||||
static int32_t SimdLanes[] = {
|
||||
#define SIMD_LANE(_kind, _type, _name, _lanes) \
|
||||
_lanes,
|
||||
JS_FOR_EACH_SIMD_TYPE_REPR(SIMD_LANE) 0
|
||||
#undef SIMD_LANE
|
||||
};
|
||||
|
||||
int32_t
|
||||
SimdTypeDescr::size(Type t)
|
||||
{
|
||||
|
@ -430,6 +437,12 @@ SimdTypeDescr::alignment(Type t)
|
|||
return SimdSizes[t];
|
||||
}
|
||||
|
||||
int32_t
|
||||
SimdTypeDescr::lanes(Type t)
|
||||
{
|
||||
return SimdLanes[t];
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* ArrayMetaTypeDescr class
|
||||
*/
|
||||
|
@ -2694,6 +2707,16 @@ js::GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetFloat64x2TypeDescr(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
Rooted<GlobalObject*> global(cx, cx->global());
|
||||
MOZ_ASSERT(global);
|
||||
args.rval().setObject(global->float64x2TypeDescr());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
|
|
|
@ -325,7 +325,7 @@ class ComplexTypeDescr : public TypeDescr
|
|||
};
|
||||
|
||||
/*
|
||||
* Type descriptors `float32x4` and `int32x4`
|
||||
* Type descriptors `float32x4`, `int32x4` and `float64x2`
|
||||
*/
|
||||
class SimdTypeDescr : public ComplexTypeDescr
|
||||
{
|
||||
|
@ -333,6 +333,7 @@ class SimdTypeDescr : public ComplexTypeDescr
|
|||
enum Type {
|
||||
TYPE_INT32 = JS_SIMDTYPEREPR_INT32,
|
||||
TYPE_FLOAT32 = JS_SIMDTYPEREPR_FLOAT32,
|
||||
TYPE_FLOAT64 = JS_SIMDTYPEREPR_FLOAT64
|
||||
};
|
||||
|
||||
static const type::Kind Kind = type::Simd;
|
||||
|
@ -340,6 +341,7 @@ class SimdTypeDescr : public ComplexTypeDescr
|
|||
static const Class class_;
|
||||
static int32_t size(Type t);
|
||||
static int32_t alignment(Type t);
|
||||
static int32_t lanes(Type t);
|
||||
|
||||
SimdTypeDescr::Type type() const {
|
||||
return (SimdTypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32();
|
||||
|
@ -350,8 +352,9 @@ class SimdTypeDescr : public ComplexTypeDescr
|
|||
};
|
||||
|
||||
#define JS_FOR_EACH_SIMD_TYPE_REPR(macro_) \
|
||||
macro_(SimdTypeDescr::TYPE_INT32, int32_t, int32) \
|
||||
macro_(SimdTypeDescr::TYPE_FLOAT32, float, float32)
|
||||
macro_(SimdTypeDescr::TYPE_INT32, int32_t, int32, 4) \
|
||||
macro_(SimdTypeDescr::TYPE_FLOAT32, float, float32, 4) \
|
||||
macro_(SimdTypeDescr::TYPE_FLOAT64, double, float64, 2)
|
||||
|
||||
bool IsTypedObjectClass(const Class *clasp); // Defined below
|
||||
bool IsTypedObjectArray(JSObject& obj);
|
||||
|
@ -880,6 +883,14 @@ bool GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp);
|
|||
*/
|
||||
bool GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
/*
|
||||
* Usage: GetFloat64x2TypeDescr()
|
||||
*
|
||||
* Returns the float64x2 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetFloat64x2TypeDescr(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
/*
|
||||
* Usage: GetInt32x4TypeDescr()
|
||||
*
|
||||
|
|
|
@ -152,6 +152,11 @@ function TypedObjectGetSimd(descr, typedObj, offset) {
|
|||
var w = Load_float32(typedObj, offset + 12);
|
||||
return GetFloat32x4TypeDescr()(x, y, z, w);
|
||||
|
||||
case JS_SIMDTYPEREPR_FLOAT64:
|
||||
var x = Load_float64(typedObj, offset + 0);
|
||||
var y = Load_float64(typedObj, offset + 8);
|
||||
return GetFloat64x2TypeDescr()(x, y);
|
||||
|
||||
case JS_SIMDTYPEREPR_INT32:
|
||||
var x = Load_int32(typedObj, offset + 0);
|
||||
var y = Load_int32(typedObj, offset + 4);
|
||||
|
@ -322,6 +327,10 @@ function TypedObjectSetSimd(descr, typedObj, offset, fromValue) {
|
|||
Store_float32(typedObj, offset + 8, Load_float32(fromValue, 8));
|
||||
Store_float32(typedObj, offset + 12, Load_float32(fromValue, 12));
|
||||
break;
|
||||
case JS_SIMDTYPEREPR_FLOAT64:
|
||||
Store_float64(typedObj, offset + 0, Load_float64(fromValue, 0));
|
||||
Store_float64(typedObj, offset + 8, Load_float64(fromValue, 8));
|
||||
break;
|
||||
case JS_SIMDTYPEREPR_INT32:
|
||||
Store_int32(typedObj, offset + 0, Load_int32(fromValue, 0));
|
||||
Store_int32(typedObj, offset + 4, Load_int32(fromValue, 4));
|
||||
|
@ -457,6 +466,21 @@ function SimdProtoString(type) {
|
|||
return "int32x4";
|
||||
case JS_SIMDTYPEREPR_FLOAT32:
|
||||
return "float32x4";
|
||||
case JS_SIMDTYPEREPR_FLOAT64:
|
||||
return "float64x2";
|
||||
}
|
||||
|
||||
assert(false, "Unhandled type constant");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function SimdTypeToLength(type) {
|
||||
switch (type) {
|
||||
case JS_SIMDTYPEREPR_INT32:
|
||||
case JS_SIMDTYPEREPR_FLOAT32:
|
||||
return 4;
|
||||
case JS_SIMDTYPEREPR_FLOAT64:
|
||||
return 2;
|
||||
}
|
||||
|
||||
assert(false, "Unhandled type constant");
|
||||
|
@ -473,7 +497,12 @@ function SimdToSource() {
|
|||
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this);
|
||||
|
||||
var type = DESCR_TYPE(descr);
|
||||
return SimdProtoString(type)+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
|
||||
var protoString = SimdProtoString(type);
|
||||
var length = SimdTypeToLength(type);
|
||||
if (length == 4)
|
||||
return protoString+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
|
||||
else if (length == 2)
|
||||
return protoString+"("+this.x+", "+this.y+")";
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -32,8 +32,9 @@
|
|||
#define JS_DESCR_SLOT_ARRAYPROTO 6 // Lazily created prototype for arrays
|
||||
#define JS_DESCR_SLOT_TRACE_LIST 7 // List of references for use in tracing
|
||||
|
||||
// Slots on scalars, references, and x4s
|
||||
// Slots on scalars, references, and SIMD objects
|
||||
#define JS_DESCR_SLOT_TYPE 8 // Type code
|
||||
#define JS_DESCR_SLOT_LANES 9
|
||||
|
||||
// Slots on array descriptors
|
||||
#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE 8
|
||||
|
@ -85,5 +86,6 @@
|
|||
// case.
|
||||
#define JS_SIMDTYPEREPR_INT32 0
|
||||
#define JS_SIMDTYPEREPR_FLOAT32 1
|
||||
#define JS_SIMDTYPEREPR_FLOAT64 2
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 946042;
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
|
||||
var summary = 'float32x4 clamp';
|
||||
var summary = 'float32x4/float64x2 clamp';
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
|
||||
// FIXME -- Bug 1068028: Amend to check for correctness of NaN/-0 border cases once the semantics are defined.
|
||||
|
||||
var a = float32x4(-20, 10, 30, 0.5);
|
||||
var lower = float32x4(2, 1, 50, 0);
|
||||
|
@ -37,6 +34,24 @@ function test() {
|
|||
assertEq(i.z, 10);
|
||||
assertEq(i.w, -10);
|
||||
|
||||
var j = float64x2(-20, 10);
|
||||
var k = float64x2(2.125, 3);
|
||||
var lower3 = float64x2(2, 1);
|
||||
var upper3 = float64x2(2.5, 5);
|
||||
var l = float64x2.clamp(j, lower3, upper3);
|
||||
assertEq(l.x, 2);
|
||||
assertEq(l.y, 5);
|
||||
var m = float64x2.clamp(k, lower3, upper3);
|
||||
assertEq(m.x, 2.125);
|
||||
assertEq(m.y, 3);
|
||||
|
||||
var n = float64x2(-5, 5);
|
||||
var lower4 = float64x2(-Infinity, 0);
|
||||
var upper4 = float64x2(+Infinity, +Infinity);
|
||||
var p = float64x2.clamp(n, lower4, upper4);
|
||||
assertEq(p.x, -5);
|
||||
assertEq(p.y, 5);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var fround = Math.fround;
|
||||
|
@ -15,41 +16,60 @@ function boolToSimdLogical(b) {
|
|||
}
|
||||
|
||||
function testEqualFloat32x4(v, w) {
|
||||
testBinaryFunc(v, w, float32x4.equal, (x, y) => boolToSimdLogical(fround(x) == fround(y)));
|
||||
testBinaryCompare(v, w, float32x4.equal, (x, y) => boolToSimdLogical(fround(x) == fround(y)));
|
||||
}
|
||||
function testNotEqualFloat32x4(v, w) {
|
||||
testBinaryFunc(v, w, float32x4.notEqual, (x, y) => boolToSimdLogical(fround(x) != fround(y)));
|
||||
testBinaryCompare(v, w, float32x4.notEqual, (x, y) => boolToSimdLogical(fround(x) != fround(y)));
|
||||
}
|
||||
function testLessThanFloat32x4(v, w) {
|
||||
testBinaryFunc(v, w, float32x4.lessThan, (x, y) => boolToSimdLogical(fround(x) < fround(y)));
|
||||
testBinaryCompare(v, w, float32x4.lessThan, (x, y) => boolToSimdLogical(fround(x) < fround(y)));
|
||||
}
|
||||
function testLessThanOrEqualFloat32x4(v, w) {
|
||||
testBinaryFunc(v, w, float32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(fround(x) <= fround(y)));
|
||||
testBinaryCompare(v, w, float32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(fround(x) <= fround(y)));
|
||||
}
|
||||
function testGreaterThanFloat32x4(v, w) {
|
||||
testBinaryFunc(v, w, float32x4.greaterThan, (x, y) => boolToSimdLogical(fround(x) > fround(y)));
|
||||
testBinaryCompare(v, w, float32x4.greaterThan, (x, y) => boolToSimdLogical(fround(x) > fround(y)));
|
||||
}
|
||||
function testGreaterThanOrEqualFloat32x4(v, w) {
|
||||
testBinaryFunc(v, w, float32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(fround(x) >= fround(y)));
|
||||
testBinaryCompare(v, w, float32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(fround(x) >= fround(y)));
|
||||
}
|
||||
|
||||
function testEqualFloat64x2(v, w) {
|
||||
testBinaryCompare(v, w, float64x2.equal, (x, y) => boolToSimdLogical(x == y));
|
||||
}
|
||||
function testNotEqualFloat64x2(v, w) {
|
||||
testBinaryCompare(v, w, float64x2.notEqual, (x, y) => boolToSimdLogical(x != y));
|
||||
}
|
||||
function testLessThanFloat64x2(v, w) {
|
||||
testBinaryCompare(v, w, float64x2.lessThan, (x, y) => boolToSimdLogical(x < y));
|
||||
}
|
||||
function testLessThanOrEqualFloat64x2(v, w) {
|
||||
testBinaryCompare(v, w, float64x2.lessThanOrEqual, (x, y) => boolToSimdLogical(x <= y));
|
||||
}
|
||||
function testGreaterThanFloat64x2(v, w) {
|
||||
testBinaryCompare(v, w, float64x2.greaterThan, (x, y) => boolToSimdLogical(x > y));
|
||||
}
|
||||
function testGreaterThanOrEqualFloat64x2(v, w) {
|
||||
testBinaryCompare(v, w, float64x2.greaterThanOrEqual, (x, y) => boolToSimdLogical(x >= y));
|
||||
}
|
||||
|
||||
function testEqualInt32x4(v, w) {
|
||||
testBinaryFunc(v, w, int32x4.equal, (x, y) => boolToSimdLogical(x == y));
|
||||
testBinaryCompare(v, w, int32x4.equal, (x, y) => boolToSimdLogical(x == y));
|
||||
}
|
||||
function testNotEqualInt32x4(v, w) {
|
||||
testBinaryFunc(v, w, int32x4.notEqual, (x, y) => boolToSimdLogical(x != y));
|
||||
testBinaryCompare(v, w, int32x4.notEqual, (x, y) => boolToSimdLogical(x != y));
|
||||
}
|
||||
function testLessThanInt32x4(v, w) {
|
||||
testBinaryFunc(v, w, int32x4.lessThan, (x, y) => boolToSimdLogical(x < y));
|
||||
testBinaryCompare(v, w, int32x4.lessThan, (x, y) => boolToSimdLogical(x < y));
|
||||
}
|
||||
function testLessThanOrEqualInt32x4(v, w) {
|
||||
testBinaryFunc(v, w, int32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(x <= y));
|
||||
testBinaryCompare(v, w, int32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(x <= y));
|
||||
}
|
||||
function testGreaterThanInt32x4(v, w) {
|
||||
testBinaryFunc(v, w, int32x4.greaterThan, (x, y) => boolToSimdLogical(x > y));
|
||||
testBinaryCompare(v, w, int32x4.greaterThan, (x, y) => boolToSimdLogical(x > y));
|
||||
}
|
||||
function testGreaterThanOrEqualInt32x4(v, w) {
|
||||
testBinaryFunc(v, w, int32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(x >= y));
|
||||
testBinaryCompare(v, w, int32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(x >= y));
|
||||
}
|
||||
|
||||
function test() {
|
||||
|
@ -75,6 +95,32 @@ function test() {
|
|||
}
|
||||
}
|
||||
|
||||
var float64x2val = [
|
||||
float64x2(1, 20),
|
||||
float64x2(10, 2),
|
||||
float64x2(9.999, 2.1234),
|
||||
float64x2(10, 2.1233),
|
||||
float64x2(30.4443, 4),
|
||||
float64x2(30.4444, 4.0001),
|
||||
float64x2(NaN, -Infinity),
|
||||
float64x2(+Infinity, NaN),
|
||||
float64x2(+Infinity, -0),
|
||||
float64x2(-0, -Infinity),
|
||||
float64x2(13.37, 42.42),
|
||||
float64x2(NaN, 0)
|
||||
];
|
||||
|
||||
for (v of float64x2val) {
|
||||
for (w of float64x2val) {
|
||||
testEqualFloat64x2(v, w);
|
||||
testNotEqualFloat64x2(v, w);
|
||||
testLessThanFloat64x2(v, w);
|
||||
testLessThanOrEqualFloat64x2(v, w);
|
||||
testGreaterThanFloat64x2(v, w);
|
||||
testGreaterThanOrEqualFloat64x2(v, w);
|
||||
}
|
||||
}
|
||||
|
||||
var int32x4val = [
|
||||
int32x4(1, 2, 3, 4),
|
||||
int32x4(-1, -2, -3, -4),
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 946042;
|
||||
|
||||
var float32x4 = SIMD.float32x4;
|
||||
|
||||
function testMaxFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.max, (x, y) => Math.fround(Math.max(x, y)));
|
||||
}
|
||||
function testMinFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.min, (x, y) => Math.fround(Math.min(x, y)));
|
||||
}
|
||||
|
||||
function maxNum(x, y) {
|
||||
if (x != x)
|
||||
return y;
|
||||
if (y != y)
|
||||
return x;
|
||||
return Math.max(x, y);
|
||||
}
|
||||
|
||||
function minNum(x, y) {
|
||||
if (x != x)
|
||||
return y;
|
||||
if (y != y)
|
||||
return x;
|
||||
return Math.min(x, y);
|
||||
}
|
||||
|
||||
function testMaxNumFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.maxNum, maxNum);
|
||||
}
|
||||
function testMinNumFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.minNum, minNum);
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
for ([v, w] of [[float32x4(1, 20, 30, 4), float32x4(10, 2, 3, 40)],
|
||||
[float32x4(9.999, 2.1234, 30.4443, 4), float32x4(10, 2.1233, 30.4444, 4.0001)],
|
||||
[float32x4(NaN, -Infinity, +Infinity, -0), float32x4(13.37, 42.42, NaN, 0)]])
|
||||
{
|
||||
testMinFloat32(v, w);
|
||||
testMaxFloat32(v, w);
|
||||
testMinNumFloat32(v, w);
|
||||
testMaxNumFloat32(v, w);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
|
||||
var summary = 'float32x4 fromFloat64x2';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = float64x2(1, 2);
|
||||
var c = float32x4.fromFloat64x2(a);
|
||||
assertEq(c.x, 1);
|
||||
assertEq(c.y, 2);
|
||||
assertEq(c.z, 0);
|
||||
assertEq(c.w, 0);
|
||||
|
||||
var d = float64x2(-0, NaN);
|
||||
var f = float32x4.fromFloat64x2(d);
|
||||
assertEq(f.x, -0);
|
||||
assertEq(f.y, NaN);
|
||||
assertEq(f.z, 0);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
var g = float64x2(Infinity, -Infinity);
|
||||
var i = float32x4.fromFloat64x2(g);
|
||||
assertEq(i.x, Infinity);
|
||||
assertEq(i.y, -Infinity);
|
||||
assertEq(i.z, 0);
|
||||
assertEq(i.w, 0);
|
||||
|
||||
var j = Math.pow(2, 25) - 1;
|
||||
var k = -Math.pow(2, 25);
|
||||
var l = float64x2(j, k);
|
||||
var m = float32x4.fromFloat64x2(l);
|
||||
assertEq(m.x, Math.fround(j));
|
||||
assertEq(m.y, Math.fround(k));
|
||||
assertEq(m.z, 0);
|
||||
assertEq(m.w, 0);
|
||||
|
||||
var o = Math.pow(2, 1000);
|
||||
var p = Math.pow(2, -1000);
|
||||
var q = float64x2(o, p);
|
||||
var r = float32x4.fromFloat64x2(q);
|
||||
assertEq(r.x, Math.fround(o));
|
||||
assertEq(r.y, Math.fround(p));
|
||||
assertEq(r.z, 0);
|
||||
assertEq(r.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float32x4 fromFloat64x2Bits';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = float64x2(2.000000473111868, 512.0001225471497);
|
||||
var b = float32x4.fromFloat64x2Bits(a);
|
||||
assertEq(b.x, 1.0);
|
||||
assertEq(b.y, 2.0);
|
||||
assertEq(b.z, 3.0);
|
||||
assertEq(b.w, 4.0);
|
||||
|
||||
var c = float64x2(-0, NaN);
|
||||
var d = float32x4.fromFloat64x2Bits(c);
|
||||
assertEq(d.x, 0);
|
||||
assertEq(d.y, -0);
|
||||
assertEq(d.z, 0);
|
||||
assertEq(d.w, NaN);
|
||||
|
||||
var e = float64x2(Infinity, -Infinity);
|
||||
var f = float32x4.fromFloat64x2Bits(e);
|
||||
assertEq(f.x, 0);
|
||||
assertEq(f.y, NaN);
|
||||
assertEq(f.z, 0);
|
||||
assertEq(f.w, NaN);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
|
||||
var summary = 'float64x2 arithmetic';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function add(a, b) { return a + b; }
|
||||
function sub(a, b) { return a - b; }
|
||||
function mul(a, b) { return a * b; }
|
||||
function div(a, b) { return a / b; }
|
||||
function neg(a) { return -a; }
|
||||
function reciprocal(a) { return 1 / a; }
|
||||
function reciprocalSqrt(a) { return 1 / Math.sqrt(a); }
|
||||
|
||||
function testAdd(v, w) {
|
||||
return testBinaryFunc(v, w, float64x2.add, add);
|
||||
}
|
||||
function testSub(v, w) {
|
||||
return testBinaryFunc(v, w, float64x2.sub, sub);
|
||||
}
|
||||
function testMul(v, w) {
|
||||
return testBinaryFunc(v, w, float64x2.mul, mul);
|
||||
}
|
||||
function testDiv(v, w) {
|
||||
return testBinaryFunc(v, w, float64x2.div, div);
|
||||
}
|
||||
function testAbs(v) {
|
||||
return testUnaryFunc(v, float64x2.abs, Math.abs);
|
||||
}
|
||||
function testNeg(v) {
|
||||
return testUnaryFunc(v, float64x2.neg, neg);
|
||||
}
|
||||
function testReciprocal(v) {
|
||||
return testUnaryFunc(v, float64x2.reciprocal, reciprocal);
|
||||
}
|
||||
function testReciprocalSqrt(v) {
|
||||
return testUnaryFunc(v, float64x2.reciprocalSqrt, reciprocalSqrt);
|
||||
}
|
||||
function testSqrt(v) {
|
||||
return testUnaryFunc(v, float64x2.sqrt, Math.sqrt);
|
||||
}
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var v, w;
|
||||
for ([v, w] of [[float64x2(1, 2), float64x2(3, 4)],
|
||||
[float64x2(1.894, 2.8909), float64x2(100.764, 200.987)],
|
||||
[float64x2(-1, -2), float64x2(-14.54, 57)],
|
||||
[float64x2(+Infinity, -Infinity), float64x2(NaN, -0)]])
|
||||
{
|
||||
testAdd(v, w);
|
||||
testSub(v, w);
|
||||
testMul(v, w);
|
||||
testDiv(v, w);
|
||||
testAbs(v);
|
||||
testNeg(v);
|
||||
testReciprocal(v);
|
||||
testSqrt(v);
|
||||
}
|
||||
for (v of [float64x2(1, 0.25), float64x2(3, 0.5),
|
||||
float64x2(-0, NaN), float64x2(+Infinity, -Infinity)])
|
||||
{
|
||||
testReciprocalSqrt(v);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
|
||||
var summary = 'float64x2 alignment';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var StructType = TypedObject.StructType;
|
||||
var uint8 = TypedObject.uint8;
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
assertEq(float64x2.byteLength, 16);
|
||||
assertEq(float64x2.byteAlignment, 16);
|
||||
|
||||
var Compound = new StructType({c: uint8, d: uint8, f: float64x2});
|
||||
assertEq(Compound.fieldOffsets["c"], 0);
|
||||
assertEq(Compound.fieldOffsets["d"], 1);
|
||||
assertEq(Compound.fieldOffsets["f"], 16);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
|
@ -0,0 +1,41 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
|
||||
var summary = 'float64x2 fromFloat32x4';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = float32x4(100, 200, 300, 400);
|
||||
var c = float64x2.fromFloat32x4(a);
|
||||
assertEq(c.x, 100);
|
||||
assertEq(c.y, 200);
|
||||
|
||||
var d = float32x4(NaN, -0, NaN, -0);
|
||||
var f = float64x2.fromFloat32x4(d);
|
||||
assertEq(f.x, NaN);
|
||||
assertEq(f.y, -0);
|
||||
|
||||
var g = float32x4(Infinity, -Infinity, Infinity, -Infinity);
|
||||
var i = float64x2.fromFloat32x4(g);
|
||||
assertEq(i.x, Infinity);
|
||||
assertEq(i.y, -Infinity);
|
||||
|
||||
var j = float32x4(13.37, 12.853, 49.97, 53.124);
|
||||
var l = float64x2.fromFloat32x4(j);
|
||||
assertEq(l.x, Math.fround(13.37));
|
||||
assertEq(l.y, Math.fround(12.853));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float64x2 fromFloat32x4Bits';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = float32x4(0, 1.875, 0, 2);
|
||||
var c = float64x2.fromFloat32x4Bits(a);
|
||||
assertEq(c.x, 1.0);
|
||||
assertEq(c.y, 2.0);
|
||||
|
||||
var d = float32x4(NaN, -0, Infinity, -Infinity);
|
||||
var f = float64x2.fromFloat32x4Bits(d);
|
||||
assertEq(f.x, -1.058925634e-314);
|
||||
assertEq(f.y, -1.404448428688076e+306);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float64x2 fromInt32x4';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var c = float64x2.fromInt32x4(a);
|
||||
assertEq(c.x, 1);
|
||||
assertEq(c.y, 2);
|
||||
|
||||
var d = int32x4(INT32_MAX, INT32_MIN, 0, 0);
|
||||
var f = float64x2.fromInt32x4(d);
|
||||
assertEq(f.x, INT32_MAX);
|
||||
assertEq(f.y, INT32_MIN);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float64x2 fromInt32x4Bits';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = int32x4(0x00000000, 0x3ff00000, 0x0000000, 0x40000000);
|
||||
var c = float64x2.fromInt32x4Bits(a);
|
||||
assertEq(c.x, 1.0);
|
||||
assertEq(c.y, 2.0);
|
||||
|
||||
var d = int32x4(0xabcdef12, 0x3ff00000, 0x21fedcba, 0x40000000);
|
||||
var f = float64x2.fromInt32x4Bits(d);
|
||||
assertEq(f.x, 1.0000006400213732);
|
||||
assertEq(f.y, 2.0000002532866263);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float64x2 getters';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// Create a float64x2 and check that the getters work:
|
||||
var f = float64x2(11, 22);
|
||||
assertEq(f.x, 11);
|
||||
assertEq(f.y, 22);
|
||||
|
||||
// Test that the getters work when called reflectively:
|
||||
var g = f.__lookupGetter__("x");
|
||||
assertEq(g.call(f), 11);
|
||||
|
||||
// Test that getters cannot be applied to various incorrect things:
|
||||
assertThrowsInstanceOf(function() {
|
||||
g.call({})
|
||||
}, TypeError, "Getter applicable to random objects");
|
||||
assertThrowsInstanceOf(function() {
|
||||
g.call(0xDEADBEEF)
|
||||
}, TypeError, "Getter applicable to integers");
|
||||
assertThrowsInstanceOf(function() {
|
||||
var T = new TypedObject.StructType({x: TypedObject.float64,
|
||||
y: TypedObject.float64});
|
||||
var v = new T({x: 11, y: 22});
|
||||
g.call(v)
|
||||
}, TypeError, "Getter applicable to structs");
|
||||
assertThrowsInstanceOf(function() {
|
||||
var t = new int32x4(1, 2, 3, 4);
|
||||
g.call(t)
|
||||
}, TypeError, "Getter applicable to int32x4");
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
|
@ -0,0 +1,51 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float64x2 handles';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var ArrayType = TypedObject.ArrayType;
|
||||
|
||||
var float64 = TypedObject.float64;
|
||||
var Handle = TypedObject.Handle;
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var Array = float64x2.array(3);
|
||||
var array = new Array([float64x2(1, 2),
|
||||
float64x2(3, 4),
|
||||
float64x2(5, 6)]);
|
||||
|
||||
// Test that trying to create handle into the interior of a
|
||||
// float64x2 fails.
|
||||
|
||||
assertThrowsInstanceOf(function() {
|
||||
var h = float64.handle(array, 1, "w");
|
||||
}, TypeError, "Creating a float64 handle to prop via ctor");
|
||||
|
||||
assertThrowsInstanceOf(function() {
|
||||
var h = float64.handle();
|
||||
Handle.move(h, array, 1, "w");
|
||||
}, TypeError, "Creating a float64 handle to prop via move");
|
||||
|
||||
assertThrowsInstanceOf(function() {
|
||||
var h = float64.handle(array, 1, 0);
|
||||
}, TypeError, "Creating a float64 handle to elem via ctor");
|
||||
|
||||
assertThrowsInstanceOf(function() {
|
||||
var h = float64.handle();
|
||||
Handle.move(h, array, 1, 0);
|
||||
}, TypeError, "Creating a float64 handle to elem via move");
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
|
@ -0,0 +1,36 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
|
||||
var summary = 'float64x2 reify';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var ArrayType = TypedObject.ArrayType;
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var Array = float64x2.array(3);
|
||||
var array = new Array([float64x2(1, 2),
|
||||
float64x2(3, 4),
|
||||
float64x2(5, 6)]);
|
||||
|
||||
// Test that reading array[1] produces a *copy* of float64x2, not an
|
||||
// alias into the array.
|
||||
|
||||
var f = array[1];
|
||||
assertEq(f.y, 4);
|
||||
assertEq(array[1].y, 4);
|
||||
array[1] = float64x2(7, 8);
|
||||
assertEq(f.y, 4);
|
||||
assertEq(array[1].y, 8);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
|
@ -0,0 +1,45 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
|
||||
var summary = 'float64x2 setter';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var ArrayType = TypedObject.ArrayType;
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var Array = float64x2.array(3);
|
||||
var array = new Array([float64x2(1, 2),
|
||||
float64x2(3, 4),
|
||||
float64x2(5, 6)]);
|
||||
assertEq(array[1].y, 4);
|
||||
|
||||
// Test that we are allowed to write float64x2 values into array,
|
||||
// but not other things.
|
||||
|
||||
array[1] = float64x2(7, 8);
|
||||
assertEq(array[1].y, 8);
|
||||
|
||||
assertThrowsInstanceOf(function() {
|
||||
array[1] = {x: 7, y: 8 };
|
||||
}, TypeError, "Setting float64x2 from an object");
|
||||
|
||||
assertThrowsInstanceOf(function() {
|
||||
array[1] = [ 7, 8 ];
|
||||
}, TypeError, "Setting float64x2 from an array");
|
||||
|
||||
assertThrowsInstanceOf(function() {
|
||||
array[1] = 9;
|
||||
}, TypeError, "Setting float64x2 from a number");
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
|
@ -0,0 +1,36 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
|
||||
var summary = 'float64x2 with';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = float64x2(1, 2);
|
||||
var x = float64x2.withX(a, 5);
|
||||
var y = float64x2.withY(a, 5);
|
||||
assertEq(x.x, 5);
|
||||
assertEq(x.y, 2);
|
||||
assertEq(y.x, 1);
|
||||
assertEq(y.y, 5);
|
||||
|
||||
var b = float64x2(NaN, -0);
|
||||
var x1 = float64x2.withX(b, Infinity);
|
||||
var y1 = float64x2.withY(b, -Infinity);
|
||||
assertEq(x1.x, Infinity);
|
||||
assertEq(x1.y, -0);
|
||||
assertEq(y1.x, NaN);
|
||||
assertEq(y1.y, -Infinity);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'int32x4 fromFloat64x2';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = float64x2(1, 2.2);
|
||||
var c = int32x4.fromFloat64x2(a);
|
||||
assertEq(c.x, 1);
|
||||
assertEq(c.y, 2);
|
||||
assertEq(c.z, 0);
|
||||
assertEq(c.w, 0);
|
||||
|
||||
var d = float64x2(NaN, -0);
|
||||
var f = int32x4.fromFloat64x2(d);
|
||||
assertEq(f.x, 0);
|
||||
assertEq(f.y, 0);
|
||||
assertEq(f.z, 0);
|
||||
assertEq(f.w, 0);
|
||||
|
||||
var g = float64x2(Infinity, -Infinity);
|
||||
var i = int32x4.fromFloat64x2(g);
|
||||
assertEq(i.x, 0);
|
||||
assertEq(i.y, 0);
|
||||
assertEq(i.z, 0);
|
||||
assertEq(i.w, 0);
|
||||
|
||||
var j = Math.pow(2, 31);
|
||||
var k = -Math.pow(2, 31) - 1;
|
||||
var m = float64x2(j, k);
|
||||
var l = int32x4.fromFloat64x2(m);
|
||||
assertEq(l.x, INT32_MIN);
|
||||
assertEq(l.y, INT32_MAX);
|
||||
assertEq(l.z, 0);
|
||||
assertEq(l.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1031203;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'int32x4 fromFloat64x2Bits';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = float64x2(1.0, 2.0);
|
||||
var c = int32x4.fromFloat64x2Bits(a);
|
||||
assertEq(c.x, 0x00000000);
|
||||
assertEq(c.y, 0x3FF00000);
|
||||
assertEq(c.z, 0x00000000);
|
||||
assertEq(c.w, 0x40000000);
|
||||
|
||||
var d = float64x2(+Infinity, -Infinity);
|
||||
var f = int32x4.fromFloat64x2Bits(d);
|
||||
assertEq(f.x, 0x00000000);
|
||||
assertEq(f.y, 0x7ff00000);
|
||||
assertEq(f.z, 0x00000000);
|
||||
assertEq(f.w, -0x100000);
|
||||
|
||||
var g = float64x2(-0, NaN);
|
||||
var i = int32x4.fromFloat64x2Bits(g);
|
||||
assertEq(i.x, 0x00000000);
|
||||
assertEq(i.y, -0x80000000);
|
||||
assertEq(i.z, 0x00000000);
|
||||
assertEq(i.w, 0x7ff80000);
|
||||
|
||||
var j = float64x2(1.0000006400213732, 2.0000002532866263);
|
||||
var l = int32x4.fromFloat64x2Bits(j);
|
||||
assertEq(l.x, -0x543210ee);
|
||||
assertEq(l.y, 0x3ff00000);
|
||||
assertEq(l.z, 0x21fedcba);
|
||||
assertEq(l.w, 0x40000000);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -5,11 +5,11 @@
|
|||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Our float32array will have 16 elements
|
||||
const SIZE_ARRAY = 16;
|
||||
// Our array for int32x4 and float32x4 will have 16 elements
|
||||
const SIZE_32_ARRAY = 16;
|
||||
const SIZE_64_ARRAY = 8;
|
||||
|
||||
// 1 float32 == 4 bytes
|
||||
const SIZE_BYTES = SIZE_ARRAY * 4;
|
||||
const SIZE_BYTES = SIZE_32_ARRAY * 4;
|
||||
|
||||
function MakeComparator(kind, arr) {
|
||||
var bpe = arr.BYTES_PER_ELEMENT;
|
||||
|
@ -28,10 +28,14 @@ function MakeComparator(kind, arr) {
|
|||
sizeOfLaneElem = 4;
|
||||
typedArrayCtor = Float32Array;
|
||||
break;
|
||||
case 'float64x2':
|
||||
sizeOfLaneElem = 8;
|
||||
typedArrayCtor = Float64Array;
|
||||
break;
|
||||
default:
|
||||
assertEq(true, false, "unknown SIMD kind");
|
||||
}
|
||||
|
||||
var lanes = 16 / sizeOfLaneElem;
|
||||
// Reads (numElemToRead * sizeOfLaneElem) bytes in arr, and reinterprets
|
||||
// these bytes as a typed array equivalent to the typed SIMD vector.
|
||||
var slice = function(start, numElemToRead) {
|
||||
|
@ -49,31 +53,38 @@ function MakeComparator(kind, arr) {
|
|||
return new typedArrayCtor(new Uint8Array(asArray).buffer);
|
||||
}
|
||||
|
||||
var assertFunc = (lanes == 2) ? assertEqX2 : assertEqX4;
|
||||
var type = SIMD[kind];
|
||||
return {
|
||||
loadX: function(index) {
|
||||
var v = SIMD[kind].loadX(arr, index);
|
||||
assertEqX4(v, slice(index, 1));
|
||||
var v = type.loadX(arr, index);
|
||||
assertFunc(v, slice(index, 1));
|
||||
},
|
||||
|
||||
loadXY: function(index) {
|
||||
var v = SIMD[kind].loadXY(arr, index);
|
||||
assertEqX4(v, slice(index, 2));
|
||||
if (lanes < 4)
|
||||
return;
|
||||
var v = type.loadXY(arr, index);
|
||||
assertFunc(v, slice(index, 2));
|
||||
},
|
||||
|
||||
loadXYZ: function(index) {
|
||||
var v = SIMD[kind].loadXYZ(arr, index);
|
||||
assertEqX4(v, slice(index, 3));
|
||||
if (lanes < 4)
|
||||
return;
|
||||
var v = type.loadXYZ(arr, index);
|
||||
assertFunc(v, slice(index, 3));
|
||||
},
|
||||
|
||||
load: function(index) {
|
||||
var v = SIMD[kind].load(arr, index);
|
||||
assertEqX4(v, slice(index, 4));
|
||||
var v = type.load(arr, index);
|
||||
assertFunc(v, slice(index, 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testLoad(kind, TA) {
|
||||
for (var i = SIZE_ARRAY; i--;)
|
||||
var lanes = TA.length / 4;
|
||||
for (var i = TA.length; i--;)
|
||||
TA[i] = i;
|
||||
|
||||
for (var ta of [
|
||||
|
@ -97,7 +108,7 @@ function testLoad(kind, TA) {
|
|||
var C = MakeComparator(kind, ta);
|
||||
var bpe = ta.BYTES_PER_ELEMENT;
|
||||
|
||||
var lastValidArgLoadX = (SIZE_BYTES - 4) / bpe | 0;
|
||||
var lastValidArgLoadX = (SIZE_BYTES - (lanes == 4 ? 4 : 8)) / bpe | 0;
|
||||
var lastValidArgLoadXY = (SIZE_BYTES - 8) / bpe | 0;
|
||||
var lastValidArgLoadXYZ = (SIZE_BYTES - 12) / bpe | 0;
|
||||
var lastValidArgLoad = (SIZE_BYTES - 16) / bpe | 0;
|
||||
|
@ -121,16 +132,20 @@ function testLoad(kind, TA) {
|
|||
C.loadXY(2);
|
||||
C.loadXY(3);
|
||||
C.loadXY(lastValidArgLoadXY);
|
||||
assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), RangeError);
|
||||
|
||||
C.loadXYZ(0);
|
||||
C.loadXYZ(1);
|
||||
C.loadXYZ(2);
|
||||
C.loadXYZ(3);
|
||||
C.loadXYZ(lastValidArgLoadXYZ);
|
||||
|
||||
if (lanes >= 4) {
|
||||
assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), RangeError);
|
||||
assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), RangeError);
|
||||
}
|
||||
}
|
||||
|
||||
if (lanes == 4) {
|
||||
// Test ToInt32 behavior
|
||||
var v = SIMD[kind].load(TA, 12.5);
|
||||
assertEqX4(v, [12, 13, 14, 15]);
|
||||
|
@ -140,6 +155,7 @@ function testLoad(kind, TA) {
|
|||
}
|
||||
var v = SIMD[kind].load(TA, obj);
|
||||
assertEqX4(v, [12, 13, 14, 15]);
|
||||
}
|
||||
|
||||
var obj = {
|
||||
valueOf: function() { throw new TypeError("i ain't a number"); }
|
||||
|
@ -147,8 +163,9 @@ function testLoad(kind, TA) {
|
|||
assertThrowsInstanceOf(() => SIMD[kind].load(TA, obj), TypeError);
|
||||
}
|
||||
|
||||
testLoad('float32x4', new Float32Array(SIZE_ARRAY));
|
||||
testLoad('int32x4', new Int32Array(SIZE_ARRAY));
|
||||
testLoad('float32x4', new Float32Array(SIZE_32_ARRAY));
|
||||
testLoad('float64x2', new Float64Array(SIZE_64_ARRAY));
|
||||
testLoad('int32x4', new Int32Array(SIZE_32_ARRAY));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
|
||||
function testMaxFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.max, (x, y) => Math.fround(Math.max(x, y)), 4);
|
||||
}
|
||||
function testMinFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.min, (x, y) => Math.fround(Math.min(x, y)), 4);
|
||||
}
|
||||
|
||||
function testMaxFloat64(v, w) {
|
||||
return testBinaryFunc(v, w, float64x2.max, (x, y) => Math.max(x, y), 2);
|
||||
}
|
||||
function testMinFloat64(v, w) {
|
||||
return testBinaryFunc(v, w, float64x2.min, (x, y) => Math.min(x, y), 2);
|
||||
}
|
||||
|
||||
function maxNum(x, y) {
|
||||
if (x != x)
|
||||
return y;
|
||||
if (y != y)
|
||||
return x;
|
||||
return Math.max(x, y);
|
||||
}
|
||||
|
||||
function minNum(x, y) {
|
||||
if (x != x)
|
||||
return y;
|
||||
if (y != y)
|
||||
return x;
|
||||
return Math.min(x, y);
|
||||
}
|
||||
|
||||
function testMaxNumFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.maxNum, maxNum, 4);
|
||||
}
|
||||
function testMinNumFloat32(v, w) {
|
||||
return testBinaryFunc(v, w, float32x4.minNum, minNum, 4);
|
||||
}
|
||||
|
||||
function testMaxNumFloat64(v, w) {
|
||||
return testBinaryFunc(v, w, float64x2.maxNum, maxNum, 2);
|
||||
}
|
||||
function testMinNumFloat64(v, w) {
|
||||
return testBinaryFunc(v, w, float64x2.minNum, minNum, 2);
|
||||
}
|
||||
|
||||
function test() {
|
||||
var v, w;
|
||||
for ([v, w] of [[float32x4(1, 20, 30, 4), float32x4(10, 2, 3, 40)],
|
||||
[float32x4(9.999, 2.1234, 30.4443, 4), float32x4(10, 2.1233, 30.4444, 4.0001)],
|
||||
[float32x4(NaN, -Infinity, +Infinity, -0), float32x4(13.37, 42.42, NaN, 0)]])
|
||||
{
|
||||
testMinFloat32(v, w);
|
||||
testMaxFloat32(v, w);
|
||||
testMinNumFloat32(v, w);
|
||||
testMaxNumFloat32(v, w);
|
||||
}
|
||||
|
||||
for ([v, w] of [[float64x2(1, 20), float64x2(10, 2)],
|
||||
[float64x2(30, 4), float64x2(3, 40)],
|
||||
[float64x2(9.999, 2.1234), float64x2(10, 2.1233)],
|
||||
[float64x2(30.4443, 4), float64x2(30.4444, 4.0001)],
|
||||
[float64x2(NaN, -Infinity), float64x2(13.37, 42.42)],
|
||||
[float64x2(+Infinity, -0), float64x2(NaN, 0)]])
|
||||
{
|
||||
testMinFloat64(v, w);
|
||||
testMaxFloat64(v, w);
|
||||
testMinNumFloat64(v, w);
|
||||
testMaxNumFloat64(v, w);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
function select(mask, ifTrue, ifFalse) {
|
||||
|
@ -27,38 +28,38 @@ function testSelect(type, inputs) {
|
|||
for (var i = 0; i < 16; i++) {
|
||||
var mask = int32x4.bool(!!(i & 1), !!((i >> 1) & 1), !!((i >> 2) & 1), !!((i >> 3) & 1));
|
||||
for ([x, y] of inputs)
|
||||
assertEqX4(type.select(mask, x, y), select(mask, x, y));
|
||||
assertEqVec(type.select(mask, x, y), select(mask, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
function bitselect(ScalarTypedArray, mask, ifTrue, ifFalse) {
|
||||
var m = simdToArray(mask);
|
||||
|
||||
var tv = new ScalarTypedArray(simdToArray(ifTrue));
|
||||
var fv = new ScalarTypedArray(simdToArray(ifFalse));
|
||||
|
||||
tv = new Int32Array(tv.buffer);
|
||||
fv = new Int32Array(fv.buffer);
|
||||
var res = new Int32Array(4);
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var t = 0;
|
||||
for (var bit = 0; bit < 32; bit++) {
|
||||
var readVal = (m[i] >> bit) & 1 ? tv[i] : fv[i];
|
||||
var readBit = (readVal >> bit) & 1;
|
||||
t |= readBit << bit;
|
||||
}
|
||||
res[i] = t;
|
||||
function int32x4FromTypeBits(type, vec) {
|
||||
switch (type) {
|
||||
case float32x4:
|
||||
return int32x4.fromFloat32x4Bits(vec);
|
||||
case float64x2:
|
||||
return int32x4.fromFloat64x2Bits(vec);
|
||||
case int32x4:
|
||||
return vec;
|
||||
default:
|
||||
throw new TypeError("Unknown SIMD type.");
|
||||
}
|
||||
}
|
||||
|
||||
res = new ScalarTypedArray(res.buffer);
|
||||
return Array.prototype.map.call(res, x => x);
|
||||
function bitselect(type, mask, ifTrue, ifFalse) {
|
||||
var tv = int32x4FromTypeBits(type, ifTrue);
|
||||
var fv = int32x4FromTypeBits(type, ifFalse);
|
||||
var tr = int32x4.and(mask, tv);
|
||||
var fr = int32x4.and(int32x4.not(mask), fv);
|
||||
var orApplied = int32x4.or(tr, fr);
|
||||
var converted = type == int32x4 ? orApplied : type.fromInt32x4Bits(orApplied);
|
||||
return simdToArray(converted);
|
||||
}
|
||||
|
||||
function findCorrespondingScalarTypedArray(type) {
|
||||
switch (type) {
|
||||
case int32x4: return Int32Array;
|
||||
case float32x4: return Float32Array;
|
||||
case float64x2: return Float64Array;
|
||||
default: throw new Error("undefined scalar typed array");
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +74,7 @@ function testBitSelectSimple(type, inputs) {
|
|||
for (var i = 0; i < 16; i++) {
|
||||
var mask = int32x4.bool(!!(i & 1), !!((i >> 1) & 1), !!((i >> 2) & 1), !!((i >> 3) & 1));
|
||||
for ([x, y] of inputs)
|
||||
assertEqX4(type.bitselect(mask, x, y), bitselect(ScalarTypedArray, mask, x, y));
|
||||
assertEqVec(type.bitselect(mask, x, y), bitselect(type, mask, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +92,7 @@ function testBitSelectComplex(type, inputs) {
|
|||
var ScalarTypedArray = findCorrespondingScalarTypedArray(type);
|
||||
for (var mask of masks) {
|
||||
for ([x, y] of inputs)
|
||||
assertEqX4(type.bitselect(mask, x, y), bitselect(ScalarTypedArray, mask, x, y));
|
||||
assertEqVec(type.bitselect(mask, x, y), bitselect(type, mask, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +116,19 @@ function test() {
|
|||
testBitSelectSimple(float32x4, inputs);
|
||||
testBitSelectComplex(float32x4, inputs);
|
||||
|
||||
inputs = [
|
||||
[float64x2(0.125,4.25), float64x2(9.75,16.125)],
|
||||
[float64x2(1.5,2.75), float64x2(3.25,4.5)],
|
||||
[float64x2(-1.5,-0), float64x2(NaN,-Infinity)],
|
||||
[float64x2(1,-2), float64x2(13.37,3.13)],
|
||||
[float64x2(1.5,2.75), float64x2(NaN,Infinity)],
|
||||
[float64x2(-NaN,-Infinity), float64x2(9.75,16.125)]
|
||||
];
|
||||
|
||||
testSelect(float64x2, inputs);
|
||||
testBitSelectSimple(float64x2, inputs);
|
||||
testBitSelectComplex(float64x2, inputs);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
function assertEqX2(v, arr) {
|
||||
try {
|
||||
assertEq(v.x, arr[0]);
|
||||
assertEq(v.y, arr[1]);
|
||||
} catch (e) {
|
||||
print("stack trace:", e.stack);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqX4(v, arr) {
|
||||
try {
|
||||
assertEq(v.x, arr[0]);
|
||||
|
@ -10,14 +20,51 @@ function assertEqX4(v, arr) {
|
|||
}
|
||||
}
|
||||
|
||||
function simdLength(v) {
|
||||
var pt = Object.getPrototypeOf(v);
|
||||
if (pt === SIMD.int32x4.prototype || pt === SIMD.float32x4.prototype) {
|
||||
return 4;
|
||||
} else if (pt === SIMD.float64x2.prototype) {
|
||||
return 2;
|
||||
} else {
|
||||
throw new TypeError("Unknown SIMD kind.");
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqVec(v, arr) {
|
||||
var lanes = simdLength(v);
|
||||
if (lanes == 4)
|
||||
assertEqX4(v, arr);
|
||||
else if (lanes == 2)
|
||||
assertEqX2(v, arr);
|
||||
else
|
||||
throw new TypeError("Unknown SIMD kind.");
|
||||
}
|
||||
|
||||
function simdToArray(v) {
|
||||
var lanes = simdLength(v);
|
||||
if (lanes == 4)
|
||||
return [v.x, v.y, v.z, v.w];
|
||||
else if (lanes == 2)
|
||||
return [v.x, v.y];
|
||||
else
|
||||
throw new TypeError("Unknown SIMD kind.");
|
||||
}
|
||||
|
||||
const INT32_MAX = Math.pow(2, 31) - 1;
|
||||
const INT32_MIN = -Math.pow(2, 31);
|
||||
assertEq(INT32_MAX + 1 | 0, INT32_MIN);
|
||||
|
||||
function testUnaryFunc(v, simdFunc, func) {
|
||||
var varr = simdToArray(v);
|
||||
|
||||
var observed = simdToArray(simdFunc(v));
|
||||
var expected = varr.map(function(v, i) { return func(varr[i]); });
|
||||
|
||||
for (var i = 0; i < observed.length; i++)
|
||||
assertEq(observed[i], expected[i]);
|
||||
}
|
||||
|
||||
function testBinaryFunc(v, w, simdFunc, func) {
|
||||
var varr = simdToArray(v);
|
||||
var warr = simdToArray(w);
|
||||
|
@ -29,6 +76,19 @@ function testBinaryFunc(v, w, simdFunc, func) {
|
|||
assertEq(observed[i], expected[i]);
|
||||
}
|
||||
|
||||
function testBinaryCompare(v, w, simdFunc, func) {
|
||||
var varr = simdToArray(v);
|
||||
var warr = simdToArray(w);
|
||||
|
||||
var inLanes = simdLength(v);
|
||||
var observed = simdToArray(simdFunc(v, w));
|
||||
assertEq(observed.length, 4);
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var j = ((i * inLanes) / 4) | 0;
|
||||
assertEq(observed[i], func(varr[j], warr[j]));
|
||||
}
|
||||
}
|
||||
|
||||
function testBinaryScalarFunc(v, scalar, simdFunc, func) {
|
||||
var varr = simdToArray(v);
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ function testStore(ta, kind, i, v) {
|
|||
SIMD[kind].storeX(ta, i, v);
|
||||
assertChanged(ta, i, [v.x]);
|
||||
|
||||
reset(ta);
|
||||
SIMD[kind].store(ta, i, v);
|
||||
assertChanged(ta, i, simdToArray(v));
|
||||
|
||||
if (simdLength(v) > 2) {
|
||||
reset(ta);
|
||||
SIMD[kind].storeXY(ta, i, v);
|
||||
assertChanged(ta, i, [v.x, v.y]);
|
||||
|
@ -38,10 +43,7 @@ function testStore(ta, kind, i, v) {
|
|||
reset(ta);
|
||||
SIMD[kind].storeXYZ(ta, i, v);
|
||||
assertChanged(ta, i, [v.x, v.y, v.z]);
|
||||
|
||||
reset(ta);
|
||||
SIMD[kind].store(ta, i, v);
|
||||
assertChanged(ta, i, [v.x, v.y, v.z, v.w]);
|
||||
}
|
||||
}
|
||||
|
||||
function testStoreInt32x4() {
|
||||
|
@ -78,8 +80,32 @@ function testStoreFloat32x4() {
|
|||
assertThrowsInstanceOf(() => SIMD.int32x4.store(F32, 0, v), TypeError);
|
||||
}
|
||||
|
||||
function testStoreFloat64x2() {
|
||||
var F64 = new Float64Array(16);
|
||||
|
||||
var v = SIMD.float64x2(1, 2);
|
||||
testStore(F64, 'float64x2', 0, v);
|
||||
testStore(F64, 'float64x2', 1, v);
|
||||
testStore(F64, 'float64x2', 14, v);
|
||||
|
||||
var v = SIMD.float64x2(NaN, -0);
|
||||
testStore(F64, 'float64x2', 0, v);
|
||||
testStore(F64, 'float64x2', 1, v);
|
||||
testStore(F64, 'float64x2', 14, v);
|
||||
|
||||
var v = SIMD.float64x2(-Infinity, +Infinity);
|
||||
testStore(F64, 'float64x2', 0, v);
|
||||
testStore(F64, 'float64x2', 1, v);
|
||||
testStore(F64, 'float64x2', 14, v);
|
||||
|
||||
assertThrowsInstanceOf(() => SIMD.float64x2.store(F64), TypeError);
|
||||
assertThrowsInstanceOf(() => SIMD.float64x2.store(F64, 0), TypeError);
|
||||
assertThrowsInstanceOf(() => SIMD.float32x4.store(F64, 0, v), TypeError);
|
||||
}
|
||||
|
||||
testStoreInt32x4();
|
||||
testStoreFloat32x4();
|
||||
testStoreFloat64x2();
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -6,28 +6,61 @@
|
|||
*/
|
||||
|
||||
var float32x4 = SIMD.float32x4;
|
||||
var float64x2 = SIMD.float64x2;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
function swizzle(arr, x, y, z, w) {
|
||||
function swizzle2(arr, x, y) {
|
||||
return [arr[x], arr[y]];
|
||||
}
|
||||
|
||||
function swizzle4(arr, x, y, z, w) {
|
||||
return [arr[x], arr[y], arr[z], arr[w]];
|
||||
}
|
||||
|
||||
function getNumberOfLanesFromType(type) {
|
||||
switch (type) {
|
||||
case float32x4:
|
||||
case int32x4:
|
||||
return 4;
|
||||
case float64x2:
|
||||
return 2;
|
||||
}
|
||||
throw new TypeError("Unknown SIMD type.");
|
||||
}
|
||||
|
||||
function testSwizzleForType(type) {
|
||||
var v = type(1,2,3,4);
|
||||
var lanes = getNumberOfLanesFromType(type);
|
||||
var v = lanes == 4 ? type(1, 2, 3, 4) : type(1, 2);
|
||||
|
||||
assertThrowsInstanceOf(() => type.swizzle() , TypeError);
|
||||
assertThrowsInstanceOf(() => type.swizzle(v, 0) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.swizzle(v, 0, 1) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, 4) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, -1) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.swizzle(0, 1, 2, 3, v) , TypeError);
|
||||
|
||||
if (lanes == 2) {
|
||||
assertThrowsInstanceOf(() => type.swizzle(v, 0, -1) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.swizzle(v, 0, 2) , TypeError);
|
||||
} else {
|
||||
assertEq(lanes, 4);
|
||||
assertThrowsInstanceOf(() => type.swizzle(v, 0, 1), TypeError);
|
||||
}
|
||||
|
||||
// Test all possible swizzles.
|
||||
if (lanes == 4) {
|
||||
var x, y, z, w;
|
||||
for (var i = 0; i < Math.pow(4, 4); i++) {
|
||||
[x, y, z, w] = [i & 3, (i >> 2) & 3, (i >> 4) & 3, (i >> 6) & 3];
|
||||
assertEqX4(type.swizzle(v, x, y, z, w), swizzle(simdToArray(v), x, y, z, w));
|
||||
assertEqVec(type.swizzle(v, x, y, z, w), swizzle4(simdToArray(v), x, y, z, w));
|
||||
}
|
||||
} else {
|
||||
assertEq(lanes, 2);
|
||||
var x, y;
|
||||
for (var i = 0; i < Math.pow(2, 2); i++) {
|
||||
[x, y] = [x & 1, (y >> 1) & 1];
|
||||
assertEqVec(type.swizzle(v, x, y), swizzle2(simdToArray(v), x, y));
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the lane inputs are converted into an int32.
|
||||
|
@ -36,7 +69,12 @@ function testSwizzleForType(type) {
|
|||
x: 0,
|
||||
valueOf: function() { return this.x++ }
|
||||
};
|
||||
assertEqX4(type.swizzle(v, obj, obj, obj, obj), swizzle(simdToArray(v), 0, 1, 2, 3));
|
||||
if (lanes == 4) {
|
||||
assertEqVec(type.swizzle(v, obj, obj, obj, obj), swizzle4(simdToArray(v), 0, 1, 2, 3));
|
||||
} else {
|
||||
assertEq(lanes, 2);
|
||||
assertEqVec(type.swizzle(v, obj, obj), swizzle2(simdToArray(v), 0, 1));
|
||||
}
|
||||
|
||||
// Object for which ToInt32 will fail.
|
||||
obj = {
|
||||
|
@ -65,7 +103,21 @@ function testSwizzleFloat32x4() {
|
|||
testSwizzleForType(float32x4);
|
||||
}
|
||||
|
||||
function shuffle(lhsa, rhsa, x, y, z, w) {
|
||||
function testSwizzleFloat64x2() {
|
||||
var v = float64x2(1, 2);
|
||||
|
||||
assertThrowsInstanceOf(function() {
|
||||
float32x4.swizzle(v, 0, 0, 0, 0);
|
||||
}, TypeError);
|
||||
|
||||
testSwizzleForType(float64x2);
|
||||
}
|
||||
|
||||
function shuffle2(lhsa, rhsa, x, y) {
|
||||
return [(x < 2 ? lhsa : rhsa)[x % 2],
|
||||
(y < 2 ? lhsa : rhsa)[y % 2]];
|
||||
}
|
||||
function shuffle4(lhsa, rhsa, x, y, z, w) {
|
||||
return [(x < 4 ? lhsa : rhsa)[x % 4],
|
||||
(y < 4 ? lhsa : rhsa)[y % 4],
|
||||
(z < 4 ? lhsa : rhsa)[z % 4],
|
||||
|
@ -73,24 +125,50 @@ function shuffle(lhsa, rhsa, x, y, z, w) {
|
|||
}
|
||||
|
||||
function testShuffleForType(type) {
|
||||
var lhs = type(1,2,3,4);
|
||||
var rhs = type(5,6,7,8);
|
||||
var lanes = getNumberOfLanesFromType(type);
|
||||
var lhs, rhs;
|
||||
if (lanes == 4) {
|
||||
lhs = type(1, 2, 3, 4);
|
||||
rhs = type(5, 6, 7, 8);
|
||||
} else {
|
||||
assertEq(lanes, 2);
|
||||
lhs = type(1, 2);
|
||||
rhs = type(3, 4);
|
||||
}
|
||||
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, rhs) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, -1) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, 8) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, 0, 1, 2, 7, rhs) , TypeError);
|
||||
|
||||
if (lanes == 2) {
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 4) , TypeError);
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, -1) , TypeError);
|
||||
} else {
|
||||
assertEq(lanes, 4);
|
||||
assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1) , TypeError);
|
||||
}
|
||||
|
||||
// Test all possible shuffles.
|
||||
var x, y, z, w;
|
||||
if (lanes == 4) {
|
||||
var x, y, z, w;
|
||||
for (var i = 0; i < Math.pow(8, 4); i++) {
|
||||
[x, y, z, w] = [i & 7, (i >> 3) & 7, (i >> 6) & 7, (i >> 9) & 7];
|
||||
assertEqX4(type.shuffle(lhs, rhs, x, y, z, w),
|
||||
shuffle(simdToArray(lhs), simdToArray(rhs), x, y, z, w));
|
||||
assertEqVec(type.shuffle(lhs, rhs, x, y, z, w),
|
||||
shuffle4(simdToArray(lhs), simdToArray(rhs), x, y, z, w));
|
||||
}
|
||||
} else {
|
||||
assertEq(lanes, 2);
|
||||
var x, y;
|
||||
for (var i = 0; i < Math.pow(4, 2); i++) {
|
||||
[x, y] = [i & 3, (i >> 3) & 3];
|
||||
assertEqVec(type.shuffle(lhs, rhs, x, y),
|
||||
shuffle2(simdToArray(lhs), simdToArray(rhs), x, y));
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the lane inputs are converted into an int32.
|
||||
|
@ -99,8 +177,14 @@ function testShuffleForType(type) {
|
|||
x: 0,
|
||||
valueOf: function() { return this.x++ }
|
||||
};
|
||||
assertEqX4(type.shuffle(lhs, rhs, obj, obj, obj, obj),
|
||||
shuffle(simdToArray(lhs),simdToArray(rhs), 0, 1, 2, 3));
|
||||
if (lanes == 4) {
|
||||
assertEqVec(type.shuffle(lhs, rhs, obj, obj, obj, obj),
|
||||
shuffle4(simdToArray(lhs), simdToArray(rhs), 0, 1, 2, 3));
|
||||
} else {
|
||||
assertEq(lanes, 2);
|
||||
assertEqVec(type.shuffle(lhs, rhs, obj, obj),
|
||||
shuffle2(simdToArray(lhs), simdToArray(rhs), 0, 1));
|
||||
}
|
||||
|
||||
// Object for which ToInt32 will fail.
|
||||
obj = {
|
||||
|
@ -129,10 +213,22 @@ function testShuffleFloat32x4() {
|
|||
testShuffleForType(float32x4);
|
||||
}
|
||||
|
||||
function testShuffleFloat64x2() {
|
||||
var v = float64x2(1, 2);
|
||||
|
||||
assertThrowsInstanceOf(function() {
|
||||
float32x4.shuffle(v, v, 0, 0, 0, 0);
|
||||
}, TypeError);
|
||||
|
||||
testShuffleForType(float64x2);
|
||||
}
|
||||
|
||||
testSwizzleInt32x4();
|
||||
testSwizzleFloat32x4();
|
||||
testSwizzleFloat64x2();
|
||||
testShuffleInt32x4();
|
||||
testShuffleFloat32x4();
|
||||
testShuffleFloat64x2();
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
macro(float32, float32, "float32") \
|
||||
macro(float32x4, float32x4, "float32x4") \
|
||||
macro(float64, float64, "float64") \
|
||||
macro(float64x2, float64x2, "float64x2") \
|
||||
macro(forceInterpreter, forceInterpreter, "forceInterpreter") \
|
||||
macro(format, format, "format") \
|
||||
macro(frame, frame, "frame") \
|
||||
|
|
|
@ -105,7 +105,8 @@ class GlobalObject : public NativeObject
|
|||
static const unsigned DEBUGGERS = RUNTIME_CODEGEN_ENABLED + 1;
|
||||
static const unsigned INTRINSICS = DEBUGGERS + 1;
|
||||
static const unsigned FLOAT32X4_TYPE_DESCR = INTRINSICS + 1;
|
||||
static const unsigned INT32X4_TYPE_DESCR = FLOAT32X4_TYPE_DESCR + 1;
|
||||
static const unsigned FLOAT64X2_TYPE_DESCR = FLOAT32X4_TYPE_DESCR + 1;
|
||||
static const unsigned INT32X4_TYPE_DESCR = FLOAT64X2_TYPE_DESCR + 1;
|
||||
static const unsigned FOR_OF_PIC_CHAIN = INT32X4_TYPE_DESCR + 1;
|
||||
|
||||
/* Total reserved-slot count for global objects. */
|
||||
|
@ -408,6 +409,16 @@ class GlobalObject : public NativeObject
|
|||
return getSlotRef(FLOAT32X4_TYPE_DESCR).toObject();
|
||||
}
|
||||
|
||||
void setFloat64x2TypeDescr(JSObject &obj) {
|
||||
MOZ_ASSERT(getSlotRef(FLOAT64X2_TYPE_DESCR).isUndefined());
|
||||
setSlot(FLOAT64X2_TYPE_DESCR, ObjectValue(obj));
|
||||
}
|
||||
|
||||
JSObject &float64x2TypeDescr() {
|
||||
MOZ_ASSERT(getSlotRef(FLOAT64X2_TYPE_DESCR).isObject());
|
||||
return getSlotRef(FLOAT64X2_TYPE_DESCR).toObject();
|
||||
}
|
||||
|
||||
void setInt32x4TypeDescr(JSObject &obj) {
|
||||
MOZ_ASSERT(getSlotRef(INT32X4_TYPE_DESCR).isUndefined());
|
||||
setSlot(INT32X4_TYPE_DESCR, ObjectValue(obj));
|
||||
|
|
|
@ -969,6 +969,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
|||
JS_FN("ClampToUint8", js::ClampToUint8, 1, 0),
|
||||
JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0),
|
||||
JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0),
|
||||
JS_FN("GetFloat64x2TypeDescr", js::GetFloat64x2TypeDescr, 0, 0),
|
||||
JS_FN("GetInt32x4TypeDescr", js::GetInt32x4TypeDescr, 0, 0),
|
||||
|
||||
#define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name) \
|
||||
|
|
|
@ -584,18 +584,30 @@ SelectionCarets::SelectWord()
|
|||
nsPoint ptInFrame = mDownPoint;
|
||||
nsLayoutUtils::TransformPoint(rootFrame, ptFrame, ptInFrame);
|
||||
|
||||
// If target frame is editable, we should move focus to targe frame. If
|
||||
// target frame isn't editable and our focus content is editable, we should
|
||||
nsIFrame* currFrame = ptFrame;
|
||||
nsIContent* newFocusContent = nullptr;
|
||||
while (currFrame) {
|
||||
int32_t tabIndexUnused = 0;
|
||||
if (currFrame->IsFocusable(&tabIndexUnused, true)) {
|
||||
newFocusContent = currFrame->GetContent();
|
||||
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
|
||||
if (domElement)
|
||||
break;
|
||||
}
|
||||
currFrame = currFrame->GetParent();
|
||||
}
|
||||
|
||||
|
||||
// If target frame is focusable, we should move focus to it. If target frame
|
||||
// isn't focusable, and our previous focused content is editable, we should
|
||||
// clear focus.
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
nsIContent* editingHost = ptFrame->GetContent()->GetEditingHost();
|
||||
if (editingHost) {
|
||||
nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(editingHost->GetParent());
|
||||
if (elt) {
|
||||
fm->SetFocus(elt, 0);
|
||||
}
|
||||
if (newFocusContent && currFrame) {
|
||||
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
|
||||
fm->SetFocus(domElement,0);
|
||||
|
||||
if (!nsContentUtils::HasNonEmptyTextContent(
|
||||
if (editingHost && !nsContentUtils::HasNonEmptyTextContent(
|
||||
editingHost, nsContentUtils::eRecurseIntoChildren)) {
|
||||
SELECTIONCARETS_LOG("Select a editable content %p with empty text",
|
||||
editingHost);
|
||||
|
|
|
@ -39,17 +39,32 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
self._contenteditable = self.marionette.find_element(*self._contenteditable_selector)
|
||||
self._content = self.marionette.find_element(*self._content_selector)
|
||||
|
||||
def _long_press_to_select_first_word(self, el, sel):
|
||||
# Move caret inside the first word.
|
||||
def _first_word_location(self, el):
|
||||
'''Get the location (x, y) of the first word in el.
|
||||
|
||||
Note: this function has a side effect which changes focus to the
|
||||
target element el.
|
||||
|
||||
'''
|
||||
sel = SelectionManager(el)
|
||||
|
||||
# Move caret behind the first character to get the location of the first
|
||||
# word.
|
||||
el.tap()
|
||||
sel.move_caret_to_front()
|
||||
sel.move_caret_by_offset(1)
|
||||
x, y = sel.caret_location()
|
||||
|
||||
# Long press the caret position. Selection carets should appear, and the
|
||||
# first word will be selected. On Windows, those spaces after the word
|
||||
# will also be selected.
|
||||
long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y)
|
||||
return sel.caret_location()
|
||||
|
||||
def _long_press_to_select(self, el, x, y):
|
||||
'''Long press the location (x, y) to select a word.
|
||||
|
||||
SelectionCarets should appear. On Windows, those spaces after the
|
||||
word will also be selected.
|
||||
|
||||
'''
|
||||
long_press_without_contextmenu(self.marionette, el, self._long_press_time,
|
||||
x, y)
|
||||
|
||||
def _test_long_press_to_select_a_word(self, el, assertFunc):
|
||||
sel = SelectionManager(el)
|
||||
|
@ -59,7 +74,8 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
target_content = words[0]
|
||||
|
||||
# Goal: Select the first word.
|
||||
self._long_press_to_select_first_word(el, sel)
|
||||
x, y = self._first_word_location(el)
|
||||
self._long_press_to_select(el, x, y)
|
||||
|
||||
# Ignore extra spaces selected after the word.
|
||||
assertFunc(target_content, sel.selected_content.rstrip())
|
||||
|
@ -79,7 +95,8 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
sel.select_all()
|
||||
(_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location()
|
||||
|
||||
self._long_press_to_select_first_word(el, sel)
|
||||
x, y = self._first_word_location(el)
|
||||
self._long_press_to_select(el, x, y)
|
||||
|
||||
# Move the right caret to the end of the content.
|
||||
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
|
||||
|
@ -91,7 +108,8 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
# Ignore extra spaces at the beginning of the content in comparison.
|
||||
assertFunc(target_content.lstrip(), sel.selected_content.lstrip())
|
||||
|
||||
def _test_minimum_select_one_character(self, el, assertFunc):
|
||||
def _test_minimum_select_one_character(self, el, assertFunc,
|
||||
x=None, y=None):
|
||||
sel = SelectionManager(el)
|
||||
original_content = sel.content
|
||||
words = original_content.split()
|
||||
|
@ -100,7 +118,13 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
# Goal: Select the first character.
|
||||
target_content = original_content[0]
|
||||
|
||||
self._long_press_to_select_first_word(el, sel)
|
||||
if x and y:
|
||||
# If we got x and y from the arguments, use it as a hint of the
|
||||
# location of the first word
|
||||
pass
|
||||
else:
|
||||
x, y = self._first_word_location(el)
|
||||
self._long_press_to_select(el, x, y)
|
||||
|
||||
# Move the right caret to the position of the left caret.
|
||||
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
|
||||
|
@ -108,6 +132,24 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
|
||||
assertFunc(target_content, sel.selected_content)
|
||||
|
||||
def _test_focus_obtained_by_long_press(self, el1, el2):
|
||||
'''Test the focus could be changed from el1 to el2 by long press.
|
||||
|
||||
If the focus is changed to e2 successfully, SelectionCarets should
|
||||
appear and could be dragged.
|
||||
|
||||
'''
|
||||
# Goal: Tap to focus el1, and then select the first character on
|
||||
# el2.
|
||||
|
||||
# We want to collect the location of the first word in el2 here
|
||||
# since self._first_word_location() has the side effect which would
|
||||
# change the focus.
|
||||
x, y = self._first_word_location(el2)
|
||||
el1.tap()
|
||||
self._test_minimum_select_one_character(el2, self.assertEqual,
|
||||
x=x, y=y)
|
||||
|
||||
########################################################################
|
||||
# <input> test cases with selection carets enabled
|
||||
########################################################################
|
||||
|
@ -123,6 +165,18 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
self.openTestHtml(enabled=True)
|
||||
self._test_minimum_select_one_character(self._input, self.assertEqual)
|
||||
|
||||
def test_input_focus_obtained_by_long_press_from_textarea(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._textarea, self._input)
|
||||
|
||||
def test_input_focus_obtained_by_long_press_from_contenteditable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._contenteditable, self._input)
|
||||
|
||||
def test_input_focus_obtained_by_long_press_from_content_non_editable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._content, self._input)
|
||||
|
||||
########################################################################
|
||||
# <input> test cases with selection carets disabled
|
||||
########################################################################
|
||||
|
@ -149,6 +203,18 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
self.openTestHtml(enabled=True)
|
||||
self._test_minimum_select_one_character(self._textarea, self.assertEqual)
|
||||
|
||||
def test_textarea_focus_obtained_by_long_press_from_input(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._input, self._textarea)
|
||||
|
||||
def test_textarea_focus_obtained_by_long_press_from_contenteditable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea)
|
||||
|
||||
def test_textarea_focus_obtained_by_long_press_from_content_non_editable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._content, self._textarea)
|
||||
|
||||
########################################################################
|
||||
# <textarea> test cases with selection carets disabled
|
||||
########################################################################
|
||||
|
@ -201,6 +267,18 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
self.openTestHtml(enabled=True)
|
||||
self._test_minimum_select_one_character(self._contenteditable, self.assertEqual)
|
||||
|
||||
def test_contenteditable_focus_obtained_by_long_press_from_input(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._input, self._contenteditable)
|
||||
|
||||
def test_contenteditable_focus_obtained_by_long_press_from_textarea(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable)
|
||||
|
||||
def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._content, self._contenteditable)
|
||||
|
||||
########################################################################
|
||||
# <div> contenteditable test cases with selection carets disabled
|
||||
########################################################################
|
||||
|
@ -218,3 +296,16 @@ class SelectionCaretsTest(MarionetteTestCase):
|
|||
def test_content_non_editable_minimum_select_one_character_by_selection(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_minimum_select_one_character(self._content, self.assertEqual)
|
||||
|
||||
def test_content_non_editable_focus_obtained_by_long_press_from_input(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._input, self._content)
|
||||
|
||||
def test_content_non_editable_focus_obtained_by_long_press_from_textarea(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._textarea, self._content)
|
||||
|
||||
def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._contenteditable, self._content)
|
||||
|
||||
|
|
|
@ -2925,8 +2925,24 @@ nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext)
|
|||
UniquePtr<nsMediaQueryResultCacheKey>
|
||||
nsCSSRuleProcessor::CloneMQCacheKey()
|
||||
{
|
||||
MOZ_ASSERT(!(mRuleCascades && mPreviousCacheKey));
|
||||
|
||||
RuleCascadeData* c = mRuleCascades;
|
||||
if (!c || !c->mCacheKey.HasFeatureConditions()) {
|
||||
if (!c) {
|
||||
// We might have an mPreviousCacheKey. It already comes from a call
|
||||
// to CloneMQCacheKey, so don't bother checking
|
||||
// HasFeatureConditions().
|
||||
if (mPreviousCacheKey) {
|
||||
NS_ASSERTION(mPreviousCacheKey->HasFeatureConditions(),
|
||||
"we shouldn't have a previous cache key unless it has "
|
||||
"feature conditions");
|
||||
return MakeUnique<nsMediaQueryResultCacheKey>(*mPreviousCacheKey);
|
||||
}
|
||||
|
||||
return UniquePtr<nsMediaQueryResultCacheKey>();
|
||||
}
|
||||
|
||||
if (!c->mCacheKey.HasFeatureConditions()) {
|
||||
return UniquePtr<nsMediaQueryResultCacheKey>();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1089417
|
|||
is(fwin.getComputedStyle(fdoc.documentElement).backgroundColor,
|
||||
"rgb(0, 128, 0)",
|
||||
"media query change should have restyled");
|
||||
|
||||
f.height = "200";
|
||||
fdoc.getElementById("s").disabled = true;
|
||||
fdoc.getElementById("s").disabled = false;
|
||||
is(fwin.getComputedStyle(fdoc.documentElement).backgroundColor,
|
||||
"rgb(255, 0, 0)",
|
||||
"media query change should have restyled");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
|
|
@ -3752,13 +3752,20 @@ pref("browser.zoom.reflowZoom.reflowTimeout", 500);
|
|||
*/
|
||||
pref("browser.zoom.reflowZoom.reflowTextOnPageLoad", true);
|
||||
|
||||
//
|
||||
// Image-related prefs
|
||||
//
|
||||
|
||||
// The maximum size, in bytes, of the decoded images we cache
|
||||
pref("image.cache.size", 5242880);
|
||||
|
||||
// A weight, from 0-1000, to place on time when comparing to size.
|
||||
// Size is given a weight of 1000 - timeweight.
|
||||
pref("image.cache.timeweight", 500);
|
||||
|
||||
// Whether we attempt to downscale images during decoding.
|
||||
pref("image.downscale-during-decode.enabled", false);
|
||||
|
||||
// The default Accept header sent for images loaded over HTTP(S)
|
||||
pref("image.http.accept", "image/png,image/*;q=0.8,*/*;q=0.5");
|
||||
|
||||
|
|
|
@ -1365,6 +1365,15 @@ HttpBaseChannel::IsNoCacheResponse(bool *value)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::IsPrivateResponse(bool *value)
|
||||
{
|
||||
if (!mResponseHead)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
*value = mResponseHead->Private();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetResponseStatus(uint32_t *aValue)
|
||||
{
|
||||
|
|
|
@ -153,6 +153,7 @@ public:
|
|||
NS_IMETHOD SetRedirectionLimit(uint32_t value) MOZ_OVERRIDE;
|
||||
NS_IMETHOD IsNoStoreResponse(bool *value) MOZ_OVERRIDE;
|
||||
NS_IMETHOD IsNoCacheResponse(bool *value) MOZ_OVERRIDE;
|
||||
NS_IMETHOD IsPrivateResponse(bool *value) MOZ_OVERRIDE;
|
||||
NS_IMETHOD GetResponseStatus(uint32_t *aValue) MOZ_OVERRIDE;
|
||||
NS_IMETHOD GetResponseStatusText(nsACString& aValue) MOZ_OVERRIDE;
|
||||
NS_IMETHOD GetRequestSucceeded(bool *aValue) MOZ_OVERRIDE;
|
||||
|
|
|
@ -193,6 +193,12 @@ NullHttpChannel::IsNoCacheResponse(bool *_retval)
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::IsPrivateResponse(bool *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::RedirectTo(nsIURI *aNewURI)
|
||||
{
|
||||
|
|
|
@ -145,6 +145,7 @@ struct ParamTraits<mozilla::net::nsHttpResponseHead>
|
|||
WriteParam(aMsg, aParam.mContentLength);
|
||||
WriteParam(aMsg, aParam.mContentType);
|
||||
WriteParam(aMsg, aParam.mContentCharset);
|
||||
WriteParam(aMsg, aParam.mCacheControlPrivate);
|
||||
WriteParam(aMsg, aParam.mCacheControlNoStore);
|
||||
WriteParam(aMsg, aParam.mCacheControlNoCache);
|
||||
WriteParam(aMsg, aParam.mPragmaNoCache);
|
||||
|
@ -159,6 +160,7 @@ struct ParamTraits<mozilla::net::nsHttpResponseHead>
|
|||
!ReadParam(aMsg, aIter, &aResult->mContentLength) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mContentType) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mContentCharset) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mCacheControlPrivate) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mCacheControlNoStore) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mCacheControlNoCache) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mPragmaNoCache))
|
||||
|
|
|
@ -624,6 +624,7 @@ nsHttpResponseHead::Reset()
|
|||
mVersion = NS_HTTP_VERSION_1_1;
|
||||
mStatus = 200;
|
||||
mContentLength = UINT64_MAX;
|
||||
mCacheControlPrivate = false;
|
||||
mCacheControlNoStore = false;
|
||||
mCacheControlNoCache = false;
|
||||
mPragmaNoCache = false;
|
||||
|
@ -792,11 +793,16 @@ nsHttpResponseHead::ParseCacheControl(const char *val)
|
|||
{
|
||||
if (!(val && *val)) {
|
||||
// clear flags
|
||||
mCacheControlPrivate = false;
|
||||
mCacheControlNoCache = false;
|
||||
mCacheControlNoStore = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// search header value for occurrence of "private"
|
||||
if (nsHttp::FindToken(val, "private", HTTP_HEADER_VALUE_SEPS))
|
||||
mCacheControlPrivate = true;
|
||||
|
||||
// search header value for occurrence(s) of "no-cache" but ignore
|
||||
// occurrence(s) of "no-cache=blah"
|
||||
if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS))
|
||||
|
|
|
@ -23,6 +23,7 @@ public:
|
|||
nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1)
|
||||
, mStatus(200)
|
||||
, mContentLength(UINT64_MAX)
|
||||
, mCacheControlPrivate(false)
|
||||
, mCacheControlNoStore(false)
|
||||
, mCacheControlNoCache(false)
|
||||
, mPragmaNoCache(false) {}
|
||||
|
@ -37,6 +38,7 @@ public:
|
|||
int64_t ContentLength() const { return mContentLength; }
|
||||
const nsAFlatCString &ContentType() const { return mContentType; }
|
||||
const nsAFlatCString &ContentCharset() const { return mContentCharset; }
|
||||
bool Private() const { return mCacheControlPrivate; }
|
||||
bool NoStore() const { return mCacheControlNoStore; }
|
||||
bool NoCache() const { return (mCacheControlNoCache || mPragmaNoCache); }
|
||||
/**
|
||||
|
@ -128,6 +130,7 @@ private:
|
|||
int64_t mContentLength;
|
||||
nsCString mContentType;
|
||||
nsCString mContentCharset;
|
||||
bool mCacheControlPrivate;
|
||||
bool mCacheControlNoStore;
|
||||
bool mCacheControlNoCache;
|
||||
bool mPragmaNoCache;
|
||||
|
|
|
@ -14,7 +14,7 @@ interface nsIHttpHeaderVisitor;
|
|||
* the inspection of the resulting HTTP response status and headers when they
|
||||
* become available.
|
||||
*/
|
||||
[scriptable, uuid(82083578-fb78-4f9a-953c-cecbae500697)]
|
||||
[scriptable, uuid(a8bed710-653c-4ea4-9747-a629cc482cf8)]
|
||||
interface nsIHttpChannel : nsIChannel
|
||||
{
|
||||
/**************************************************************************
|
||||
|
@ -299,6 +299,15 @@ interface nsIHttpChannel : nsIChannel
|
|||
*/
|
||||
boolean isNoCacheResponse();
|
||||
|
||||
/**
|
||||
* Returns true if the server sent a "Cache-Control: private" response
|
||||
* header.
|
||||
*
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if called before the response
|
||||
* has been received (before onStartRequest).
|
||||
*/
|
||||
boolean isPrivateResponse();
|
||||
|
||||
/**
|
||||
* Instructs the channel to immediately redirect to a new destination.
|
||||
* Can only be called on channels not yet opened.
|
||||
|
|
|
@ -768,6 +768,13 @@ nsViewSourceChannel::IsNoCacheResponse(bool *_retval)
|
|||
mHttpChannel->IsNoCacheResponse(_retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::IsPrivateResponse(bool *_retval)
|
||||
{
|
||||
return !mHttpChannel ? NS_ERROR_NULL_POINTER :
|
||||
mHttpChannel->IsPrivateResponse(_retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::RedirectTo(nsIURI *uri)
|
||||
{
|
||||
|
|
|
@ -171,7 +171,7 @@ int main(int argc, char **argv)
|
|||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
|
||||
RETURN_IF_FAILED(rv, "NS_OpenURI");
|
||||
RETURN_IF_FAILED(rv, "NS_NewChannel");
|
||||
|
||||
rv = chan->AsyncOpen(listener, nullptr);
|
||||
RETURN_IF_FAILED(rv, "AsyncOpen");
|
||||
|
|
|
@ -1,398 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#include "TestCommon.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIProgressEventSink.h"
|
||||
#include "nsIComponentManager.h"
|
||||
#include "prprf.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "plstr.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsIComponentRegistrar.h"
|
||||
#include <algorithm>
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
|
||||
namespace TestPageLoad {
|
||||
|
||||
int getStrLine(const char *src, char *str, int ind, int max);
|
||||
nsresult auxLoad(char *uriBuf);
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
|
||||
#define RETURN_IF_FAILED(rv, ret, step) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (NS_FAILED(rv)) { \
|
||||
printf(">>> %s failed: rv=%x\n", step, static_cast<uint32_t>(rv)); \
|
||||
return ret;\
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
static nsCString globalStream;
|
||||
//static char urlBuf[256];
|
||||
static nsCOMPtr<nsIURI> baseURI;
|
||||
static nsCOMArray<nsIURI> uriList;
|
||||
|
||||
//Temp, should remove:
|
||||
static int numStart=0;
|
||||
static int numFound=0;
|
||||
|
||||
static int32_t gKeepRunning = 0;
|
||||
|
||||
|
||||
//--------writer fun----------------------
|
||||
|
||||
static NS_METHOD streamParse (nsIInputStream* in,
|
||||
void* closure,
|
||||
const char* fromRawSegment,
|
||||
uint32_t toOffset,
|
||||
uint32_t count,
|
||||
uint32_t *writeCount) {
|
||||
|
||||
char parseBuf[2048], loc[2048], lineBuf[2048];
|
||||
char *loc_t, *loc_t2;
|
||||
int i = 0;
|
||||
const char *tmp;
|
||||
|
||||
if(!globalStream.IsEmpty()) {
|
||||
globalStream.Append(fromRawSegment);
|
||||
tmp = globalStream.get();
|
||||
//printf("\n>>NOW:\n^^^^^\n%s\n^^^^^^^^^^^^^^", tmp);
|
||||
} else {
|
||||
tmp = fromRawSegment;
|
||||
}
|
||||
|
||||
while(i < (int)count) {
|
||||
i = getStrLine(tmp, lineBuf, i, count);
|
||||
if(i < 0) {
|
||||
*writeCount = count;
|
||||
return NS_OK;
|
||||
}
|
||||
parseBuf[0]='\0';
|
||||
if((loc_t=PL_strcasestr(lineBuf, "img"))!= nullptr
|
||||
|| (loc_t=PL_strcasestr(lineBuf, "script"))!=nullptr) {
|
||||
loc_t2=PL_strcasestr(loc_t, "src");
|
||||
if(loc_t2!=nullptr) {
|
||||
loc_t2+=3;
|
||||
strcpy(loc, loc_t2);
|
||||
sscanf(loc, "=\"%[^\"]", parseBuf);
|
||||
if(parseBuf[0]=='\0')
|
||||
sscanf(loc, "=%s", parseBuf);
|
||||
if(parseBuf[0]!='\0'){
|
||||
numFound++;
|
||||
auxLoad(parseBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***NEED BETTER CHECK FOR STYLESHEETS
|
||||
if((loc_t=PL_strcasestr(lineBuf, "link"))!= nullptr) {
|
||||
loc_t2=PL_strcasestr(loc_t, "href");
|
||||
if(loc_t2!=nullptr) {
|
||||
loc_t2+=4;
|
||||
strcpy(loc, loc_t2);
|
||||
//printf("%s\n", loc);
|
||||
sscanf(loc, "=\"%[^\"]", parseBuf);
|
||||
if(parseBuf[0]!='\0'){
|
||||
//printf("%s\n", parseBuf);
|
||||
numFound++;
|
||||
auxLoad(parseBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if((loc_t=PL_strcasestr(lineBuf, "background"))!=nullptr) {
|
||||
loc_t+=10;
|
||||
strcpy(loc, loc_t);
|
||||
sscanf(loc, "=\"%[^\"]", parseBuf);
|
||||
if(parseBuf[0]!='\0') {
|
||||
numFound++;
|
||||
auxLoad(parseBuf);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
|
||||
}
|
||||
*writeCount = count;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIStreamListener implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class MyListener : public nsIStreamListener
|
||||
{
|
||||
virtual ~MyListener() {}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
||||
MyListener() { }
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(MyListener,
|
||||
nsIRequestObserver,
|
||||
nsIStreamListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
MyListener::OnStartRequest(nsIRequest *req, nsISupports *ctxt)
|
||||
{
|
||||
//printf(">>> OnStartRequest\n");
|
||||
numStart++;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MyListener::OnStopRequest(nsIRequest *req, nsISupports *ctxt, nsresult status)
|
||||
{
|
||||
//printf(">>> OnStopRequest status=%x\n", status);
|
||||
if (--gKeepRunning == 0)
|
||||
QuitPumpingEvents();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MyListener::OnDataAvailable(nsIRequest *req, nsISupports *ctxt,
|
||||
nsIInputStream *stream,
|
||||
uint64_t offset, uint32_t count)
|
||||
{
|
||||
//printf(">>> OnDataAvailable [count=%u]\n", count);
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
uint32_t bytesRead=0;
|
||||
char buf[1024];
|
||||
|
||||
if(ctxt == nullptr) {
|
||||
bytesRead=0;
|
||||
rv = stream->ReadSegments(streamParse, nullptr, count, &bytesRead);
|
||||
} else {
|
||||
while (count) {
|
||||
uint32_t amount = std::min<uint32_t>(count, sizeof(buf));
|
||||
rv = stream->Read(buf, amount, &bytesRead);
|
||||
count -= bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
printf(">>> stream->Read failed with rv=%x\n",
|
||||
static_cast<uint32_t>(rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NotificationCallbacks implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class MyNotifications : public nsIInterfaceRequestor
|
||||
, public nsIProgressEventSink
|
||||
{
|
||||
virtual ~MyNotifications() {}
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSIPROGRESSEVENTSINK
|
||||
|
||||
MyNotifications() { }
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(MyNotifications,
|
||||
nsIInterfaceRequestor,
|
||||
nsIProgressEventSink)
|
||||
|
||||
NS_IMETHODIMP
|
||||
MyNotifications::GetInterface(const nsIID &iid, void **result)
|
||||
{
|
||||
return QueryInterface(iid, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MyNotifications::OnStatus(nsIRequest *req, nsISupports *ctx,
|
||||
nsresult status, const char16_t *statusText)
|
||||
{
|
||||
//printf("status: %x\n", status);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MyNotifications::OnProgress(nsIRequest *req, nsISupports *ctx,
|
||||
uint64_t progress, uint64_t progressMax)
|
||||
{
|
||||
// char buf[100];
|
||||
// PR_snprintf(buf, sizeof(buf), "%llu/%llu\n", progress, progressMax);
|
||||
// printf("%s", buf);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// main, etc..
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//---------getStrLine Helper function---------------
|
||||
//Finds a newline in src starting at ind. Puts the
|
||||
//line in str (must be big enough). Returns the index
|
||||
//of the newline, or -1 if at end of string. If reaches
|
||||
//end of string ('\0'), then will copy contents to
|
||||
//globalStream.
|
||||
int getStrLine(const char *src, char *str, int ind, int max) {
|
||||
char c = src[ind];
|
||||
int i=0;
|
||||
globalStream.Assign('\0');
|
||||
while(c!='\n' && c!='\0' && i<max) {
|
||||
str[i] = src[ind];
|
||||
i++; ind++;
|
||||
c = src[ind];
|
||||
}
|
||||
str[i]='\0';
|
||||
if(i==max || c=='\0') {
|
||||
globalStream.Assign(str);
|
||||
//printf("\nCarryover (%d|%d):\n------------\n%s\n-------\n",i,max,str);
|
||||
return -1;
|
||||
}
|
||||
return ind;
|
||||
}
|
||||
|
||||
//----------AUX LOAD-----------
|
||||
nsresult auxLoad(char *uriBuf)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsISupportsPRBool> myBool = do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
nsCOMPtr<nsIStreamListener> listener = new MyListener();
|
||||
nsCOMPtr<nsIInterfaceRequestor> callbacks = new MyNotifications();
|
||||
|
||||
printf("Getting: %s", uriBuf);
|
||||
|
||||
//If relative link
|
||||
if(strncmp(uriBuf, "http:", 5)) {
|
||||
//Relative link
|
||||
rv = NS_NewURI(getter_AddRefs(uri), uriBuf, baseURI);
|
||||
if (NS_FAILED(rv)) return(rv);
|
||||
} else {
|
||||
//Absolute link, no base needed
|
||||
rv = NS_NewURI(getter_AddRefs(uri), uriBuf);
|
||||
if (NS_FAILED(rv)) return(rv);
|
||||
}
|
||||
|
||||
//Compare to see if exists
|
||||
bool equal;
|
||||
for(int32_t i = 0; i < uriList.Count(); i++) {
|
||||
uri->Equals(uriList[i], &equal);
|
||||
if(equal) {
|
||||
printf("(duplicate, canceling) %s\n",uriBuf);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
uriList.AppendObject(uri);
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secman =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
||||
RETURN_IF_FAILED(rv, rv, "Couldn't get script security manager!");
|
||||
nsCOMPtr<nsIPrincipal> systemPrincipal;
|
||||
rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
|
||||
RETURN_IF_FAILED(rv, rv, "Couldn't get system principal!");
|
||||
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
uri,
|
||||
systemPrincipal,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
nullptr, // loadGroup
|
||||
callbacks);
|
||||
|
||||
RETURN_IF_FAILED(rv, rv, "NS_NewChannel");
|
||||
|
||||
gKeepRunning++;
|
||||
rv = chan->AsyncOpen(listener, myBool);
|
||||
RETURN_IF_FAILED(rv, rv, "AsyncOpen");
|
||||
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
|
||||
//---------Buffer writer fun---------
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace TestPageLoad;
|
||||
|
||||
//---------MAIN-----------
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (test_common_init(&argc, &argv) != 0)
|
||||
return -1;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (argc == 1) {
|
||||
printf("usage: TestPageLoad <url>\n");
|
||||
return -1;
|
||||
}
|
||||
{
|
||||
nsCOMPtr<nsIServiceManager> servMan;
|
||||
NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr);
|
||||
|
||||
PRTime start, finish;
|
||||
|
||||
printf("Loading necko ... \n");
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
nsCOMPtr<nsIStreamListener> listener = new MyListener();
|
||||
nsCOMPtr<nsIInterfaceRequestor> callbacks = new MyNotifications();
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(baseURI), argv[1]);
|
||||
RETURN_IF_FAILED(rv, -1, "NS_NewURI");
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secman =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
||||
RETURN_IF_FAILED(rv, -1, "Couldn't get script security manager!");
|
||||
nsCOMPtr<nsIPrincipal> systemPrincipal;
|
||||
rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
|
||||
RETURN_IF_FAILED(rv, -1, "Couldn't get system principal!");
|
||||
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
baseURI,
|
||||
systemPrincipal,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
nullptr, // loadGroup
|
||||
callbacks);
|
||||
|
||||
RETURN_IF_FAILED(rv, -1, "NS_OpenURI");
|
||||
gKeepRunning++;
|
||||
|
||||
//TIMER STARTED-----------------------
|
||||
printf("Starting clock ... \n");
|
||||
start = PR_Now();
|
||||
rv = chan->AsyncOpen(listener, nullptr);
|
||||
RETURN_IF_FAILED(rv, -1, "AsyncOpen");
|
||||
|
||||
PumpEvents();
|
||||
|
||||
finish = PR_Now();
|
||||
uint32_t totalTime32 = uint32_t(finish - start);
|
||||
|
||||
printf("\n\n--------------------\nAll done:\nnum found:%d\nnum start:%d\n", numFound, numStart);
|
||||
|
||||
printf("\n\n>>PageLoadTime>>%u>>\n\n", totalTime32);
|
||||
} // this scopes the nsCOMPtrs
|
||||
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
|
||||
rv = NS_ShutdownXPCOM(nullptr);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
|
||||
return 0;
|
||||
}
|
|
@ -1,273 +0,0 @@
|
|||
#include "TestCommon.h"
|
||||
#include <stdio.h>
|
||||
#include "nsCRT.h" /* should be "plstr.h"? */
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIComponentRegistrar.h"
|
||||
#include "nsISupportsArray.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace TestPerf {
|
||||
|
||||
static nsIIOService *gIOService = nullptr;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static bool
|
||||
load_sync_1(nsISupports *element, void *data)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsCOMPtr<nsIURI> uri( do_QueryInterface(element) );
|
||||
nsAutoCString spec;
|
||||
nsresult rv;
|
||||
|
||||
rv = NS_OpenURI(getter_AddRefs(stream),
|
||||
uri,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
LOAD_NORMAL,
|
||||
gIOService);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
uri->GetAsciiSpec(spec);
|
||||
fprintf(stderr, "*** failed opening %s [rv=%x]\n", spec.get(), rv);
|
||||
return true;
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
uint32_t bytesRead;
|
||||
|
||||
while (1) {
|
||||
rv = stream->Read(buf, sizeof(buf), &bytesRead);
|
||||
if (NS_FAILED(rv) || bytesRead == 0) {
|
||||
if (NS_FAILED(rv)) {
|
||||
uri->GetAsciiSpec(spec);
|
||||
fprintf(stderr, "*** failed reading %s [rv=%x]\n", spec.get(), rv);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
load_sync(nsISupportsArray *urls)
|
||||
{
|
||||
urls->EnumerateForwards(load_sync_1, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static int gRequestCount = 0;
|
||||
|
||||
class MyListener : public nsIStreamListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
||||
MyListener() { }
|
||||
virtual ~MyListener() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(MyListener, nsIStreamListener, nsIRequestObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
MyListener::OnStartRequest(nsIRequest *req, nsISupports *ctx)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MyListener::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
|
||||
nsIInputStream *stream,
|
||||
uint64_t offset, uint32_t count)
|
||||
{
|
||||
nsresult rv;
|
||||
char buf[4096];
|
||||
uint32_t n, bytesRead;
|
||||
while (count) {
|
||||
n = std::min<uint32_t>(count, sizeof(buf));
|
||||
rv = stream->Read(buf, n, &bytesRead);
|
||||
if (NS_FAILED(rv))
|
||||
break;
|
||||
count -= bytesRead;
|
||||
if (bytesRead == 0)
|
||||
break;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MyListener::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
|
||||
{
|
||||
if (NS_FAILED(status)) {
|
||||
nsAutoCString spec;
|
||||
req->GetName(spec);
|
||||
fprintf(stderr, "*** failed loading %s [reason=%x]\n", spec.get(), status);
|
||||
}
|
||||
if (--gRequestCount == 0) {
|
||||
// post shutdown event
|
||||
QuitPumpingEvents();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
load_async_1(nsISupports *element, void *data)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri( do_QueryInterface(element) );
|
||||
if (!uri)
|
||||
return true;
|
||||
|
||||
MyListener *listener = new MyListener();
|
||||
if (!listener)
|
||||
return true;
|
||||
NS_ADDREF(listener);
|
||||
|
||||
nsresult rv = NS_OpenURI(listener,
|
||||
nullptr, // aContext
|
||||
uri,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
gIOService);
|
||||
|
||||
NS_RELEASE(listener);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
gRequestCount++;
|
||||
else
|
||||
printf(">> NS_OpenURI failed [rv=%x]\n", rv);
|
||||
return true;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
load_async(nsISupportsArray *urls)
|
||||
{
|
||||
urls->EnumerateForwards(load_async_1, nullptr);
|
||||
|
||||
PumpEvents();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static nsresult
|
||||
read_file(const char *fname, nsISupportsArray *urls)
|
||||
{
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
printf("failed opening file: %s\n", fname);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv;
|
||||
char buf[512];
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
// remove trailing newline
|
||||
buf[strlen(buf) - 1] = 0;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), buf, nullptr, gIOService);
|
||||
if (NS_FAILED(rv))
|
||||
printf("*** ignoring malformed uri: %s\n", buf);
|
||||
else {
|
||||
//nsXPIDLCString spec;
|
||||
//uri->GetSpec(getter_Copies(spec));
|
||||
//printf("read url: %s\n", spec.get());
|
||||
urls->AppendElement(uri);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
print_usage()
|
||||
{
|
||||
printf("usage: TestPerf [-sync|-async] <file-of-urls>\n");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace TestPerf;
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (test_common_init(&argc, &argv) != 0)
|
||||
return -1;
|
||||
|
||||
nsresult rv;
|
||||
bool sync;
|
||||
|
||||
if (argc < 3) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (PL_strcasecmp(argv[1], "-sync") == 0)
|
||||
sync = true;
|
||||
else if (PL_strcasecmp(argv[1], "-async") == 0)
|
||||
sync = false;
|
||||
else {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIServiceManager> servMan;
|
||||
NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr);
|
||||
nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
|
||||
NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
|
||||
registrar->AutoRegister(nullptr);
|
||||
|
||||
// cache the io service
|
||||
{
|
||||
nsCOMPtr<nsIIOService> ioserv( do_GetIOService() );
|
||||
NS_ADDREF(gIOService = ioserv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupportsArray> urls;
|
||||
rv = NS_NewISupportsArray(getter_AddRefs(urls));
|
||||
if (NS_FAILED(rv)) return -1;
|
||||
|
||||
rv = read_file(argv[2], urls);
|
||||
if (NS_FAILED(rv)) {
|
||||
printf("failed reading file-of-urls\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t urlCount;
|
||||
urls->Count(&urlCount);
|
||||
|
||||
PRIntervalTime start = PR_IntervalNow();
|
||||
|
||||
if (sync)
|
||||
rv = load_sync(urls);
|
||||
else
|
||||
rv = load_async(urls);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
printf("load failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
PRIntervalTime end = PR_IntervalNow();
|
||||
fprintf(stderr, "read: %u urls; total time: %u milliseconds\n",
|
||||
urlCount,
|
||||
PR_IntervalToMilliseconds(end - start));
|
||||
|
||||
NS_RELEASE(gIOService);
|
||||
return 0;
|
||||
}
|
|
@ -649,7 +649,7 @@ nsresult StartLoadingURL(const char* aUrlString)
|
|||
|
||||
NS_RELEASE(callbacks);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("ERROR: NS_OpenURI failed for %s [rv=%x]\n", aUrlString, rv));
|
||||
LOG(("ERROR: NS_NewChannel failed for %s [rv=%x]\n", aUrlString, rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#include <nsCOMPtr.h>
|
||||
#include <nsString.h>
|
||||
#include <nsIURI.h>
|
||||
#include <nsIChannel.h>
|
||||
#include <nsIHTTPChannel.h>
|
||||
#include <nsIInputStream.h>
|
||||
#include "nsContentUtils.h"
|
||||
#include <nsNetUtil.h>
|
||||
|
||||
/*
|
||||
* Test synchronous HTTP.
|
||||
*/
|
||||
|
||||
#define RETURN_IF_FAILED(rv, what) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (NS_FAILED(rv)) { \
|
||||
printf(what ": failed - %08x\n", rv); \
|
||||
return -1; \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
struct TestContext {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
PRTime t1, t2;
|
||||
uint32_t bytesRead, totalRead;
|
||||
|
||||
TestContext()
|
||||
: t1(0), t2(0), bytesRead(0), totalRead(0)
|
||||
{ printf("TestContext [this=%p]\n", (void*)this); }
|
||||
~TestContext()
|
||||
{ printf("~TestContext [this=%p]\n", (void*)this); }
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
nsresult rv;
|
||||
TestContext *c;
|
||||
int i, nc=0, npending=0;
|
||||
char buf[256];
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: TestSyncHTTP <url-list>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
c = new TestContext[argc-1];
|
||||
|
||||
for (i=0; i<(argc-1); ++i, ++nc) {
|
||||
rv = NS_NewURI(getter_AddRefs(c[i].uri), argv[i+1]);
|
||||
RETURN_IF_FAILED(rv, "NS_NewURI");
|
||||
|
||||
rv = NS_OpenURI(getter_AddRefs(c[i].channel,
|
||||
c[i].uri,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
|
||||
RETURN_IF_FAILED(rv, "NS_OpenURI");
|
||||
|
||||
nsCOMPtr<nsIHTTPChannel> httpChannel = do_QueryInterface(c[i].channel);
|
||||
if (httpChannel)
|
||||
httpChannel->SetOpenHasEventQueue(false);
|
||||
|
||||
// initialize these fields for reading
|
||||
c[i].bytesRead = 1;
|
||||
c[i].totalRead = 0;
|
||||
}
|
||||
|
||||
for (i=0; i<nc; ++i) {
|
||||
c[i].t1 = PR_Now();
|
||||
|
||||
rv = c[i].channel->Open(getter_AddRefs(c[i].inputStream));
|
||||
RETURN_IF_FAILED(rv, "nsIChannel::OpenInputStream");
|
||||
}
|
||||
|
||||
npending = nc;
|
||||
while (npending) {
|
||||
for (i=0; i<nc; ++i) {
|
||||
//
|
||||
// read the response content...
|
||||
//
|
||||
if (c[i].bytesRead > 0) {
|
||||
rv = c[i].inputStream->Read(buf, sizeof buf, &c[i].bytesRead);
|
||||
RETURN_IF_FAILED(rv, "nsIInputStream::Read");
|
||||
c[i].totalRead += c[i].bytesRead;
|
||||
|
||||
if (c[i].bytesRead == 0) {
|
||||
c[i].t2 = PR_Now();
|
||||
printf("finished GET of: %s\n", argv[i+1]);
|
||||
printf("total read: %u bytes\n", c[i].totalRead);
|
||||
printf("total read time: %0.3f\n",
|
||||
((double) (c[i].t2 - c[i].t1))/1000000.0);
|
||||
npending--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] c;
|
||||
|
||||
NS_ShutdownXPCOM(nullptr);
|
||||
return 0;
|
||||
}
|
|
@ -1,290 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
#include <stdio.h>
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIEventQueueService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include <algorithm>
|
||||
//#include "prthread.h"
|
||||
|
||||
// This test attempts to load a URL on a separate thread. It is currently
|
||||
// designed simply to expose the problems inherent in such an ambitous task
|
||||
// (i.e., it don't work).
|
||||
|
||||
// Utility functions...
|
||||
|
||||
// Create event queue for current thread.
|
||||
static nsCOMPtr<nsIEventQueue>
|
||||
createEventQueue() {
|
||||
nsCOMPtr<nsIEventQueue> result;
|
||||
// Get event queue service.
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsIEventQueueService> eqs =
|
||||
do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
eqs->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(result));
|
||||
} else {
|
||||
printf( "%s %d: NS_WITH_SERVICE(nsIEventQueueService) failed, rv=0x%08X\n",
|
||||
(char*)__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create channel for requested URL.
|
||||
static nsCOMPtr<nsIChannel>
|
||||
createChannel( const char *url ) {
|
||||
nsCOMPtr<nsIInputStream> result;
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
printf( "Calling NS_NewURI for %s...\n", url );
|
||||
nsresult rv = NS_NewURI( getter_AddRefs( uri ), url );
|
||||
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
printf( "...NS_NewURI completed OK\n" );
|
||||
|
||||
// Allocate a new input channel on this thread.
|
||||
printf( "Calling NS_OpenURI...\n" );
|
||||
|
||||
nsresult rv = NS_OpenURI(getter_AddRefs(result),
|
||||
uri,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
printf( "...NS_OpenURI completed OK\n" );
|
||||
} else {
|
||||
printf( "%s %d: NS_OpenURI failed, rv=0x%08X\n",
|
||||
(char*)__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
} else {
|
||||
printf( "%s %d: NS_NewURI failed, rv=0x%08X\n",
|
||||
(char*)__FILE__, (int)__LINE__, (int)rv );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Test listener. It basically dumps incoming data to console.
|
||||
class TestListener : public nsIStreamListener {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSISTREAMOBSERVER
|
||||
|
||||
TestListener();
|
||||
~TestListener();
|
||||
static void IOThread( void *p );
|
||||
|
||||
private:
|
||||
bool mDone;
|
||||
int mThreadNo;
|
||||
FILE *mFile;
|
||||
static int threadCount;
|
||||
}; // class TestListener
|
||||
|
||||
int TestListener::threadCount = 0;
|
||||
|
||||
TestListener::TestListener()
|
||||
: mDone( false ), mThreadNo( ++threadCount ) {
|
||||
printf( "TestListener ctor called on thread %d\n", mThreadNo );
|
||||
}
|
||||
|
||||
TestListener::~TestListener() {
|
||||
printf( "TestListener dtor called on thread %d\n", mThreadNo );
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS( TestListener, nsIStreamListener, nsIRequestObserver )
|
||||
|
||||
NS_IMETHODIMP
|
||||
TestListener::OnStartRequest( nsIChannel *aChannel, nsISupports *aContext ) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
printf( "TestListener::OnStartRequest called on thread %d\n", mThreadNo );
|
||||
|
||||
// Open output file.
|
||||
char fileName[32];
|
||||
sprintf( fileName, "%s%d", "thread", mThreadNo );
|
||||
mFile = fopen( fileName, "wb" );
|
||||
setbuf( mFile, 0 );
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TestListener::OnStopRequest( nsIChannel *aChannel,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatus,
|
||||
const char16_t *aMsg ) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
printf( "TestListener::OnStopRequest called on thread %d\n", mThreadNo );
|
||||
|
||||
fclose( mFile );
|
||||
mDone = true;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TestListener::OnDataAvailable( nsIChannel *aChannel,
|
||||
nsISupports *aContext,
|
||||
nsIInputStream *aStream,
|
||||
uint64_t offset,
|
||||
uint32_t aLength ) {
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
printf( "TestListener::OnDataAvailable called on thread %d\n", mThreadNo );
|
||||
|
||||
// Write the data to the console.
|
||||
// Read a buffer full till aLength bytes have been processed.
|
||||
char buffer[ 8192 ];
|
||||
unsigned long bytesRemaining = aLength;
|
||||
while ( bytesRemaining ) {
|
||||
unsigned int bytesRead;
|
||||
// Read a buffer full or the number remaining (whichever is smaller).
|
||||
rv = aStream->Read( buffer,
|
||||
std::min( sizeof( buffer ), bytesRemaining ),
|
||||
&bytesRead );
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
// Write the bytes just read to the output file.
|
||||
fwrite( buffer, 1, bytesRead, mFile );
|
||||
bytesRemaining -= bytesRead;
|
||||
} else {
|
||||
printf( "%s %d: Read error, rv=0x%08X\n",
|
||||
(char*)__FILE__, (int)__LINE__, (int)rv );
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf( "\n" );
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// IOThread: this function creates a new TestListener object (on the new
|
||||
// thread), opens a channel, and does AsyncRead to it.
|
||||
void
|
||||
TestListener::IOThread( void *p ) {
|
||||
printf( "I/O thread (0x%08X) started...\n", (int)(void*)PR_GetCurrentThread() );
|
||||
|
||||
// Argument is pointer to the nsIEventQueue for the main thread.
|
||||
nsIEventQueue *mainThreadQ = static_cast<nsIEventQueue*>(p);
|
||||
|
||||
// Create channel for random web page.
|
||||
nsCOMPtr<nsIChannel> channel = createChannel( (const char*)p );
|
||||
|
||||
if ( channel ) {
|
||||
// Create event queue.
|
||||
nsCOMPtr<nsIEventQueue> ioEventQ = createEventQueue();
|
||||
|
||||
if ( ioEventQ ) {
|
||||
// Create test listener.
|
||||
TestListener *testListener = new TestListener();
|
||||
testListener->AddRef();
|
||||
|
||||
// Read the channel.
|
||||
printf( "Doing AsyncRead...\n" );
|
||||
nsresult rv = channel->AsyncRead( testListener, 0 );
|
||||
|
||||
if ( NS_SUCCEEDED( rv ) ) {
|
||||
printf( "...AsyncRead completed OK\n" );
|
||||
|
||||
// Process events till testListener says stop.
|
||||
printf( "Start event loop on io thread %d...\n", testListener->mThreadNo );
|
||||
while ( !testListener->mDone ) {
|
||||
PLEvent *event;
|
||||
ioEventQ->GetEvent( &event );
|
||||
ioEventQ->HandleEvent( event );
|
||||
}
|
||||
printf( "...io thread %d event loop exiting\n", testListener->mThreadNo );
|
||||
} else {
|
||||
printf( "%s %d: AsyncRead failed on thread %d, rv=0x%08X\n",
|
||||
(char*)__FILE__, (int)__LINE__, testListener->mThreadNo, (int)rv );
|
||||
}
|
||||
|
||||
// Release the test listener.
|
||||
testListener->Release();
|
||||
}
|
||||
}
|
||||
|
||||
printf( "...I/O thread terminating\n" );
|
||||
}
|
||||
|
||||
static const int maxThreads = 5;
|
||||
|
||||
int
|
||||
main( int argc, char* argv[] ) {
|
||||
setbuf( stdout, 0 );
|
||||
if ( argc < 2 || argc > maxThreads + 1 ) {
|
||||
printf( "usage: testThreadedIO url1 <url2>...\n"
|
||||
"where <url#> is a location to be loaded on a separate thread\n"
|
||||
"limit is %d urls/threads", maxThreads );
|
||||
return -1;
|
||||
}
|
||||
|
||||
nsresult rv= (nsresult)-1;
|
||||
|
||||
printf( "Test starting...\n" );
|
||||
|
||||
// Initialize XPCOM.
|
||||
printf( "Initializing XPCOM...\n" );
|
||||
rv = NS_InitXPCOM2(nullptr, nullptr, nullptr);
|
||||
if ( NS_FAILED( rv ) ) {
|
||||
printf( "%s %d: NS_InitXPCOM failed, rv=0x%08X\n",
|
||||
(char*)__FILE__, (int)__LINE__, (int)rv );
|
||||
return rv;
|
||||
}
|
||||
printf( "...XPCOM initialized OK\n" );
|
||||
// Create the Event Queue for this thread...
|
||||
printf( "Creating event queue for main thread (0x%08X)...\n",
|
||||
(int)(void*)PR_GetCurrentThread() );
|
||||
{
|
||||
nsCOMPtr<nsIEventQueue> mainThreadQ = createEventQueue();
|
||||
|
||||
if ( mainThreadQ ) {
|
||||
printf( "...main thread's event queue created OK\n" );
|
||||
|
||||
// Spawn threads to do I/O.
|
||||
int goodThreads = 0;
|
||||
PRThread *thread[ maxThreads ];
|
||||
for ( int threadNo = 1; threadNo < argc; threadNo++ ) {
|
||||
printf( "Creating I/O thread %d to load %s...\n", threadNo, argv[threadNo] );
|
||||
PRThread *ioThread = PR_CreateThread( PR_USER_THREAD,
|
||||
TestListener::IOThread,
|
||||
argv[threadNo],
|
||||
PR_PRIORITY_NORMAL,
|
||||
PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD,
|
||||
0 );
|
||||
if ( ioThread ) {
|
||||
thread[ goodThreads++ ] = ioThread;
|
||||
printf( "...I/O thread %d (0x%08X) created OK\n",
|
||||
threadNo, (int)(void*)ioThread );
|
||||
} else {
|
||||
printf( "%s %d: PR_CreateThread for thread %d failed\n",
|
||||
(char*)__FILE__, (int)__LINE__, threadNo );
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all the threads to terminate.
|
||||
for ( int joinThread = 0; joinThread < goodThreads; joinThread++ ) {
|
||||
printf( "Waiting for thread %d to terminate...\n", joinThread+1 );
|
||||
PR_JoinThread( thread[ joinThread ] );
|
||||
}
|
||||
}
|
||||
} // this scopes the nsCOMPtrs
|
||||
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
|
||||
// Shut down XPCOM.
|
||||
printf( "Shutting down XPCOM...\n" );
|
||||
NS_ShutdownXPCOM( 0 );
|
||||
printf( "...XPCOM shutdown complete\n" );
|
||||
|
||||
// Exit.
|
||||
printf( "...test complete, rv=0x%08X\n", (int)rv );
|
||||
return rv;
|
||||
}
|
|
@ -59,6 +59,7 @@ function commonCheck(ch)
|
|||
do_check_true(ch.contentLength > -1);
|
||||
do_check_eq(ch.getResponseHeader("connection"), "close");
|
||||
do_check_false(ch.isNoStoreResponse());
|
||||
do_check_false(ch.isPrivateResponse());
|
||||
}
|
||||
|
||||
function start_objHandler(ch, cx)
|
||||
|
|
|
@ -24,7 +24,6 @@ GeckoSimplePrograms([
|
|||
'TestDNS',
|
||||
'TestIncrementalDownload',
|
||||
'TestOpen',
|
||||
'TestPageLoad',
|
||||
'TestProtocols',
|
||||
'TestServ',
|
||||
'TestStandardURL',
|
||||
|
@ -38,7 +37,6 @@ GeckoSimplePrograms([
|
|||
#SIMPLE_PROGRAMS += [
|
||||
# TestIDN',
|
||||
# TestIOThreads',
|
||||
# TestPerf',
|
||||
# TestSocketTransport',
|
||||
# TestStreamChannel',
|
||||
# TestStreamPump',
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче