author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Mon, 19 Jan 2015 16:09:46 +0100 | |
changeset 224489 | f8e4fdb89a058fd12700e4f694d4397c5b3e855a |
parent 224456 | 0e9496c4e398cd81f2e0ece755adeff281aa3c17 (current diff) |
parent 224488 | 837613fc3a49ae3bca3e2651348e14c53803774c (diff) |
child 224497 | dcb4c5573aef75c9d07e5aff3610f08b9f74ac15 |
push id | 28131 |
push user | cbook@mozilla.com |
push date | Mon, 19 Jan 2015 15:10:25 +0000 |
treeherder | mozilla-central@f8e4fdb89a05 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 38.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/tests/ecma_7/SIMD/float32x4-minmax.js | file | annotate | diff | comparison | revisions | |
js/src/tests/ecma_7/SIMD/float32x4clamp.js | file | annotate | diff | comparison | revisions | |
netwerk/test/TestPageLoad.cpp | file | annotate | diff | comparison | revisions | |
netwerk/test/TestPerf.cpp | file | annotate | diff | comparison | revisions | |
netwerk/test/TestSyncHTTP.cpp | file | annotate | diff | comparison | revisions | |
netwerk/test/TestThreadedIO.cpp | file | annotate | diff | comparison | revisions | |
xpcom/tests/CvtURL.cpp | file | annotate | diff | comparison | revisions |
--- a/browser/base/content/test/general/mochitest.ini +++ b/browser/base/content/test/general/mochitest.ini @@ -24,16 +24,16 @@ support-files = offlineEvent.cacheManifest^headers^ offlineEvent.html subtst_contextmenu.html video.ogg [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] [test_offlineNotification.html] skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works? [test_offline_gzip.html] skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
--- a/browser/base/content/test/general/test_contextmenu.html +++ b/browser/base/content/test/general/test_contextmenu.html @@ -847,26 +847,20 @@ function waitForEvents(event) loaded = true; if (painted && loaded) { subwindow.removeEventListener("MozAfterPaint", waitForEvents, false); subwindow.onload = null; startTest(); } } -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> </html>
--- a/caps/tests/mochitest/test_bug995943.xul +++ b/caps/tests/mochitest/test_bug995943.xul @@ -21,18 +21,20 @@ https://bugzilla.mozilla.org/show_bug.cg const Cc = Components.classes; const Ci = Components.interfaces; Cu.import("resource://gre/modules/Services.jsm"); function debug(msg) { info(msg); } /** 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:///"; function checkLoadFileURI(domain, shouldLoad) { debug("Invoking checkLoadFileURI with domain: " + domain + ", shouldLoad: " + shouldLoad); return new Promise(function(resolve, reject) { $('ifr').addEventListener('load', function l1() { debug("Invoked l1 for " + domain);
--- a/dom/base/ScriptSettings.h +++ b/dom/base/ScriptSettings.h @@ -314,18 +314,18 @@ private: AutoJSAPI(const AutoJSAPI&) = delete; AutoJSAPI& operator= (const AutoJSAPI&) = delete; }; /* * A class that represents a new script entry point. */ -class AutoEntryScript : public AutoJSAPI, - protected ScriptSettingsStackEntry { +class MOZ_STACK_CLASS AutoEntryScript : public AutoJSAPI, + protected ScriptSettingsStackEntry { public: explicit AutoEntryScript(nsIGlobalObject* aGlobalObject, bool aIsMainThread = NS_IsMainThread(), // Note: aCx is mandatory off-main-thread. JSContext* aCx = nullptr); ~AutoEntryScript(); @@ -336,20 +336,20 @@ public: private: // It's safe to make this a weak pointer, since it's the subject principal // when we go on the stack, so can't go away until after we're gone. In // particular, this is only used from the CallSetup constructor, and only in // the aIsJSImplementedWebIDL case. And in that case, the subject principal // 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; }; /* * A class that can be used to force a particular incumbent script on the stack. */ class AutoIncumbentScript : protected ScriptSettingsStackEntry {
--- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -150,17 +150,19 @@ enum EventNameType { EventNameType_HTMLBodyOrFramesetOnly = 0x0020, EventNameType_HTMLXUL = 0x0003, EventNameType_All = 0xFFFF }; 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; }; struct nsShortcutCandidate { nsShortcutCandidate(uint32_t aCharCode, bool aIgnoreShift) : mCharCode(aCharCode), mIgnoreShift(aIgnoreShift)
--- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -675,17 +675,17 @@ skip-if = buildapp == 'b2g' || toolkit = [test_domparser_null_char.html] [test_domparsing.html] [test_elementTraversal.html] [test_element_closest.html] [test_encodeToStringWithMaxLength.html] [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] [test_html_in_xhr.html] [test_htmlcopyencoder.html] [test_htmlcopyencoder.xhtml] [test_ipc_messagemanager_blob.html] skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
--- a/dom/base/test/test_fileapi_slice.html +++ b/dom/base/test/test_fileapi_slice.html @@ -17,21 +17,16 @@ https://bugzilla.mozilla.org/show_bug.cg <canvas id=testcanvas hidden moz-opaque></canvas> <input id="fileList" type="file"></input> </p> <div id="content" style="display: none"> </div> <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(); // Create files containing data we'll test with. We'll want long // strings to ensure they span multiple buffers while loading // Create a decent-sized image cx = $("canvas").getContext('2d'); @@ -131,12 +126,11 @@ expectedTestCount++; // image past end var imgfile = createFileWithData(testBinaryData + fileData); is(imgfile.size, size + testBinaryData.length, "correct file size (past end)"); var img = new Image; img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size + 1000)); img.onload = imageLoadHandler; expectedTestCount++; -} </script> </pre> </body> </html>
--- a/dom/bindings/DOMString.h +++ b/dom/bindings/DOMString.h @@ -170,17 +170,19 @@ public: } private: // We need to be able to act like a string as needed Maybe<nsAutoString> mString; // 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; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_DOMString_h
--- a/dom/events/EventListenerManager.h +++ b/dom/events/EventListenerManager.h @@ -553,17 +553,17 @@ protected: uint32_t mMayHaveScrollWheelEventListener : 1; uint32_t mMayHaveMouseEnterLeaveEventListener : 1; uint32_t mMayHavePointerEnterLeaveEventListener : 1; uint32_t mClearingListeners : 1; uint32_t mIsMainThreadELM : 1; 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; static uint32_t sMainThreadCreatedCount; }; } // namespace mozilla
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -13,16 +13,17 @@ #include <stdint.h> #include "MediaDecoderStateMachine.h" #include "MediaDecoderStateMachineScheduler.h" #include "AudioSink.h" #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" #include "nsDeque.h" #include "AudioSegment.h" #include "VideoSegment.h" #include "ImageContainer.h" #include "nsComponentManagerUtils.h" @@ -160,17 +161,17 @@ static const uint32_t QUICK_BUFFERING_LO // QUICK_BUFFERING_LOW_DATA_USECS. static_assert(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS, "QUICK_BUFFERING_LOW_DATA_USECS is too large"); // The amount of instability we tollerate in calls to // 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); } static int64_t DurationToUsecs(TimeDuration aDuration) { return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S); } @@ -1444,17 +1445,17 @@ void MediaDecoderStateMachine::SetDurati mEndTime = mStartTime + aDuration; } 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); NS_DispatchToMainThread(event); } } void MediaDecoderStateMachine::SetMediaEndTime(int64_t aEndTime)
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp +++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp @@ -54,16 +54,20 @@ using namespace mozilla; using namespace mozilla::dom; static const int kDefaultPeriod = 1000; // ms 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"; // While most methods of GonkGPSGeolocationProvider should only be // called from main thread, we deliberately put the Init and ShutdownGPS // methods off main thread to avoid blocking. NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider, @@ -284,16 +288,20 @@ GonkGPSGeolocationProvider::AGPSRILRefLo #endif // MOZ_B2G_RIL GonkGPSGeolocationProvider::GonkGPSGeolocationProvider() : mStarted(false) , mSupportsScheduling(false) #ifdef MOZ_B2G_RIL , mSupportsMSB(false) , mSupportsMSA(false) + , mRilDataServiceId(0) + , mNumberOfRilServices(1) + , mObservingNetworkConnStateChange(false) + , mObservingSettingsChange(false) #endif , mSupportsSingleShot(false) , mSupportsTimeInjection(false) , mGpsInterface(nullptr) { } GonkGPSGeolocationProvider::~GonkGPSGeolocationProvider() @@ -709,27 +717,34 @@ GonkGPSGeolocationProvider::SetupAGPS() int32_t suplPort = Preferences::GetInt("geo.gps.supl_port", -1); if (!suplServer.IsEmpty() && suplPort > 0) { mAGpsInterface->set_server(AGPS_TYPE_SUPL, suplServer.get(), suplPort); } else { NS_WARNING("Cannot get SUPL server settings"); 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 NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider::NetworkLocationUpdate, nsIGeolocationUpdate) NS_IMETHODIMP @@ -843,19 +858,22 @@ GonkGPSGeolocationProvider::Startup() } RequestSettingValue(kSettingDebugEnabled); RequestSettingValue(kSettingDebugGpsIgnored); // 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; } } if (!mInitThread) { nsresult rv = NS_NewThread(getter_AddRefs(mInitThread)); NS_ENSURE_SUCCESS(rv, rv); } @@ -867,16 +885,19 @@ GonkGPSGeolocationProvider::Startup() nsresult rv = mNetworkLocationProvider->Startup(); if (NS_SUCCEEDED(rv)) { nsRefPtr<NetworkLocationUpdate> update = new NetworkLocationUpdate(); mNetworkLocationProvider->Watch(update); } } mStarted = true; +#ifdef MOZ_B2G_RIL + mNumberOfRilServices = Preferences::GetUint(kPrefRilNumRadioInterfaces, 1); +#endif return NS_OK; } NS_IMETHODIMP GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback) { MOZ_ASSERT(NS_IsMainThread()); @@ -901,21 +922,25 @@ GonkGPSGeolocationProvider::Shutdown() nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); if (obs) { nsresult rv; #ifdef MOZ_B2G_RIL 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; } } mInitThread->Dispatch(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::ShutdownGPS), NS_DISPATCH_NORMAL); return NS_OK; } @@ -1038,16 +1063,25 @@ GonkGPSGeolocationProvider::Observe(nsIS gDebug_isGPSLocationIgnored); } return NS_OK; } else if (setting.mKey.EqualsASCII(kSettingDebugEnabled)) { nsContentUtils::LogMessageToConsole("geo: received mozsettings-changed: logging\n"); 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; } } return NS_OK; } /** nsISettingsServiceCallback **/ @@ -1066,16 +1100,42 @@ GonkGPSGeolocationProvider::Handle(const nsAutoJSString apn; if (!apn.init(cx, aResult.toString())) { return NS_ERROR_FAILURE; } if (!apn.IsEmpty()) { 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; } NS_IMETHODIMP GonkGPSGeolocationProvider::HandleError(const nsAString& aErrorMessage) {
--- a/dom/system/gonk/GonkGPSGeolocationProvider.h +++ b/dom/system/gonk/GonkGPSGeolocationProvider.h @@ -77,16 +77,18 @@ private: #endif void Init(); void StartGPS(); void ShutdownGPS(); 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); void RequestDataConnection(); void ReleaseDataConnection(); void RequestSetID(uint32_t flags); void SetReferenceLocation(); #endif @@ -96,16 +98,23 @@ private: static GonkGPSGeolocationProvider* sSingleton; bool mStarted; bool mSupportsScheduling; #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; const GpsInterface* mGpsInterface; #ifdef MOZ_B2G_RIL const AGpsInterface* mAGpsInterface; const AGpsRilInterface* mAGpsRilInterface;
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2032,16 +2032,22 @@ public: // Assumes that WorkerJSRuntimeStats will hold a reference to |path|, and // not a copy, as TryToMapAddon() may later modify if. nsCString path; WorkerJSRuntimeStats rtStats(path); { 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>"); } else { nsCString escapedDomain(mWorkerPrivate->Domain()); if (escapedDomain.IsEmpty()) { escapedDomain += "chrome"; } else { @@ -2051,22 +2057,16 @@ public: path.AppendLiteral(")/worker("); NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL()); escapedURL.ReplaceChar('/', '\\'); path.Append(escapedURL); } 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, aCallback, aClosure, aAnonymize); } private:
--- a/editor/libeditor/nsHTMLURIRefObject.cpp +++ b/editor/libeditor/nsHTMLURIRefObject.cpp @@ -34,42 +34,16 @@ applet: codebase, archive <list> object: codebase, data, classid, usemap head: profile del: cite ins: cite 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" #include "nsAString.h" #include "nsDebug.h" #include "nsError.h" #include "nsID.h" #include "nsIDOMAttr.h"
--- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -229,16 +229,17 @@ private: DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust", TouchVsyncSampleAdjust, int32_t, 5); DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold", TouchResampleVsyncDelayThreshold, int32_t, 20); DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17); DECL_GFX_PREF(Live, "gl.msaa-level", MSAALevel, uint32_t, 2); 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); DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time", ImageMemDecodeBytesAtATime, uint32_t, 200000); DECL_GFX_PREF(Live, "image.mem.decodeondraw", ImageMemDecodeOnDraw, bool, false); DECL_GFX_PREF(Live, "image.mem.discardable", ImageMemDiscardable, bool, false); DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1); DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb", ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
--- a/image/decoders/nsICODecoder.cpp +++ b/image/decoders/nsICODecoder.cpp @@ -633,29 +633,29 @@ nsICODecoder::NeedsNewFrame() const if (mContainedDecoder) { return mContainedDecoder->NeedsNewFrame(); } return Decoder::NeedsNewFrame(); } 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()); return rv; } // 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; } } // namespace image } // namespace mozilla
--- a/image/decoders/nsICODecoder.h +++ b/image/decoders/nsICODecoder.h @@ -35,17 +35,18 @@ public: // Obtains the height of the icon directory entry uint32_t GetRealHeight() const { return mDirEntry.mHeight == 0 ? 256 : mDirEntry.mHeight; } 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; private: // Writes to the contained decoder and sets the appropriate errors // Returns true if there are no errors. bool WriteToContainedDecoder(const char* aBuffer, uint32_t aCount);
--- a/image/public/imgIContainer.idl +++ b/image/public/imgIContainer.idl @@ -64,17 +64,17 @@ native nsIntSizeByVal(nsIntSize); /** * imgIContainer is the interface that represents an image. It allows * access to frames as Thebes surfaces. It also allows drawing of images * onto Thebes contexts. * * 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 { /** * The width of the container rectangle. In the case of any error, * zero is returned, and an exception will be thrown. */ readonly attribute int32_t width; @@ -340,22 +340,38 @@ interface imgIContainer : nsISupports * the image will at some point fire off decode notifications. Calling draw() * or getFrame() triggers the same mechanism internally. Thus, if you want to * be sure that the image will be decoded but don't want to access it until * then, you must call requestDecode(). */ 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 * because decoded images may be discarded, isDecoded() may return false even * if it has returned true in the past. */ [noscript, notxpcom, nostdcall] bool isDecoded(); /**
--- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -323,21 +323,28 @@ Decoder::FinishSharedDecoder() MOZ_ASSERT(NS_IsMainThread()); if (!HasError()) { FinishInternal(); } } 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, mNewFrameData.mPaletteDepth, mCurrentFrame.get()); if (mCurrentFrame) { // Gather the raw pointers the decoders will use. @@ -361,16 +368,17 @@ Decoder::AllocateFrame() mNeedsToFlushData = true; } return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE; } RawAccessFrameRef Decoder::EnsureFrame(uint32_t aFrameNum, + const nsIntSize& aTargetSize, const nsIntRect& aFrameRect, uint32_t aDecodeFlags, SurfaceFormat aFormat, uint8_t aPaletteDepth, imgFrame* aPreviousFrame) { if (mDataError || NS_FAILED(mFailCode)) { return RawAccessFrameRef(); @@ -378,18 +386,18 @@ Decoder::EnsureFrame(uint32_t aFrameNum, MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!"); if (aFrameNum > mFrameCount) { return RawAccessFrameRef(); } // 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 // ever replace any other frame, since the first frame is the only one we // speculatively allocate without knowing what the decoder really needs. // XXX(seth): I'm not convinced there's any reason to support this at all. We // should figure out how to avoid triggering this and rip it out. MOZ_ASSERT(aFrameNum == 0, "Replacing a frame other than the first?"); @@ -410,70 +418,69 @@ Decoder::EnsureFrame(uint32_t aFrameNum, MOZ_ASSERT(ref, "No ref to current frame?"); // Reinitialize the old frame. nsIntSize oldSize = ThebesIntSize(aPreviousFrame->GetImageSize()); bool nonPremult = aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; if (NS_FAILED(aPreviousFrame->ReinitForDecoder(oldSize, aFrameRect, aFormat, - aPaletteDepth, nonPremult))) { + aPaletteDepth, nonPremult))) { NS_WARNING("imgFrame::ReinitForDecoder should succeed"); mFrameCount = 0; aPreviousFrame->Abort(); return RawAccessFrameRef(); } return ref; } RawAccessFrameRef Decoder::InternalAddFrame(uint32_t aFrameNum, + const nsIntSize& aTargetSize, const nsIntRect& aFrameRect, uint32_t aDecodeFlags, SurfaceFormat aFormat, uint8_t aPaletteDepth, imgFrame* aPreviousFrame) { MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!"); if (aFrameNum > mFrameCount) { 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(); } 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(); } RawAccessFrameRef ref = frame->RawAccessRef(); if (!ref) { frame->Abort(); return RawAccessFrameRef(); } InsertOutcome outcome = SurfaceCache::Insert(frame, ImageKey(mImage.get()), - RasterSurfaceKey(imageSize.ToIntSize(), + RasterSurfaceKey(aTargetSize.ToIntSize(), aDecodeFlags, aFrameNum), Lifetime::Persistent); if (outcome != InsertOutcome::SUCCESS) { // We either hit InsertOutcome::FAILURE, which is a temporary failure due to // low memory (we know it's not permanent because we checked CanHold() // above), or InsertOutcome::FAILURE_ALREADY_PRESENT, which means that // another decoder beat us to decoding this frame. Either way, we should @@ -594,32 +601,35 @@ Decoder::PostFrameStop(Opacity aFrameOpa mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod); mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED; // 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!"); NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!"); // Record this invalidation, unless we're not sending partial invalidations // or we're past the first frame. if (mSendPartialInvalidations && !mIsAnimated) { mInvalidRect.UnionRect(mInvalidRect, aRect); - mCurrentFrame->ImageUpdated(aRect); + mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect)); } } void Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */) { NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!"); NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
--- a/image/src/Decoder.h +++ b/image/src/Decoder.h @@ -123,16 +123,34 @@ public: bool IsSizeDecode() { return mSizeDecode; } void SetSizeDecode(bool aSizeDecode) { MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet"); 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. * * If @aSend is true, we'll send partial invalidations when decoding the first * frame of the image, so image notifications observers will be able to * gradually draw in the image as it downloads. * * If @aSend is false (the default), we'll only send an invalidation when we * complete the first frame. @@ -228,16 +246,22 @@ public: bool HasSize() const { return mImageMetadata.HasSize(); } void SetSizeOnImage(); void SetSize(const nsIntSize& aSize, const Orientation& aOrientation) { 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; } ImageMetadata& GetImageMetadata() { return mImageMetadata; } /** * Returns a weak pointer to the image associated with this decoder. */ @@ -254,17 +278,17 @@ public: uint32_t width, uint32_t height, gfx::SurfaceFormat format, uint8_t palette_depth = 0); virtual bool NeedsNewFrame() const { return mNeedsNewFrame; } // 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() { nsRefPtr<imgFrame> frame = mCurrentFrame.get(); return frame.forget(); } RawAccessFrameRef GetCurrentFrameRef() @@ -327,19 +351,29 @@ protected: // Specify whether this frame is opaque as an optimization. // For animated images, specify the disposal, blend method and timeout for // this frame. void PostFrameStop(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY, DisposalMethod aDisposalMethod = DisposalMethod::KEEP, 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 // the stream, or by us calling FinishInternal(). // // May not be called mid-frame. // // For animated images, specify the loop count. -1 means loop forever, 0 @@ -356,26 +390,32 @@ protected: bool NeedsToFlushData() const { return mNeedsToFlushData; } /** * Ensures that a given frame number exists with the given parameters, and * returns a RawAccessFrameRef for that frame. * 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, uint8_t aPaletteDepth, imgFrame* aPreviousFrame); RawAccessFrameRef InternalAddFrame(uint32_t aFrameNum, + const nsIntSize& aTargetSize, const nsIntRect& aFrameRect, uint32_t aDecodeFlags, gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth, imgFrame* aPreviousFrame); /* * Member variables.
--- a/image/src/DynamicImage.cpp +++ b/image/src/DynamicImage.cpp @@ -250,16 +250,22 @@ DynamicImage::RequestDecode() } NS_IMETHODIMP DynamicImage::StartDecoding() { return NS_OK; } +NS_IMETHODIMP +DynamicImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags) +{ + return NS_OK; +} + bool DynamicImage::IsDecoded() { return true; } NS_IMETHODIMP DynamicImage::LockImage()
--- a/image/src/Image.h +++ b/image/src/Image.h @@ -47,21 +47,26 @@ public: * INIT_FLAG_DECODE_ON_DRAW: The container should decode on draw rather than * decoding on load. * * INIT_FLAG_TRANSIENT: The container is likely to exist for only a short time * before being destroyed. (For example, containers for * 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_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. * * @param aMimeType The mimetype of the image. * @param aFlags Initialization flags of the INIT_FLAG_* variety. */ virtual nsresult Init(const char* aMimeType,
--- a/image/src/ImageFactory.cpp +++ b/image/src/ImageFactory.cpp @@ -26,24 +26,32 @@ namespace mozilla { namespace image { /*static*/ void 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. bool isChrome = false; rv = uri->SchemeIs("chrome", &isChrome); if (NS_SUCCEEDED(rv) && isChrome) { isDiscardable = doDecodeOnDraw = false; } @@ -51,50 +59,58 @@ ComputeImageFlags(ImageURL* uri, bool is // We don't want resources like the "loading" icon to be discardable or // decode-on-draw either. bool isResource = false; rv = uri->SchemeIs("resource", &isResource); if (NS_SUCCEEDED(rv) && isResource) { 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. uint32_t imageFlags = Image::INIT_FLAG_NONE; if (isDiscardable) { imageFlags |= Image::INIT_FLAG_DISCARDABLE; } if (doDecodeOnDraw) { imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW; } if (isMultiPart) { imageFlags |= Image::INIT_FLAG_TRANSIENT; } + if (doDownscaleDuringDecode) { + imageFlags |= Image::INIT_FLAG_DOWNSCALE_DURING_DECODE; + } return imageFlags; } /* static */ already_AddRefed<Image> ImageFactory::CreateImage(nsIRequest* aRequest, ProgressTracker* aProgressTracker, const nsCString& aMimeType, ImageURL* aURI, bool aIsMultiPart, uint32_t aInnerWindowId) { MOZ_ASSERT(gfxPrefs::SingletonExists(), "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)) { return CreateVectorImage(aRequest, aProgressTracker, aMimeType, aURI, imageFlags, aInnerWindowId); } else { return CreateRasterImage(aRequest, aProgressTracker, aMimeType, aURI, imageFlags, aInnerWindowId);
--- a/image/src/ImageMetadata.h +++ b/image/src/ImageMetadata.h @@ -48,16 +48,17 @@ public: } } bool HasSize() const { return mSize.isSome(); } bool HasOrientation() const { return mOrientation.isSome(); } 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: // The hotspot found on cursors, or -1 if none was found. int32_t mHotspotX; int32_t mHotspotY; // The loop count for animated images, or -1 for infinite loop.
--- a/image/src/ImageWrapper.cpp +++ b/image/src/ImageWrapper.cpp @@ -219,16 +219,22 @@ ImageWrapper::RequestDecode() } NS_IMETHODIMP ImageWrapper::StartDecoding() { return mInnerImage->StartDecoding(); } +NS_IMETHODIMP +ImageWrapper::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags) +{ + return mInnerImage->RequestDecodeForSize(aSize, aFlags); +} + bool ImageWrapper::IsDecoded() { return mInnerImage->IsDecoded(); } NS_IMETHODIMP ImageWrapper::LockImage()
--- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -29,16 +29,17 @@ #include "nsJPEGDecoder.h" #include "nsBMPDecoder.h" #include "nsICODecoder.h" #include "nsIconDecoder.h" #include "gfxContext.h" #include "mozilla/gfx/2D.h" +#include "mozilla/DebugOnly.h" #include "mozilla/RefPtr.h" #include "mozilla/Move.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Services.h" #include <stdint.h> #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "mozilla/ClearOnShutdown.h" @@ -310,33 +311,39 @@ RasterImage::Init(const char* aMimeType, return NS_ERROR_FAILURE; NS_ENSURE_ARG_POINTER(aMimeType); // We must be non-discardable and non-decode-on-draw for // 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) { SurfaceCache::LockImage(ImageKey(this)); } // 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; } // Mark us as initialized mInitialized = true; return NS_OK; @@ -458,69 +465,91 @@ RasterImage::GetType(uint16_t *aType) NS_IMETHODIMP_(uint16_t) RasterImage::GetType() { return imgIContainer::TYPE_RASTER; } DrawableFrameRef RasterImage::LookupFrameInternal(uint32_t aFrameNum, - const nsIntSize& aSize, + const IntSize& aSize, uint32_t aFlags) { if (!mAnim) { NS_ASSERTION(aFrameNum == 0, "Don't ask for a frame > 0 if we're not animated!"); aFrameNum = 0; } if (mAnim && aFrameNum > 0) { MOZ_ASSERT(DecodeFlags(aFlags) == DECODE_FLAGS_DEFAULT, "Can't composite frames with non-default decode flags"); return mAnim->GetCompositedFrame(aFrameNum); } - return SurfaceCache::Lookup(ImageKey(this), - RasterSurfaceKey(aSize.ToIntSize(), - DecodeFlags(aFlags), - 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, + DecodeFlags(aFlags), + 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 RasterImage::LookupFrame(uint32_t aFrameNum, const nsIntSize& aSize, uint32_t aFlags, bool aShouldSyncNotify /* = true */) { MOZ_ASSERT(NS_IsMainThread()); - DrawableFrameRef ref = LookupFrameInternal(aFrameNum, aSize, aFlags); + IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags) + ? aSize.ToIntSize() + : mSize.ToIntSize(); + + 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 && 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); + 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(ThebesIntSize(requestedSize), aFlags, aShouldSyncNotify); + + // If we can sync decode, we should already have the frame. + if ((aFlags & FLAG_SYNC_DECODE) && aShouldSyncNotify) { + ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags); + } } if (!ref) { - // The OS threw this frame away. We need to redecode if we can. - MOZ_ASSERT(!mAnim, "Animated frames should be locked"); - - WantDecodedFrames(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 (!ref) { - // We didn't successfully redecode, so just fail. - return DrawableFrameRef(); - } + // We still weren't able to get a frame. Give up. + return DrawableFrameRef(); } if (ref->GetCompositingFailed()) { return DrawableFrameRef(); } MOZ_ASSERT(!ref || !ref->GetIsPaletted(), "Should not have paletted frame"); @@ -1117,18 +1146,17 @@ RasterImage::OnImageDataComplete(nsIRequ // Let decoders know that there won't be any more data coming. mSourceBuffer->Complete(aStatus); if (!mHasSize) { // 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 // check after running the size decode above in case it triggered an error. nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK; if (NS_FAILED(aStatus)) { finalStatus = aStatus; } @@ -1244,23 +1272,26 @@ RasterImage::Discard() bool RasterImage::CanDiscard() { return mHasSourceData && // ...have the source data... !mAnim; // Can never discard animated images } // 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) { + 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"); - } else { - MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!"); } // Figure out which decoder we want. eDecoderType type = GetDecoderType(mSourceDataMimeType.get()); if (type == eDecoderType_unknown) { return nullptr; } @@ -1291,37 +1322,48 @@ RasterImage::CreateDecoder(bool aDoSizeD break; default: MOZ_ASSERT_UNREACHABLE("Unknown decoder type"); } 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); if (mDecodeCount > sMaxDecodeCount) { // Don't subtract out 0 from the histogram, because that causes its count // to go negative, which is not kosher. if (sMaxDecodeCount > 0) { @@ -1331,121 +1373,124 @@ RasterImage::CreateDecoder(bool aDoSizeD Telemetry::GetHistogramById(Telemetry::IMAGE_MAX_DECODE_COUNT)->Add(sMaxDecodeCount); } } return decoder.forget(); } 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; } // Here we are explicitly trading off flashing for responsiveness in the // 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); } //****************************************************************************** /* void requestDecode() */ 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() */ NS_IMETHODIMP RasterImage::StartDecoding() { if (!NS_IsMainThread()) { return NS_DispatchToMainThread( 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; } bool RasterImage::IsDecoded() { // XXX(seth): We need to get rid of this; it's not reliable. return mHasBeenDecoded || mError; } 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()); // Lock the image while we're decoding, so that it doesn't get evicted from // the SurfaceCache before we have a chance to realize that it's animated. // The corresponding unlock happens in FinalizeDecoder. LockImage(); @@ -1490,16 +1535,21 @@ RasterImage::CanScale(GraphicsFilter aFi // we usually have no way of updating what we've drawn, so HQ scaling is // useless. if (!gfxPrefs::ImageHQDownscalingEnabled() || !mHasSize || !mHasSourceData || !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING) || aFilter != GraphicsFilter::FILTER_GOOD) { 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) { return false; } // If target size is 1:1 with original, don't scale. if (aSize == mSize) { @@ -1525,16 +1575,50 @@ RasterImage::CanScale(GraphicsFilter aFi // If that's all it's getting us, I'd rather we just forbid that explicitly. gfx::Size scale(double(aSize.width) / mSize.width, double(aSize.height) / mSize.height); gfxFloat minFactor = gfxPrefs::ImageHQDownscalingMinFactor() / 1000.0; return (scale.width < minFactor || scale.height < minFactor); #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() { // Send an invalidation so observers will repaint and can take advantage of // the new scaled frame if possible. NotifyProgress(NoProgress, nsIntRect(0, 0, mSize.width, mSize.height)); } @@ -1649,36 +1733,37 @@ RasterImage::Draw(gfxContext* aContext, return NS_ERROR_FAILURE; NS_ENSURE_ARG_POINTER(aContext); if (IsUnlocked() && mProgressTracker) { 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()) { mDrawStartTime = TimeStamp::Now(); } return NS_OK; } bool shouldRecordTelemetry = !mDrawStartTime.IsNull() && ref->IsImageComplete(); DrawWithPreDownscaleIfNeeded(Move(ref), aContext, aSize, - aRegion, aFilter, aFlags); + aRegion, aFilter, flags); if (shouldRecordTelemetry) { TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime; Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, int32_t(drawLatency.ToMicroseconds())); mDrawStartTime = TimeStamp(); } @@ -1933,17 +2018,20 @@ RasterImage::OptimalImageSizeForDest(con "Unexpected destination size"); if (mSize.IsEmpty() || aDest.IsEmpty()) { return nsIntSize(0, 0); } 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(), DecodeFlags(aFlags), 0)); if (frameRef && frameRef->IsImageComplete()) { return destSize; // We have an existing HQ scale for this size.
--- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -291,17 +291,17 @@ private: TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame, uint32_t aFlags, bool aShouldSyncNotify = true); TemporaryRef<gfx::SourceSurface> GetFrameInternal(uint32_t aWhichFrame, uint32_t aFlags, 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, uint32_t aFlags, bool aShouldSyncNotify = true); uint32_t GetCurrentFrameIndex() const; uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const; @@ -320,22 +320,36 @@ private: // that case we use our animation consumers count as a proxy for lock count. bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); } ////////////////////////////////////////////////////////////////////////////// // 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; Orientation mOrientation; nsCOMPtr<nsIProperties> mProperties; /// If this image is animated, a FrameAnimator which manages its animation. @@ -373,16 +387,17 @@ private: // data // Boolean flags (clustered together to conserve space): bool mHasSize:1; // Has SetSize() been called? bool mDecodeOnDraw:1; // Decoding on draw? bool mTransient:1; // Is the image short-lived? 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 // we know to start animation later if/when we have more frames. bool mPendingAnimation:1; // Whether the animation can stop, due to running out // of frames, or no more owning request @@ -403,16 +418,19 @@ private: // data ////////////////////////////////////////////////////////////////////////////// // Initiates an HQ scale for the given frame, if possible. void RequestScale(imgFrame* aFrame, uint32_t aFlags, const nsIntSize& aSize); // 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(); friend class ScaleRunner; // Error handling. void DoError();
--- a/image/src/SurfaceCache.cpp +++ b/image/src/SurfaceCache.cpp @@ -243,25 +243,111 @@ public: already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey) { nsRefPtr<CachedSurface> surface; mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface)); 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); } void SetLocked(bool aLocked) { mLocked = aLocked; } 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; }; /** * SurfaceCacheImpl is responsible for determining which surfaces will be cached * and managing the surface cache data structures. Rather than interact with * SurfaceCacheImpl directly, client code interacts with SurfaceCache, which @@ -448,16 +534,55 @@ public: if (!surface->IsLocked()) { mExpirationTracker.MarkUsed(surface); } 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) { nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey); if (!cache) return; // No cached surfaces for this image. nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey); @@ -782,25 +907,47 @@ SurfaceCache::Initialize() SurfaceCache::Shutdown() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?"); sInstance = nullptr; } /* static */ DrawableFrameRef -SurfaceCache::Lookup(const ImageKey aImageKey, - const SurfaceKey& aSurfaceKey) +SurfaceCache::Lookup(const ImageKey aImageKey, + 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 SurfaceCache::Insert(imgFrame* aSurface, const ImageKey aImageKey, const SurfaceKey& aSurfaceKey, Lifetime aLifetime) {
--- a/image/src/SurfaceCache.h +++ b/image/src/SurfaceCache.h @@ -58,16 +58,24 @@ public: { uint32_t hash = HashGeneric(mSize.width, mSize.height); hash = AddToHash(hash, mSVGContext.map(HashSIC).valueOr(0)); hash = AddToHash(hash, mAnimationTime, mFlags); return hash; } 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, const Maybe<SVGImageContext>& aSVGContext, const float aAnimationTime, const uint32_t aFlags) : mSize(aSize) , mSVGContext(aSVGContext) @@ -166,22 +174,51 @@ struct SurfaceCache * If the imgFrame was found in the cache, but had stored its surface in a * volatile buffer which was discarded by the OS, then it is automatically * removed from the cache and an empty DrawableFrameRef is returned. Note that * this will never happen to persistent surfaces associated with a locked * image; the cache keeps a strong reference to such surfaces internally. * * @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 * SurfaceKey is already in the cache, Insert returns FAILURE_ALREADY_PRESENT. * * Each surface in the cache has a lifetime, either Transient or Persistent. * Transient surfaces can expire from the cache at any time. Persistent * surfaces can ordinarily also expire from the cache at any time, but if the
--- a/image/src/VectorImage.cpp +++ b/image/src/VectorImage.cpp @@ -894,16 +894,25 @@ VectorImage::RequestDecode() NS_IMETHODIMP VectorImage::StartDecoding() { // Nothing to do for SVG images 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() { return mIsFullyLoaded || mError; } //****************************************************************************** /* void lockImage() */
--- a/image/test/crashtests/crashtests.list +++ b/image/test/crashtests/crashtests.list @@ -26,17 +26,17 @@ load invalid-icc-profile.jpg load 256-width.ico load 256-height.ico # A 3-frame animated GIF with an inordinate delay between the second and third # frame. 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 load truncated-second-frame.png # bug 863975 # Bug 863958 # This icon's size is such that it leads to multiple writes to the PNG decoder # after we've gotten our size.
--- a/js/public/Class.h +++ b/js/public/Class.h @@ -457,17 +457,17 @@ struct JSClass { // Implementing this efficiently requires that global objects have classes // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was // previously allowed, but is now an ES5 violation and thus unsupported. // // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at // 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 \ JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0) #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp) \ (((clasp)->flags & JSCLASS_IS_GLOBAL) \ && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
--- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -26,16 +26,17 @@ using namespace js; using mozilla::ArrayLength; using mozilla::IsFinite; using mozilla::IsNaN; using mozilla::FloorLog2; namespace js { extern const JSFunctionSpec Float32x4Methods[]; +extern const JSFunctionSpec Float64x2Methods[]; extern const JSFunctionSpec Int32x4Methods[]; } /////////////////////////////////////////////////////////////////////////// // SIMD static const char *laneNames[] = {"lane 0", "lane 1", "lane 2", "lane3"}; @@ -60,16 +61,17 @@ template<class V> bool js::IsVectorObject(HandleValue v) { return CheckVectorObject(v, V::type); } 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 js::ToSimdConstant(JSContext *cx, HandleValue v, jit::SimdConstant *out) { typedef typename V::Elem Elem; if (!IsVectorObject<V>(v)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR); @@ -119,16 +121,19 @@ static bool type##Lane##lane(JSContext * LANE_ACCESSOR(type, 0); \ LANE_ACCESSOR(type, 1); \ LANE_ACCESSOR(type, 2); \ LANE_ACCESSOR(type, 3); 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> static bool SignMask(JSContext *cx, unsigned argc, Value *vp) { typedef typename SimdType::Elem Elem; CallArgs args = CallArgsFromVp(argc, vp); @@ -163,16 +168,17 @@ static bool SignMask(JSContext *cx, unsi return true; } #define SIGN_MASK(type) \ 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 const Class SimdTypeDescr::class_ = { "SIMD", JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE, nullptr, /* addProperty */ nullptr, /* delProperty */ @@ -196,16 +202,23 @@ class Int32x4Defn { }; class Float32x4Defn { public: static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_FLOAT32; static const JSFunctionSpec TypeDescriptorMethods[]; 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[] = { 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 }; @@ -219,16 +232,35 @@ const JSPropertySpec js::Float32x4Defn:: JS_PS_END }; const JSFunctionSpec js::Float32x4Defn::TypedObjectMethods[] = { JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0), 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), JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0), JS_FS_END, }; const JSPropertySpec js::Int32x4Defn::TypedObjectProperties[] = { @@ -263,16 +295,17 @@ CreateSimdClass(JSContext *cx, Handle<Gl return nullptr; typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd)); typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr)); typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(type))); 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; // Create prototype property, which inherits from Object.prototype. RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); if (!objProto) @@ -293,16 +326,28 @@ CreateSimdClass(JSContext *cx, Handle<Gl T::TypedObjectMethods)) { return nullptr; } 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) { CallArgs args = CallArgsFromVp(argc, vp); Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>()); if (args.length() == 1) { // SIMD type used as a coercion @@ -331,16 +376,23 @@ SimdTypeDescr::call(JSContext *cx, unsig case SimdTypeDescr::TYPE_FLOAT32: { float *mem = reinterpret_cast<float*>(result->typedMem()); for (unsigned i = 0; i < 4; i++) { if (!RoundFloat32(cx, args.get(i), &mem[i])) return false; } 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; } /////////////////////////////////////////////////////////////////////////// // SIMD class @@ -381,16 +433,33 @@ SIMDObject::initClass(JSContext *cx, Han if (!JS_DefineFunctions(cx, float32x4Object, Float32x4Methods) || !DefineProperty(cx, SIMD, cx->names().float32x4, float32x4Value, nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT)) { 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, cx->names().int32x4); if (!int32x4Object) return nullptr; // Define int32x4 functions and install as a property of the SIMD object. @@ -406,16 +475,17 @@ SIMDObject::initClass(JSContext *cx, Han RootedValue SIMDValue(cx, ObjectValue(*SIMD)); // Everything is set up, install SIMD on the global object. if (!DefineProperty(cx, global, cx->names().SIMD, SIMDValue, nullptr, nullptr, 0)) return nullptr; global->setConstructor(JSProto_SIMD, SIMDValue); global->setFloat32x4TypeDescr(*float32x4Object); + global->setFloat64x2TypeDescr(*float64x2Object); global->setInt32x4TypeDescr(*int32x4Object); return SIMD; } JSObject * js_InitSIMDClass(JSContext *cx, HandleObject obj) { MOZ_ASSERT(obj->is<GlobalObject>()); @@ -436,16 +506,17 @@ js::CreateSimd(JSContext *cx, typename V return nullptr; Elem *resultMem = reinterpret_cast<Elem *>(result->typedMem()); memcpy(resultMem, data, sizeof(Elem) * V::lanes); return result; } 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 { // Unary SIMD operators template<typename T> struct Abs { static inline T apply(T x) { return mozilla::Abs(x); } }; @@ -762,18 +833,20 @@ CompareFunc(JSContext *cx, unsigned argc CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 2 || !IsVectorObject<In>(args[0]) || !IsVectorObject<In>(args[1])) return ErrorBadArgs(cx); 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); } template<typename V, typename Vret> static bool FuncConvert(JSContext *cx, unsigned argc, Value *vp) { @@ -782,17 +855,18 @@ FuncConvert(JSContext *cx, unsigned argc CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 1 || !IsVectorObject<V>(args[0])) return ErrorBadArgs(cx); 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); } template<typename V, typename Vret> static bool FuncConvertBits(JSContext *cx, unsigned argc, Value *vp) { typedef typename Vret::Elem RetElem; @@ -853,37 +927,39 @@ Int32x4Bool(JSContext *cx, unsigned argc } int32_t result[Int32x4::lanes]; for (unsigned i = 0; i < Int32x4::lanes; i++) result[i] = args[i].toBoolean() ? 0xFFFFFFFF : 0x0; 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> static bool BitSelect(JSContext *cx, unsigned argc, Value *vp) { typedef typename V::Elem Elem; @@ -982,18 +1058,17 @@ Load(JSContext *cx, unsigned argc, Value Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global())); MOZ_ASSERT(typeDescr); Rooted<TypedObject *> result(cx, OutlineTypedObject::createZeroed(cx, typeDescr, 0)); if (!result) 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; } template<class V, unsigned NumElem> static bool Store(JSContext *cx, unsigned argc, Value *vp) @@ -1007,31 +1082,39 @@ Store(JSContext *cx, unsigned argc, Valu Elem *typedArrayData = nullptr; if (!TypedArrayDataPtrFromArgs<Elem, NumElem>(cx, args, &typedArrayData)) return false; if (!IsVectorObject<V>(args[2])) 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; } #define DEFINE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \ bool \ 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 \ js::simd_int32x4_##Name(JSContext *cx, unsigned argc, Value *vp) \ { \ return Func(cx, argc, vp); \ } INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X4_FUNCTION) @@ -1040,15 +1123,23 @@ INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X const JSFunctionSpec js::Float32x4Methods[] = { #define SIMD_FLOAT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags) \ JS_FN(#Name, js::simd_float32x4_##Name, Operands, Flags), FLOAT32X4_FUNCTION_LIST(SIMD_FLOAT32X4_FUNCTION_ITEM) #undef SIMD_FLOAT32x4_FUNCTION_ITEM 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), INT32X4_FUNCTION_LIST(SIMD_INT32X4_FUNCTION_ITEM) #undef SIMD_INT32X4_FUNCTION_ITEM JS_FS_END };
--- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -17,16 +17,18 @@ /* * JS SIMD functions. * Spec matching polyfill: * https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js */ #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) \ V(not, (CoercedUnaryFunc<Float32x4, Int32x4, Not, Float32x4>), 1, 0) \ V(reciprocal, (UnaryFunc<Float32x4, Rec, Float32x4>), 1, 0) \ V(reciprocalSqrt, (UnaryFunc<Float32x4, RecSqrt, Float32x4>), 1, 0) \ V(splat, (FuncSplat<Float32x4>), 1, 0) \ V(sqrt, (UnaryFunc<Float32x4, Sqrt, Float32x4>), 1, 0) @@ -59,32 +61,83 @@ V(withX, (FuncWith<Float32x4, WithX>), 2, 0) \ V(withY, (FuncWith<Float32x4, WithY>), 2, 0) \ V(withZ, (FuncWith<Float32x4, WithZ>), 2, 0) \ V(withW, (FuncWith<Float32x4, WithW>), 2, 0) \ V(xor, (CoercedBinaryFunc<Float32x4, Int32x4, Xor, Float32x4>), 2, 0) #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) \ V(swizzle, Swizzle<Float32x4>, 2, 0) \ V(shuffle, Shuffle<Float32x4>, 3, 0) #define FLOAT32X4_FUNCTION_LIST(V) \ FLOAT32X4_UNARY_FUNCTION_LIST(V) \ FLOAT32X4_BINARY_FUNCTION_LIST(V) \ 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) #define INT32X4_BINARY_FUNCTION_LIST(V) \ V(add, (BinaryFunc<Int32x4, Add, Int32x4>), 2, 0) \ V(and, (BinaryFunc<Int32x4, And, Int32x4>), 2, 0) \ V(equal, (CompareFunc<Int32x4, Equal>), 2, 0) \ @@ -207,16 +260,36 @@ struct Float32x4 { *out = v.toNumber(); return true; } static void setReturn(CallArgs &args, Elem value) { args.rval().setDouble(JS::CanonicalizeNaN(value)); } }; +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; static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_INT32; static TypeDescr &GetTypeDescr(GlobalObject &global) { return global.int32x4TypeDescr().as<TypeDescr>(); } @@ -241,16 +314,22 @@ template<typename V> bool ToSimdConstant(JSContext *cx, HandleValue v, jit::SimdConstant *out); #define DECLARE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \ extern bool \ 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); INT32X4_FUNCTION_LIST(DECLARE_SIMD_INT32x4_FUNCTION) #undef DECLARE_SIMD_INT32x4_FUNCTION } /* namespace js */
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -401,40 +401,53 @@ js::ReferenceTypeDescr::call(JSContext * return true; } } MOZ_CRASH("Unhandled Reference type"); } /*************************************************************************** - * 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) { return SimdSizes[t]; } int32_t SimdTypeDescr::alignment(Type t) { return SimdSizes[t]; } +int32_t +SimdTypeDescr::lanes(Type t) +{ + return SimdLanes[t]; +} + /*************************************************************************** * ArrayMetaTypeDescr class */ /* * For code like: * * var A = new TypedObject.ArrayType(uint8, 10); @@ -2690,16 +2703,26 @@ js::GetFloat32x4TypeDescr(JSContext *cx, CallArgs args = CallArgsFromVp(argc, vp); Rooted<GlobalObject*> global(cx, cx->global()); MOZ_ASSERT(global); args.rval().setObject(global->float32x4TypeDescr()); 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) { CallArgs args = CallArgsFromVp(argc, vp); Rooted<GlobalObject*> global(cx, cx->global()); MOZ_ASSERT(global); args.rval().setObject(global->int32x4TypeDescr()); return true; }
--- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -320,43 +320,46 @@ class ComplexTypeDescr : public TypeDesc // Returns the prototype that instances of this type descriptor // will have. TypedProto &instancePrototype() const { return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>(); } }; /* - * Type descriptors `float32x4` and `int32x4` + * Type descriptors `float32x4`, `int32x4` and `float64x2` */ class SimdTypeDescr : public ComplexTypeDescr { public: enum Type { TYPE_INT32 = JS_SIMDTYPEREPR_INT32, TYPE_FLOAT32 = JS_SIMDTYPEREPR_FLOAT32, + TYPE_FLOAT64 = JS_SIMDTYPEREPR_FLOAT64 }; static const type::Kind Kind = type::Simd; static const bool Opaque = false; 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(); } static bool call(JSContext *cx, unsigned argc, Value *vp); static bool is(const Value &v); }; #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); bool CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr obj); class ArrayTypeDescr; @@ -876,16 +879,24 @@ bool GetTypedObjectModule(JSContext *cx, * Usage: GetFloat32x4TypeDescr() * * Returns the float32x4 type object. SIMD pseudo-module must have * been initialized for this to be safe. */ 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() * * Returns the int32x4 type object. SIMD pseudo-module must have * been initialized for this to be safe. */ bool GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp); /*
--- a/js/src/builtin/TypedObject.js +++ b/js/src/builtin/TypedObject.js @@ -147,16 +147,21 @@ function TypedObjectGetSimd(descr, typed switch (type) { case JS_SIMDTYPEREPR_FLOAT32: var x = Load_float32(typedObj, offset + 0); var y = Load_float32(typedObj, offset + 4); var z = Load_float32(typedObj, offset + 8); 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); var z = Load_int32(typedObj, offset + 8); var w = Load_int32(typedObj, offset + 12); return GetInt32x4TypeDescr()(x, y, z, w); } @@ -317,16 +322,20 @@ function TypedObjectSetSimd(descr, typed var type = DESCR_TYPE(descr); switch (type) { case JS_SIMDTYPEREPR_FLOAT32: Store_float32(typedObj, offset + 0, Load_float32(fromValue, 0)); Store_float32(typedObj, offset + 4, Load_float32(fromValue, 4)); 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)); Store_int32(typedObj, offset + 8, Load_int32(fromValue, 8)); Store_int32(typedObj, offset + 12, Load_int32(fromValue, 12)); break; default: assert(false, "Unhandled Simd type: " + type); @@ -452,33 +461,53 @@ function TypedObjectArrayRedimension(new // SIMD function SimdProtoString(type) { switch (type) { case JS_SIMDTYPEREPR_INT32: 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"); return undefined; } function SimdToSource() { if (!IsObject(this) || !ObjectIsTypedObject(this)) ThrowError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this); var descr = TypedObjectTypeDescr(this); if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND) 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+")"; } /////////////////////////////////////////////////////////////////////////// // Miscellaneous function DescrsEquiv(descr1, descr2) { assert(IsObject(descr1) && ObjectIsTypeDescr(descr1), "descr1 not descr"); assert(IsObject(descr2) && ObjectIsTypeDescr(descr2), "descr2 not descr");
--- a/js/src/builtin/TypedObjectConstants.h +++ b/js/src/builtin/TypedObjectConstants.h @@ -27,18 +27,19 @@ #define JS_DESCR_SLOT_STRING_REPR 1 // Atomized string representation #define JS_DESCR_SLOT_ALIGNMENT 2 // Alignment in bytes #define JS_DESCR_SLOT_SIZE 3 // Size in bytes, else 0 #define JS_DESCR_SLOT_OPAQUE 4 // Atomized string representation #define JS_DESCR_SLOT_TYPROTO 5 // Prototype for instances, if any #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 #define JS_DESCR_SLOT_ARRAY_LENGTH 9 // Slots on struct type objects #define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 8 #define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 9 @@ -80,10 +81,11 @@ #define JS_REFERENCETYPEREPR_STRING 2 // These constants are for use exclusively in JS code. In C++ code, // prefer SimdTypeRepresentation::TYPE_INT32 etc, since that allows // you to write a switch which will receive a warning if you omit a // case. #define JS_SIMDTYPEREPR_INT32 0 #define JS_SIMDTYPEREPR_FLOAT32 1 +#define JS_SIMDTYPEREPR_FLOAT64 2 #endif
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/clamp.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty("SIMD")) +var float32x4 = SIMD.float32x4; +var float64x2 = SIMD.float64x2; + +var summary = 'float32x4/float64x2 clamp'; + +function test() { + // 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); + var upper = float32x4(2.5, 5, 55, 1); + var c = SIMD.float32x4.clamp(a, lower, upper); + assertEq(c.x, 2); + assertEq(c.y, 5); + assertEq(c.z, 50); + assertEq(c.w, 0.5); + + var d = float32x4(-13.37, 10.46, 31.79, 0.54); + var lower1 = float32x4(2.1, 1.1, 50.13, 0.0); + var upper1 = float32x4(2.56, 5.55, 55.93, 1.1); + var f = SIMD.float32x4.clamp(d, lower1, upper1); + assertEq(f.x, Math.fround(2.1)); + assertEq(f.y, Math.fround(5.55)); + assertEq(f.z, Math.fround(50.13)); + assertEq(f.w, Math.fround(0.54)); + + var g = float32x4(4, -Infinity, 10, -10); + var lower2 = float32x4(5, -Infinity, -Infinity, -Infinity); + var upper2 = float32x4(Infinity, 5, Infinity, Infinity); + var i = SIMD.float32x4.clamp(g, lower2, upper2); + assertEq(i.x, 5); + assertEq(i.y, -Infinity); + 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); +} + +test(); +
--- a/js/src/tests/ecma_7/SIMD/comparisons.js +++ b/js/src/tests/ecma_7/SIMD/comparisons.js @@ -1,60 +1,80 @@ // |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; var int32x4 = SIMD.int32x4; var fround = Math.fround; function boolToSimdLogical(b) { return b ? 0xFFFFFFFF | 0 : 0x0; } 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() { var float32x4val = [ 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), @@ -70,16 +90,42 @@ function test() { testNotEqualFloat32x4(v, w); testLessThanFloat32x4(v, w); testLessThanOrEqualFloat32x4(v, w); testGreaterThanFloat32x4(v, w); testGreaterThanOrEqualFloat32x4(v, w); } } + 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), int32x4(-1, 2, -3, 4), int32x4(1, -2, 3, -4), int32x4(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN), int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN) ];
deleted file mode 100644 --- a/js/src/tests/ecma_7/SIMD/float32x4-minmax.js +++ /dev/null @@ -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(); -
deleted file mode 100644 --- a/js/src/tests/ecma_7/SIMD/float32x4clamp.js +++ /dev/null @@ -1,45 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 946042; -var float32x4 = SIMD.float32x4; -var int32x4 = SIMD.int32x4; - -var summary = 'float32x4 clamp'; - -function test() { - print(BUGNUMBER + ": " + summary); - - // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined. - - var a = float32x4(-20, 10, 30, 0.5); - var lower = float32x4(2, 1, 50, 0); - var upper = float32x4(2.5, 5, 55, 1); - var c = SIMD.float32x4.clamp(a, lower, upper); - assertEq(c.x, 2); - assertEq(c.y, 5); - assertEq(c.z, 50); - assertEq(c.w, 0.5); - - var d = float32x4(-13.37, 10.46, 31.79, 0.54); - var lower1 = float32x4(2.1, 1.1, 50.13, 0.0); - var upper1 = float32x4(2.56, 5.55, 55.93, 1.1); - var f = SIMD.float32x4.clamp(d, lower1, upper1); - assertEq(f.x, Math.fround(2.1)); - assertEq(f.y, Math.fround(5.55)); - assertEq(f.z, Math.fround(50.13)); - assertEq(f.w, Math.fround(0.54)); - - var g = float32x4(4, -Infinity, 10, -10); - var lower2 = float32x4(5, -Infinity, -Infinity, -Infinity); - var upper2 = float32x4(Infinity, 5, Infinity, Infinity); - var i = SIMD.float32x4.clamp(g, lower2, upper2); - assertEq(i.x, 5); - assertEq(i.y, -Infinity); - assertEq(i.z, 10); - assertEq(i.w, -10); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); -
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2.js @@ -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(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2bits.js @@ -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(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2-arithmetic.js @@ -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(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2alignment.js @@ -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();
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4.js @@ -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(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4bits.js @@ -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(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2fromint32x4.js @@ -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(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2fromint32x4bits.js @@ -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(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2getters.js @@ -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();
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2handle.js @@ -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();
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2reify.js @@ -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();
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2setter.js @@ -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();
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/float64x2with.js @@ -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(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2.js @@ -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(); +
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2bits.js @@ -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(); +
--- a/js/src/tests/ecma_7/SIMD/load.js +++ b/js/src/tests/ecma_7/SIMD/load.js @@ -1,20 +1,20 @@ // |reftest| skip-if(!this.hasOwnProperty("SIMD")) /* * Any copyright is dedicated to the Public Domain. * 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; var uint8 = (bpe != 1) ? new Uint8Array(arr.buffer) : arr; // Size in bytes of a single element in the SIMD vector. var sizeOfLaneElem; // Typed array constructor corresponding to the SIMD kind. @@ -23,20 +23,24 @@ function MakeComparator(kind, arr) { case 'int32x4': sizeOfLaneElem = 4; typedArrayCtor = Int32Array; break; case 'float32x4': 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) { // Read enough bytes var startBytes = start * bpe; var endBytes = startBytes + numElemToRead * sizeOfLaneElem; var asArray = Array.prototype.slice.call(uint8, startBytes, endBytes); @@ -44,41 +48,48 @@ function MakeComparator(kind, arr) { // This is needed for loadX, loadXY, loadXYZ which do only partial // reads. for (var i = asArray.length; i < SIZE_BYTES; i++) asArray[i] = 0; assertEq(asArray.length, SIZE_BYTES); 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)); + loadXYZ: function(index) { + 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 [ new Uint8Array(TA.buffer), new Int8Array(TA.buffer), new Uint16Array(TA.buffer), new Int16Array(TA.buffer), new Uint32Array(TA.buffer), @@ -92,17 +103,17 @@ function testLoad(kind, TA) { assertThrowsInstanceOf(() => SIMD[kind].load(ta), TypeError); assertThrowsInstanceOf(() => SIMD[kind].load("hello", 0), TypeError); assertThrowsInstanceOf(() => SIMD[kind].load(ta, -1), RangeError); // Valid and invalid reads 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; C.load(0); C.load(1); C.load(2); C.load(3); @@ -116,39 +127,45 @@ function testLoad(kind, TA) { C.loadX(lastValidArgLoadX); assertThrowsInstanceOf(() => SIMD[kind].loadX(ta, lastValidArgLoadX + 1), RangeError); C.loadXY(0); C.loadXY(1); 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); - assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), RangeError); + + if (lanes >= 4) { + assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), RangeError); + assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), RangeError); + } } - // Test ToInt32 behavior - var v = SIMD[kind].load(TA, 12.5); - assertEqX4(v, [12, 13, 14, 15]); + if (lanes == 4) { + // Test ToInt32 behavior + var v = SIMD[kind].load(TA, 12.5); + assertEqX4(v, [12, 13, 14, 15]); - var obj = { - valueOf: function() { return 12 } + var obj = { + valueOf: function() { return 12 } + } + var v = SIMD[kind].load(TA, obj); + assertEqX4(v, [12, 13, 14, 15]); } - 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"); } } 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);
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/minmax.js @@ -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(); +
--- a/js/src/tests/ecma_7/SIMD/select-bitselect.js +++ b/js/src/tests/ecma_7/SIMD/select-bitselect.js @@ -1,16 +1,17 @@ // |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; var int32x4 = SIMD.int32x4; function select(mask, ifTrue, ifFalse) { var m = simdToArray(mask); var tv = simdToArray(ifTrue); var fv = simdToArray(ifFalse); return m.map(function(v, i) { return (v < 0 ? tv : fv)[i]; @@ -22,63 +23,63 @@ function select(mask, ifTrue, ifFalse) { * has 4 lanes and 2 possible values (true or false), there are 16 possible * masks. */ function testSelect(type, inputs) { var x, y; 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); +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."); + } +} - 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; - } - - 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"); } } /** * This tests type.bitselect on all boolean masks, as in select. For these, * bitselect(mask, x, y) === select(mask, x, y) */ function testBitSelectSimple(type, inputs) { var x, y; var ScalarTypedArray = findCorrespondingScalarTypedArray(type); 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)); } } /** * This tests type.bitselect on a few hand-defined masks. For these, * bitselect(mask, x, y) !== select(mask, x, y) */ function testBitSelectComplex(type, inputs) { @@ -86,17 +87,17 @@ function testBitSelectComplex(type, inpu var masks = [ int32x4(1337, 0x1337, 0x42, 42), int32x4(0x00FF1CE, 0xBAADF00D, 0xDEADBEEF, 0xCAFED00D), int32x4(0xD15EA5E, 0xDEADC0DE, 0xFACEB00C, 0x4B1D4B1D) ]; 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)); } } function test() { var inputs = [ [int32x4(0,4,9,16), int32x4(1,2,3,4)], [int32x4(-1, 2, INT32_MAX, INT32_MIN), int32x4(INT32_MAX, -4, INT32_MIN, 42)] ]; @@ -110,13 +111,26 @@ function test() { [float32x4(-1.5,-0,NaN,-Infinity), float32x4(1,-2,13.37,3.13)], [float32x4(1.5,2.75,NaN,Infinity), float32x4(-NaN,-Infinity,9.75,16.125)] ]; testSelect(float32x4, inputs); 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); } test();
--- a/js/src/tests/ecma_7/SIMD/shell.js +++ b/js/src/tests/ecma_7/SIMD/shell.js @@ -1,39 +1,99 @@ +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]); assertEq(v.y, arr[1]); assertEq(v.z, arr[2]); assertEq(v.w, arr[3]); } catch (e) { print("stack trace:", e.stack); throw e; } } +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) { - return [v.x, v.y, v.z, v.w]; + 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); var observed = simdToArray(simdFunc(v, w)); var expected = varr.map(function(v, i) { return func(varr[i], warr[i]); }); for (var i = 0; i < observed.length; i++) 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); var observed = simdToArray(simdFunc(v, scalar)); var expected = varr.map(function(v, i) { return func(varr[i], scalar); }); for (var i = 0; i < observed.length; i++) assertEq(observed[i], expected[i]);
--- a/js/src/tests/ecma_7/SIMD/store.js +++ b/js/src/tests/ecma_7/SIMD/store.js @@ -27,26 +27,28 @@ function assertChanged(ta, from, expecte } function testStore(ta, kind, i, v) { reset(ta); SIMD[kind].storeX(ta, i, v); assertChanged(ta, i, [v.x]); reset(ta); - SIMD[kind].storeXY(ta, i, v); - assertChanged(ta, i, [v.x, v.y]); + SIMD[kind].store(ta, i, v); + assertChanged(ta, i, simdToArray(v)); - reset(ta); - SIMD[kind].storeXYZ(ta, i, v); - assertChanged(ta, i, [v.x, v.y, v.z]); + if (simdLength(v) > 2) { + reset(ta); + SIMD[kind].storeXY(ta, i, v); + assertChanged(ta, i, [v.x, v.y]); - reset(ta); - SIMD[kind].store(ta, i, v); - assertChanged(ta, i, [v.x, v.y, v.z, v.w]); + reset(ta); + SIMD[kind].storeXYZ(ta, i, v); + assertChanged(ta, i, [v.x, v.y, v.z]); + } } function testStoreInt32x4() { var I32 = new Int32Array(16); var v = SIMD.int32x4(0, 1, Math.pow(2,31) - 1, -Math.pow(2, 31)); testStore(I32, 'int32x4', 0, v); testStore(I32, 'int32x4', 1, v); @@ -73,13 +75,37 @@ function testStoreFloat32x4() { testStore(F32, 'float32x4', 2, v); testStore(F32, 'float32x4', 12, v); assertThrowsInstanceOf(() => SIMD.float32x4.store(F32), TypeError); assertThrowsInstanceOf(() => SIMD.float32x4.store(F32, 0), TypeError); 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);
--- a/js/src/tests/ecma_7/SIMD/swizzle-shuffle.js +++ b/js/src/tests/ecma_7/SIMD/swizzle-shuffle.js @@ -1,47 +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; 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. - 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)); + 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]; + 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. // n.b, order of evaluation of args is left-to-right. var obj = { 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 = { valueOf: function() { throw new Error; } }; assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, obj), Error); } @@ -60,52 +98,98 @@ function testSwizzleFloat32x4() { assertThrowsInstanceOf(function() { int32x4.swizzle(v, 0, 0, 0, 0); }, TypeError); 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], (w < 4 ? lhsa : rhsa)[w % 4]]; } 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; - 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)); + 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]; + 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. // n.b, order of evaluation of args is left-to-right. var obj = { 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 = { valueOf: function() { throw new Error; } }; assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, obj), Error); } @@ -124,15 +208,27 @@ function testShuffleFloat32x4() { assertThrowsInstanceOf(function() { int32x4.shuffle(v, v, 0, 0, 0, 0); }, TypeError); 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);
--- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -74,16 +74,17 @@ macro(false, false_, "false") \ macro(fieldOffsets, fieldOffsets, "fieldOffsets") \ macro(fieldTypes, fieldTypes, "fieldTypes") \ macro(fileName, fileName, "fileName") \ macro(fix, fix, "fix") \ 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") \ macro(from, from, "from") \ macro(get, get, "get") \ macro(getInternals, getInternals, "getInternals") \ macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \ macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
--- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -99,18 +99,19 @@ class GlobalObject : public NativeObject static const unsigned NUMBER_FORMAT_PROTO = COLLATOR_PROTO + 1; static const unsigned DATE_TIME_FORMAT_PROTO = NUMBER_FORMAT_PROTO + 1; static const unsigned REGEXP_STATICS = DATE_TIME_FORMAT_PROTO + 1; static const unsigned WARNED_WATCH_DEPRECATED = REGEXP_STATICS + 1; static const unsigned WARNED_PROTO_SETTING_SLOW = WARNED_WATCH_DEPRECATED + 1; static const unsigned RUNTIME_CODEGEN_ENABLED = WARNED_PROTO_SETTING_SLOW + 1; 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 FLOAT32X4_TYPE_DESCR = INTRINSICS + 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. */ static const unsigned RESERVED_SLOTS = FOR_OF_PIC_CHAIN + 1; /* * The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, and * we won't expose GlobalObject, so just assert that the two values are @@ -403,16 +404,26 @@ class GlobalObject : public NativeObject setSlot(FLOAT32X4_TYPE_DESCR, ObjectValue(obj)); } JSObject &float32x4TypeDescr() { MOZ_ASSERT(getSlotRef(FLOAT32X4_TYPE_DESCR).isObject()); 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)); } JSObject &int32x4TypeDescr() { MOZ_ASSERT(getSlotRef(INT32X4_TYPE_DESCR).isObject()); return getSlotRef(INT32X4_TYPE_DESCR).toObject();
--- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -964,16 +964,17 @@ static const JSFunctionSpec intrinsic_fu JS_FN("TypedObjectIsAttached", js::TypedObjectIsAttached, 1, 0), JS_FN("TypedObjectTypeDescr", js::TypedObjectTypeDescr, 1, 0), JS_FN("ObjectIsOpaqueTypedObject", js::ObjectIsOpaqueTypedObject, 1, 0), JS_FN("TypeDescrIsArrayType", js::TypeDescrIsArrayType, 1, 0), JS_FN("TypeDescrIsSimpleType", js::TypeDescrIsSimpleType, 1, 0), 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) \ JS_FN("Store_" #_name, js::StoreScalar##_type::Func, 3, 0), \ JS_FN("Load_" #_name, js::LoadScalar##_type::Func, 3, 0), JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(LOAD_AND_STORE_SCALAR_FN_DECLS) #undef LOAD_AND_STORE_SCALAR_FN_DECLS
--- a/layout/base/SelectionCarets.cpp +++ b/layout/base/SelectionCarets.cpp @@ -579,28 +579,40 @@ SelectionCarets::SelectWord() if (!selectable) { SELECTIONCARETS_LOG(" frame %p is not selectable", ptFrame); return NS_ERROR_FAILURE; } 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); // Long tap on the content with empty text, no action for // selectioncarets but need to dispatch the touchcarettap event // to support the short cut mode DispatchCustomEvent(NS_LITERAL_STRING("touchcarettap")); return NS_OK;
--- a/layout/base/tests/marionette/test_selectioncarets.py +++ b/layout/base/tests/marionette/test_selectioncarets.py @@ -34,37 +34,53 @@ class SelectionCaretsTest(MarionetteTest self.marionette.navigate(test_html) self._input = self.marionette.find_element(*self._input_selector) self._textarea = self.marionette.find_element(*self._textarea_selector) self._textarea_rtl = self.marionette.find_element(*self._textarea_rtl_selector) 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() + + return sel.caret_location() + + def _long_press_to_select(self, el, x, y): + '''Long press the location (x, y) to select a word. - # 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) + 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) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 2, 'Expect at least two words in the content.') 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()) def _test_move_selection_carets(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() @@ -74,60 +90,98 @@ class SelectionCaretsTest(MarionetteTest target_content = original_content[len(words[0]):] # Get the location of the selection carets at the end of the content for # later use. el.tap() 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() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the left caret to the previous position of the right caret. self.actions.flick(el, caret1_x, caret2_y, caret2_x, caret2_y).perform() # 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() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # 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() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y,).perform() 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 ######################################################################## def test_input_long_press_to_select_a_word(self): self.openTestHtml(enabled=True) self._test_long_press_to_select_a_word(self._input, self.assertEqual) def test_input_move_selection_carets(self): self.openTestHtml(enabled=True) self._test_move_selection_carets(self._input, self.assertEqual) def test_input_minimum_select_one_caracter(self): 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 ######################################################################## def test_input_long_press_to_select_a_word_disabled(self): self.openTestHtml(enabled=False) self._test_long_press_to_select_a_word(self._input, self.assertNotEqual) def test_input_move_selection_carets_disabled(self): @@ -144,16 +198,28 @@ class SelectionCaretsTest(MarionetteTest def test_textarea_move_selection_carets(self): self.openTestHtml(enabled=True) self._test_move_selection_carets(self._textarea, self.assertEqual) def test_textarea_minimum_select_one_caracter(self): 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 ######################################################################## def test_textarea_long_press_to_select_a_word_disabled(self): self.openTestHtml(enabled=False) self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual) def test_textarea_move_selection_carets_disable(self): @@ -196,16 +262,28 @@ class SelectionCaretsTest(MarionetteTest def test_contenteditable_move_selection_carets(self): self.openTestHtml(enabled=True) self._test_move_selection_carets(self._contenteditable, self.assertEqual) def test_contenteditable_minimum_select_one_character(self): 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 ######################################################################## def test_contenteditable_long_press_to_select_a_word_disabled(self): self.openTestHtml(enabled=False) self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_selection_carets_disabled(self): @@ -213,8 +291,21 @@ class SelectionCaretsTest(MarionetteTest self._test_move_selection_carets(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> non-editable test cases with selection carets enabled ######################################################################## 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) +
--- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -2920,18 +2920,34 @@ nsCSSRuleProcessor::MediumFeaturesChange } return false; } 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>(); } return MakeUnique<nsMediaQueryResultCacheKey>(c->mCacheKey); } /* virtual */ size_t nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/layout/style/test/test_bug1089417.html +++ b/layout/style/test/test_bug1089417.html @@ -19,16 +19,23 @@ https://bugzilla.mozilla.org/show_bug.cg var fwin = f.contentWindow; var fdoc = f.contentDocument; f.height = "400"; fdoc.getElementById("s").disabled = false; 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(); } </script> </head> <body onload="run()"> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1089417">Mozilla Bug 1089417</a> <div id="display">
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3747,23 +3747,30 @@ pref("browser.zoom.reflowZoom.reflowTime * but has no effect if browser.zoom.reflowOnZoom is disabled. * * Note that this should be turned off only in cases where debugging of the * reflow-on-zoom feature is necessary, and enabling the feature during * a page load inhbits this debugging. */ 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"); pref("image.high_quality_downscaling.enabled", true); // The minimum percent downscaling we'll use high-quality downscaling on, // interpreted as a floating-point number / 1000. pref("image.high_quality_downscaling.min_factor", 1000);
--- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -1361,16 +1361,25 @@ HttpBaseChannel::IsNoCacheResponse(bool return NS_ERROR_NOT_AVAILABLE; *value = mResponseHead->NoCache(); if (!*value) *value = mResponseHead->ExpiresInPast(); 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) { if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; *aValue = mResponseHead->Status(); return NS_OK; }
--- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -148,16 +148,17 @@ public: NS_IMETHOD GetAllowPipelining(bool *value) MOZ_OVERRIDE; NS_IMETHOD SetAllowPipelining(bool value) MOZ_OVERRIDE; NS_IMETHOD GetAllowSTS(bool *value) MOZ_OVERRIDE; NS_IMETHOD SetAllowSTS(bool value) MOZ_OVERRIDE; NS_IMETHOD GetRedirectionLimit(uint32_t *value) MOZ_OVERRIDE; 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; NS_IMETHOD RedirectTo(nsIURI *newURI) MOZ_OVERRIDE; // nsIHttpChannelInternal NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI) MOZ_OVERRIDE; NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI) MOZ_OVERRIDE;
--- a/netwerk/protocol/http/NullHttpChannel.cpp +++ b/netwerk/protocol/http/NullHttpChannel.cpp @@ -189,16 +189,22 @@ NullHttpChannel::IsNoStoreResponse(bool NS_IMETHODIMP 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) { return NS_ERROR_NOT_IMPLEMENTED; } //----------------------------------------------------------------------------- // NullHttpChannel::nsIChannel //-----------------------------------------------------------------------------
--- a/netwerk/protocol/http/PHttpChannelParams.h +++ b/netwerk/protocol/http/PHttpChannelParams.h @@ -140,30 +140,32 @@ struct ParamTraits<mozilla::net::nsHttpR { WriteParam(aMsg, aParam.mHeaders); WriteParam(aMsg, aParam.mVersion); WriteParam(aMsg, aParam.mStatus); WriteParam(aMsg, aParam.mStatusText); 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); } static bool Read(const Message* aMsg, void** aIter, paramType* aResult) { if (!ReadParam(aMsg, aIter, &aResult->mHeaders) || !ReadParam(aMsg, aIter, &aResult->mVersion) || !ReadParam(aMsg, aIter, &aResult->mStatus) || !ReadParam(aMsg, aIter, &aResult->mStatusText) || !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)) return false; return true; } };
--- a/netwerk/protocol/http/nsHttpResponseHead.cpp +++ b/netwerk/protocol/http/nsHttpResponseHead.cpp @@ -619,16 +619,17 @@ nsHttpResponseHead::Reset() { LOG(("nsHttpResponseHead::Reset\n")); ClearHeaders(); mVersion = NS_HTTP_VERSION_1_1; mStatus = 200; mContentLength = UINT64_MAX; + mCacheControlPrivate = false; mCacheControlNoStore = false; mCacheControlNoCache = false; mPragmaNoCache = false; mStatusText.Truncate(); mContentType.Truncate(); mContentCharset.Truncate(); } @@ -787,21 +788,26 @@ nsHttpResponseHead::ParseVersion(const c mVersion = NS_HTTP_VERSION_1_0; } void 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)) mCacheControlNoCache = true; // search header value for occurrence of "no-store" if (nsHttp::FindToken(val, "no-store", HTTP_HEADER_VALUE_SEPS)) mCacheControlNoStore = true;
--- a/netwerk/protocol/http/nsHttpResponseHead.h +++ b/netwerk/protocol/http/nsHttpResponseHead.h @@ -18,30 +18,32 @@ namespace mozilla { namespace net { //----------------------------------------------------------------------------- class nsHttpResponseHead { public: nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1) , mStatus(200) , mContentLength(UINT64_MAX) + , mCacheControlPrivate(false) , mCacheControlNoStore(false) , mCacheControlNoCache(false) , mPragmaNoCache(false) {} const nsHttpHeaderArray & Headers() const { return mHeaders; } nsHttpHeaderArray &Headers() { return mHeaders; } nsHttpVersion Version() const { return mVersion; } // X11's Xlib.h #defines 'Status' to 'int' on some systems! #undef Status uint16_t Status() const { return mStatus; } const nsAFlatCString &StatusText() const { return mStatusText; } 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); } /** * Full length of the entity. For byte-range requests, this may be larger * than ContentLength(), which will only represent the requested part of the * entity. */ int64_t TotalEntitySize() const; @@ -123,16 +125,17 @@ private: // All members must be copy-constructable and assignable nsHttpHeaderArray mHeaders; nsHttpVersion mVersion; uint16_t mStatus; nsCString mStatusText; int64_t mContentLength; nsCString mContentType; nsCString mContentCharset; + bool mCacheControlPrivate; bool mCacheControlNoStore; bool mCacheControlNoCache; bool mPragmaNoCache; friend struct IPC::ParamTraits<nsHttpResponseHead>; }; }} // namespace mozilla::net
--- a/netwerk/protocol/http/nsIHttpChannel.idl +++ b/netwerk/protocol/http/nsIHttpChannel.idl @@ -9,17 +9,17 @@ interface nsIHttpHeaderVisitor; /** * nsIHttpChannel * * This interface allows for the modification of HTTP request parameters and * 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 { /************************************************************************** * REQUEST CONFIGURATION * * Modifying request parameters after asyncOpen has been called is an error. */ @@ -295,16 +295,25 @@ interface nsIHttpChannel : nsIChannel * in the past relative to the value of the "Date" header. * * @throws NS_ERROR_NOT_AVAILABLE if called before the response * has been received (before onStartRequest). */ 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. * * This method provides no explicit conflict resolution. The last * caller to call it wins. * * @throws NS_ERROR_ALREADY_OPENED if called after the channel * has been opened.
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp +++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp @@ -764,14 +764,21 @@ nsViewSourceChannel::IsNoStoreResponse(b NS_IMETHODIMP nsViewSourceChannel::IsNoCacheResponse(bool *_retval) { return !mHttpChannel ? NS_ERROR_NULL_POINTER : mHttpChannel->IsNoCacheResponse(_retval); } NS_IMETHODIMP +nsViewSourceChannel::IsPrivateResponse(bool *_retval) +{ + return !mHttpChannel ? NS_ERROR_NULL_POINTER : + mHttpChannel->IsPrivateResponse(_retval); +} + +NS_IMETHODIMP nsViewSourceChannel::RedirectTo(nsIURI *uri) { return !mHttpChannel ? NS_ERROR_NULL_POINTER : mHttpChannel->RedirectTo(uri); }
--- a/netwerk/test/TestHttp.cpp +++ b/netwerk/test/TestHttp.cpp @@ -166,17 +166,17 @@ int main(int argc, char **argv) RETURN_IF_FAILED(rv, "NS_NewURI"); rv = NS_NewChannel(getter_AddRefs(chan), uri, nsContentUtils::GetSystemPrincipal(), 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"); while (gKeepRunning) gEventQ->ProcessPendingEvents(); printf(">>> done\n");
deleted file mode 100644 --- a/netwerk/test/TestPageLoad.cpp +++ /dev/null @@ -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; -}
deleted file mode 100644 --- a/netwerk/test/TestPerf.cpp +++ /dev/null @@ -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; -}
--- a/netwerk/test/TestProtocols.cpp +++ b/netwerk/test/TestProtocols.cpp @@ -644,17 +644,17 @@ nsresult StartLoadingURL(const char* aUr nsIContentPolicy::TYPE_OTHER, nullptr, // loadGroup callbacks, nsIRequest::LOAD_NORMAL, pService); 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; } nsCOMPtr<nsITimedChannel> timed(do_QueryInterface(pChannel)); if (timed) timed->SetTimingEnabled(true); nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(pChannel);
deleted file mode 100644 --- a/netwerk/test/TestSyncHTTP.cpp +++ /dev/null @@ -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; -}
deleted file mode 100644 --- a/netwerk/test/TestThreadedIO.cpp +++ /dev/null @@ -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; -}
--- a/netwerk/test/httpserver/test/test_basic_functionality.js +++ b/netwerk/test/httpserver/test/test_basic_functionality.js @@ -54,16 +54,17 @@ const HEADER_COUNT = 1000; // common properties *always* appended by server // or invariants for every URL in paths 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) { commonCheck(ch); do_check_eq(ch.responseStatus, 200); do_check_true(ch.requestSucceeded);
--- a/netwerk/test/moz.build +++ b/netwerk/test/moz.build @@ -19,31 +19,29 @@ XPCSHELL_TESTS_MANIFESTS += [ GeckoSimplePrograms([ 'PropertiesTest', 'ReadNTLM', 'TestBlockingSocket', 'TestCallbacks', 'TestDNS', 'TestIncrementalDownload', 'TestOpen', - 'TestPageLoad', 'TestProtocols', 'TestServ', 'TestStandardURL', 'TestStreamLoader', 'TestUpload', 'TestURLParser', 'urltest', ]) # XXX Make this work in libxul builds. #SIMPLE_PROGRAMS += [ # TestIDN', # TestIOThreads', -# TestPerf', # TestSocketTransport', # TestStreamChannel', # TestStreamPump', # TestStreamTransport', # TestUDPSocketProvider', #] CppUnitTests([
--- a/testing/marionette/client/marionette/selection.py +++ b/testing/marionette/client/marionette/selection.py @@ -100,17 +100,17 @@ class SelectionManager(object): considered. ''' range_count = self.range_count(); first_rect_list = self.selection_rect_list(0) last_rect_list = self.selection_rect_list(range_count - 1) last_list_length = last_rect_list['length'] first_rect, last_rect = first_rect_list['0'], last_rect_list[str(last_list_length - 1)] - origin_x, origin_y = self.element.location['x'], self.element.location['y'] + origin_x, origin_y = self.element.rect['x'], self.element.rect['y'] if self.element.get_attribute('dir') == 'rtl': # such as Arabic start_pos, end_pos = 'right', 'left' else: start_pos, end_pos = 'left', 'right' # Calculate y offset according to different needs. if location_type == 'center':
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini +++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini @@ -12,16 +12,17 @@ b2g = true skip = false [test_marionette.py] [test_data_driven.py] [test_session.py] [test_capabilities.py] [test_accessibility.py] +b2g = false [test_expectedfail.py] expected = fail [test_import_script.py] b2g = false [test_import_script_reuse_window.py] b2g = false [test_click.py]
--- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -150,16 +150,17 @@ #endif #endif #include "base/process_util.h" #include "mozilla/ArrayUtils.h" #include "mozilla/AutoRestore.h" #include "mozilla/CycleCollectedJSRuntime.h" +#include "mozilla/DebugOnly.h" #include "mozilla/HoldDropJSObjects.h" /* This must occur *after* base/process_util.h to avoid typedefs conflicts. */ #include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" #include "mozilla/SegmentedVector.h" #include "nsCycleCollectionParticipant.h" #include "nsCycleCollectionNoteRootCallback.h" @@ -1252,31 +1253,31 @@ class nsCycleCollector : public nsIMemor CycleCollectedJSRuntime* mJSRuntime; ccPhase mIncrementalPhase; CCGraph mGraph; nsAutoPtr<CCGraphBuilder> mBuilder; nsCOMPtr<nsICycleCollectorListener> mListener; - nsIThread* mThread; + DebugOnly<void*> mThread; nsCycleCollectorParams mParams; uint32_t mWhiteNodeCount; CC_BeforeUnlinkCallback mBeforeUnlinkCB; CC_ForgetSkippableCallback mForgetSkippableCB; nsPurpleBuffer mPurpleBuf; uint32_t mUnmergedNeeded; uint32_t mMergedInARow; - JSPurpleBuffer* mJSPurpleBuffer; + nsRefPtr<JSPurpleBuffer> mJSPurpleBuffer; private: virtual ~nsCycleCollector(); public: nsCycleCollector(); void RegisterJSRuntime(CycleCollectedJSRuntime* aJSRuntime); @@ -2036,17 +2037,17 @@ private: CCGraph& mGraph; CycleCollectorResults& mResults; NodePool::Builder mNodeBuilder; EdgePool::Builder mEdgeBuilder; PtrInfo* mCurrPi; nsCycleCollectionParticipant* mJSParticipant; nsCycleCollectionParticipant* mJSZoneParticipant; nsCString mNextEdgeName; - nsICycleCollectorListener* mListener; + nsCOMPtr<nsICycleCollectorListener> mListener; bool mMergeZones; bool mRanOutOfMemory; nsAutoPtr<NodePool::Enumerator> mCurrNode; public: CCGraphBuilder(CCGraph& aGraph, CycleCollectorResults& aResults, CycleCollectedJSRuntime* aJSRuntime, @@ -2573,39 +2574,37 @@ class JSPurpleBuffer { ~JSPurpleBuffer() { MOZ_ASSERT(mValues.IsEmpty()); MOZ_ASSERT(mObjects.IsEmpty()); } public: - explicit JSPurpleBuffer(JSPurpleBuffer*& aReferenceToThis) + explicit JSPurpleBuffer(nsRefPtr<JSPurpleBuffer>& aReferenceToThis) : mReferenceToThis(aReferenceToThis) , mValues(kSegmentSize) , mObjects(kSegmentSize) { mReferenceToThis = this; - NS_ADDREF_THIS(); mozilla::HoldJSObjects(this); } void Destroy() { mReferenceToThis = nullptr; mValues.Clear(); mObjects.Clear(); mozilla::DropJSObjects(this); - NS_RELEASE_THIS(); } NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(JSPurpleBuffer) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(JSPurpleBuffer) - JSPurpleBuffer*& mReferenceToThis; + nsRefPtr<JSPurpleBuffer>& mReferenceToThis; // These are raw pointers instead of Heap<T> because we only need Heap<T> for // pointers which may point into the nursery. The purple buffer never contains // pointers to the nursery because nursery gcthings can never be gray and only // gray things can be inserted into the purple buffer. static const size_t kSegmentSize = 512; SegmentedVector<JS::Value, kSegmentSize, InfallibleAllocPolicy> mValues; SegmentedVector<JSObject*, kSegmentSize, InfallibleAllocPolicy> mObjects; @@ -2737,17 +2736,17 @@ public: } virtual void Trace(JS::Heap<JSFunction*>* aFunction, const char* aName, void* aClosure) const { } private: - nsCycleCollector* mCollector; + nsRefPtr<nsCycleCollector> mCollector; ObjectsVector mObjects; }; class RemoveSkippableVisitor : public SnowWhiteKiller { public: RemoveSkippableVisitor(nsCycleCollector* aCollector, uint32_t aMaxCount, bool aRemoveChildlessNodes, @@ -3001,17 +3000,17 @@ public: if (pi->mColor == black) { return; } FloodBlackNode(mCount, mFailed, pi); } private: CCGraph& mGraph; - nsICycleCollectorListener* mListener; + nsCOMPtr<nsICycleCollectorListener> mListener; uint32_t& mCount; bool& mFailed; }; // Objects that have been stored somewhere since the start of incremental graph building must // be treated as live for this cycle collection, because we may not have accurate information // about who holds references to them. void @@ -3395,18 +3394,17 @@ nsCycleCollector::nsCycleCollector() : mScanInProgress(false), mJSRuntime(nullptr), mIncrementalPhase(IdlePhase), mThread(NS_GetCurrentThread()), mWhiteNodeCount(0), mBeforeUnlinkCB(nullptr), mForgetSkippableCB(nullptr), mUnmergedNeeded(0), - mMergedInARow(0), - mJSPurpleBuffer(nullptr) + mMergedInARow(0) { } nsCycleCollector::~nsCycleCollector() { UnregisterWeakMemoryReporter(this); }
deleted file mode 100644 --- a/xpcom/tests/CvtURL.cpp +++ /dev/null @@ -1,95 +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 "nscore.h" -#include "nsIConverterInputStream.h" -#include "nsIURL.h" -#include "nsNetUtil.h" -#include "nsCRT.h" -#include "nsString.h" -#include "prprf.h" -#include "prtime.h" - -static nsString* ConvertCharacterSetName(const char* aName) -{ - return new nsString(NS_ConvertASCIItoUTF16(aName)); -} - -int main(int argc, char** argv) -{ - if (3 != argc) { - printf("usage: CvtURL url utf8\n"); - return -1; - } - - char* characterSetName = argv[2]; - nsString* cset = ConvertCharacterSetName(characterSetName); - if (NS_PTR_TO_INT32(cset) < 0) { - printf("illegal character set name: '%s'\n", characterSetName); - return -1; - } - - // Create url object - char* urlName = argv[1]; - nsIURI* url; - nsresult rv; - rv = NS_NewURI(&url, urlName); - if (NS_OK != rv) { - printf("invalid URL: '%s'\n", urlName); - return -1; - } - - // Get an input stream from the url - nsIInputStream* in; - nsresult ec = NS_OpenURI(&in, - url, - nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_NORMAL, - nsIContentPolicy::TYPE_OTHER); - - if (nullptr == in) { - printf("open of url('%s') failed: error=%x\n", urlName, ec); - return -1; - } - - // Translate the input using the argument character set id into - // unicode - nsCOMPtr<nsIConverterInputStream> uin = - do_CreateInstance("@mozilla.org/intl/converter-input-stream;1", &rv); - if (NS_SUCCEEDED(rv)) - rv = uin->Init(in, cset->get(), 4096); - if (NS_FAILED(rv)) { - printf("can't create converter input stream: %d\n", rv); - return -1; - } - - // Read the input and write some output - PRTime start = PR_Now(); - int32_t count = 0; - for (;;) { - char16_t buf[1000]; - uint32_t nb; - ec = uin->Read(buf, 0, 1000, &nb); - if (NS_FAILED(ec)) { - printf("i/o error: %d\n", ec); - break; - } - if (nb == 0) break; // EOF - count += nb; - } - PRTime end = PR_Now(); - PRTime conversion = (end - start) / 1000; - char buf[500]; - PR_snprintf(buf, sizeof(buf), - "converting and discarding %d bytes took %lldms", - count, conversion); - puts(buf); - - // Release the objects - in->Release(); - url->Release(); - - return 0; -}