From cc852530e23bc81e639e8a62c14d32fd72910f3c Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Fri, 31 Jul 2015 12:28:45 -0700 Subject: [PATCH 001/113] Bug 1185491: Annotate reftest decoration-color-override-quirks.html as having 5 fuzzy pixels on Mac. (no review) --- layout/reftests/text-decoration/reftest.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/reftests/text-decoration/reftest.list b/layout/reftests/text-decoration/reftest.list index a3c7012c3f6b..bebc599e61af 100644 --- a/layout/reftests/text-decoration/reftest.list +++ b/layout/reftests/text-decoration/reftest.list @@ -96,7 +96,7 @@ fuzzy-if(B2G,255,1) == dynamic-underline-vertical-align-standards-2.html dynamic == text-decoration-propagation-1-standards.html text-decoration-propagation-1-standards-ref.html skip-if(B2G||Mulet) == 641444-1.html 641444-1-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop == decoration-css21.html decoration-css21-ref.html -== decoration-color-override-quirks.html decoration-color-override-quirks-ref.html +fuzzy-if(cocoaWidget,1,5) == decoration-color-override-quirks.html decoration-color-override-quirks-ref.html == decoration-color-override-standards.html decoration-color-override-standards-ref.html != decoration-color-override-standards-ref.html decoration-color-override-quirks-ref.html == decoration-css21-block.html decoration-css21-block-ref.html From 7ed9e0fe1b2a39ff210793fc5cc5fcec09a7a776 Mon Sep 17 00:00:00 2001 From: Jesse Ruderman Date: Fri, 31 Jul 2015 12:11:48 -0700 Subject: [PATCH 002/113] Bug 1024259: Use NS_FREE_PERMANENT_DATA more consistently. r=mccr8 --- dom/ipc/ContentChild.cpp | 3 ++- gfx/thebes/gfxPlatform.cpp | 9 +++++---- ipc/glue/GeckoChildProcessHost.cpp | 5 +++-- modules/libpref/init/all.js | 6 ++++-- xpcom/base/nscore.h | 2 +- xpcom/build/IOInterposer.cpp | 9 +++++---- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 02d6995196b6..ebf99617495b 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -193,6 +193,7 @@ #include "mozilla/RemoteSpellCheckEngineChild.h" #include "GMPServiceChild.h" #include "gfxPlatform.h" +#include "nscore.h" // for NS_FREE_PERMANENT_DATA using namespace mozilla; using namespace mozilla::docshell; @@ -1897,7 +1898,7 @@ ContentChild::ActorDestroy(ActorDestroyReason why) QuickExit(); } -#if !defined(DEBUG) && !defined(MOZ_ASAN) +#ifndef NS_FREE_PERMANENT_DATA // In release builds, there's no point in the content process // going through the full XPCOM shutdown path, because it doesn't // keep persistent state. diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index c98222bc676a..a995c32cb1a9 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -111,6 +111,7 @@ class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted { #include "nsIXULRuntime.h" #include "VsyncSource.h" #include "SoftwareVsyncSource.h" +#include "nscore.h" // for NS_FREE_PERMANENT_DATA namespace mozilla { namespace layers { @@ -259,7 +260,7 @@ void CrashStatsLogForwarder::UpdateCrashReport() mCrashCriticalKey.get(), message.str().c_str()); } } - + void CrashStatsLogForwarder::Log(const std::string& aString) { MutexAutoLock lock(mMutex); @@ -645,7 +646,7 @@ gfxPlatform::Shutdown() #endif // This is a bit iffy - we're assuming that we were the ones that set the - // log forwarder in the Factory, so that it's our responsibility to + // log forwarder in the Factory, so that it's our responsibility to // delete it. delete mozilla::gfx::Factory::GetLogForwarder(); mozilla::gfx::Factory::SetLogForwarder(nullptr); @@ -714,7 +715,7 @@ gfxPlatform::~gfxPlatform() // cairo_debug_* function unconditionally. // // because cairo can assert and thus crash on shutdown, don't do this in release builds -#if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING) || defined(MOZ_VALGRIND) +#ifdef NS_FREE_PERMANENT_DATA #ifdef USE_SKIA // must do Skia cleanup before Cairo cleanup, because Skia may be referencing // Cairo objects e.g. through SkCairoFTTypeface @@ -1201,7 +1202,7 @@ gfxPlatform::CreateDrawTargetForData(unsigned char* aData, const IntSize& aSize, NS_ASSERTION(mContentBackend != BackendType::NONE, "No backend."); BackendType backendType = mContentBackend; - + if (!Factory::DoesBackendSupportDataDrawtarget(mContentBackend)) { backendType = BackendType::CAIRO; } diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 23903cbe4d50..e90f521298fe 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -47,6 +47,7 @@ #include "nsClassHashtable.h" #include "nsHashKeys.h" #include "nsNativeCharsetUtils.h" +#include "nscore.h" // for NS_FREE_PERMANENT_DATA using mozilla::MonitorAutoLock; using mozilla::ipc::GeckoChildProcessHost; @@ -125,7 +126,7 @@ GeckoChildProcessHost::~GeckoChildProcessHost() SharedMemoryBasic::CleanupForPid(mChildProcessHandle); #endif ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle -#if defined(NS_BUILD_REFCNT_LOGGING) || defined(MOZ_ASAN) +#ifdef NS_FREE_PERMANENT_DATA // If we're doing leak logging, shutdown can be slow. , false // don't "force" #endif @@ -382,7 +383,7 @@ GeckoChildProcessHost::WaitUntilConnected(int32_t aTimeoutMs) { // NB: this uses a different mechanism than the chromium parent // class. - PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? + PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT; MonitorAutoLock lock(mMonitor); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 8675344b095e..fabf83aec7b4 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2477,7 +2477,9 @@ pref("plugin.sessionPermissionNow.intervalInMinutes", 60); // to allow it persistently. pref("plugin.persistentPermissionAlways.intervalInDays", 90); -#if !defined(DEBUG) && !defined(MOZ_ASAN) +// Set IPC timeouts for plugins and tabs, except in leak-checking builds. +// (NS_FREE_PERMANENT_DATA is C++ only, so approximate its definition here.) +#if !defined(DEBUG) && !defined(MOZ_ASAN) && !defined(MOZ_VALGRIND) // How long a plugin is allowed to process a synchronous IPC message // before we consider it "hung". pref("dom.ipc.plugins.timeoutSecs", 45); @@ -2503,7 +2505,7 @@ pref("dom.ipc.plugins.hangUIMinDisplaySecs", 10); // we fear the worst and kill it. pref("dom.ipc.tabs.shutdownTimeoutSecs", 5); #else -// No timeout in DEBUG or ASan builds +// No timeout in leak-checking builds pref("dom.ipc.plugins.timeoutSecs", 0); pref("dom.ipc.plugins.contentTimeoutSecs", 0); pref("dom.ipc.plugins.processLaunchTimeoutSecs", 0); diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h index 54fcb1d226ce..0ed0256dd7bc 100644 --- a/xpcom/base/nscore.h +++ b/xpcom/base/nscore.h @@ -234,7 +234,7 @@ * sense to touch memory pages and free that memory at shutdown, * unless we are running leak stats. */ -#if defined(NS_BUILD_REFCNT_LOGGING) || defined(MOZ_VALGRIND) +#if defined(NS_BUILD_REFCNT_LOGGING) || defined(MOZ_VALGRIND) || defined(MOZ_ASAN) #define NS_FREE_PERMANENT_DATA #endif diff --git a/xpcom/build/IOInterposer.cpp b/xpcom/build/IOInterposer.cpp index ea35e7821405..26ad0045aeb5 100644 --- a/xpcom/build/IOInterposer.cpp +++ b/xpcom/build/IOInterposer.cpp @@ -16,6 +16,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/StaticPtr.h" #include "mozilla/ThreadLocal.h" +#include "nscore.h" // for NS_FREE_PERMANENT_DATA #if !defined(XP_WIN) #include "NSPRInterposer.h" #endif // !defined(XP_WIN) @@ -460,10 +461,10 @@ IOInterposeObserver::IsMainThread() void IOInterposer::Clear() { - /* Clear() is a no-op on opt builds so that we may continue to trap I/O until - process termination. In debug builds we need to shut down IOInterposer so - that all references are properly released and refcnt log remains clean. */ -#if defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING) || defined(MOZ_ASAN) + /* Clear() is a no-op on release builds so that we may continue to trap I/O + until process termination. In leak-checking builds, we need to shut down + IOInterposer so that all references are properly released. */ +#ifdef NS_FREE_PERMANENT_DATA UnregisterCurrentThread(); sMasterList = nullptr; #endif From 941160c5f6b199677255745d1545846bcfdd723e Mon Sep 17 00:00:00 2001 From: Jesse Ruderman Date: Fri, 31 Jul 2015 13:06:12 -0700 Subject: [PATCH 003/113] Bug 1189587: Tighten signature of pref_HashTableLookup. r=njn --- modules/libpref/prefapi.cpp | 2 +- modules/libpref/prefapi_private_data.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/libpref/prefapi.cpp b/modules/libpref/prefapi.cpp index 9110cf346a1b..ffaaa72089ab 100644 --- a/modules/libpref/prefapi.cpp +++ b/modules/libpref/prefapi.cpp @@ -692,7 +692,7 @@ static void pref_SetValue(PrefValue* existingValue, uint16_t *existingFlags, } } -PrefHashEntry* pref_HashTableLookup(const void *key) +PrefHashEntry* pref_HashTableLookup(const char *key) { #ifndef MOZ_B2G MOZ_ASSERT(NS_IsMainThread()); diff --git a/modules/libpref/prefapi_private_data.h b/modules/libpref/prefapi_private_data.h index 4ccf8c1bc6f1..45b6a4d7338a 100644 --- a/modules/libpref/prefapi_private_data.h +++ b/modules/libpref/prefapi_private_data.h @@ -26,7 +26,7 @@ nsresult pref_SetPref(const mozilla::dom::PrefSetting& aPref); int pref_CompareStrings(const void *v1, const void *v2, void* unused); -PrefHashEntry* pref_HashTableLookup(const void *key); +PrefHashEntry* pref_HashTableLookup(const char *key); void pref_GetPrefFromEntry(PrefHashEntry *aHashEntry, mozilla::dom::PrefSetting* aPref); From 59b72bd9f4eed3233b1f6945a663c351220ea66c Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 31 Jul 2015 16:07:35 -0400 Subject: [PATCH 004/113] Bug 1189879 - Don't use a CSS viewport height of 480 in desktop mode; compute it from the screen aspect ratio. r=snorp --- mobile/android/chrome/content/browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 832a01e2d541..2c35b5b7e2f0 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -6602,7 +6602,7 @@ var ViewportHandler = { minZoom: kViewportMinScale, maxZoom: kViewportMaxScale, width: kDefaultCSSViewportWidth, - height: kDefaultCSSViewportHeight, + height: -1, allowZoom: true, allowDoubleTapZoom: true, isSpecified: false From 5a46946f862f54051404088657de138ab321d736 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 07:29:00 -0700 Subject: [PATCH 005/113] Bug 1187386 (Part 1) - Make most decoder state private. r=tn --HG-- extra : rebase_source : 0d9179626707ac70e772ed3d768ce4d802a144be --- image/Decoder.cpp | 24 ++++++++-------- image/Decoder.h | 47 +++++++++++++++----------------- image/decoders/nsJPEGDecoder.cpp | 2 +- image/decoders/nsPNGDecoder.cpp | 2 +- 4 files changed, 37 insertions(+), 38 deletions(-) diff --git a/image/Decoder.cpp b/image/Decoder.cpp index 0e6f3f1c6db3..6f883bf4507d 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -24,27 +24,29 @@ namespace mozilla { namespace image { Decoder::Decoder(RasterImage* aImage) - : mImage(aImage) - , mProgress(NoProgress) - , mImageData(nullptr) + : mImageData(nullptr) + , mImageDataLength(0) , mColormap(nullptr) + , mColormapSize(0) + , mImage(aImage) + , mProgress(NoProgress) + , mFrameCount(0) + , mFailCode(NS_OK) , mChunkCount(0) , mFlags(0) , mBytesDecoded(0) + , mInitialized(false) + , mMetadataDecode(false) , mSendPartialInvalidations(false) + , mImageIsTransient(false) + , mImageIsLocked(false) + , mInFrame(false) + , mIsAnimated(false) , mDataDone(false) , mDecodeDone(false) , mDataError(false) , mDecodeAborted(false) , mShouldReportError(false) - , mImageIsTransient(false) - , mImageIsLocked(false) - , mFrameCount(0) - , mFailCode(NS_OK) - , mInitialized(false) - , mMetadataDecode(false) - , mInFrame(false) - , mIsAnimated(false) { } Decoder::~Decoder() diff --git a/image/Decoder.h b/image/Decoder.h index 020a784ca5c2..9458f111cc16 100644 --- a/image/Decoder.h +++ b/image/Decoder.h @@ -389,10 +389,13 @@ protected: uint8_t aPaletteDepth, imgFrame* aPreviousFrame); - /* - * Member variables. - * - */ +protected: + uint8_t* mImageData; // Pointer to image data in either Cairo or 8bit format + uint32_t mImageDataLength; + uint32_t* mColormap; // Current colormap to be used in Cairo format + uint32_t mColormapSize; + +private: nsRefPtr mImage; Maybe mIterator; RawAccessFrameRef mCurrentFrame; @@ -400,10 +403,9 @@ protected: nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame. Progress mProgress; - uint8_t* mImageData; // Pointer to image data in either Cairo or 8bit format - uint32_t mImageDataLength; - uint32_t* mColormap; // Current colormap to be used in Cairo format - uint32_t mColormapSize; + uint32_t mFrameCount; // Number of frames, including anything in-progress + + nsresult mFailCode; // Telemetry data for this decoder. TimeDuration mDecodeTime; @@ -411,24 +413,19 @@ protected: uint32_t mFlags; size_t mBytesDecoded; - bool mSendPartialInvalidations; - bool mDataDone; - bool mDecodeDone; - bool mDataError; - bool mDecodeAborted; - bool mShouldReportError; - bool mImageIsTransient; - bool mImageIsLocked; -private: - uint32_t mFrameCount; // Number of frames, including anything in-progress - - nsresult mFailCode; - - bool mInitialized; - bool mMetadataDecode; - bool mInFrame; - bool mIsAnimated; + bool mInitialized : 1; + bool mMetadataDecode : 1; + bool mSendPartialInvalidations : 1; + bool mImageIsTransient : 1; + bool mImageIsLocked : 1; + bool mInFrame : 1; + bool mIsAnimated : 1; + bool mDataDone : 1; + bool mDecodeDone : 1; + bool mDataError : 1; + bool mDecodeAborted : 1; + bool mShouldReportError : 1; }; } // namespace image diff --git a/image/decoders/nsJPEGDecoder.cpp b/image/decoders/nsJPEGDecoder.cpp index e1556d02bb8a..57e7f0496bc2 100644 --- a/image/decoders/nsJPEGDecoder.cpp +++ b/image/decoders/nsJPEGDecoder.cpp @@ -248,7 +248,7 @@ nsJPEGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount) return; // I/O suspension } - int sampleSize = mImage->GetRequestedSampleSize(); + int sampleSize = GetImage()->GetRequestedSampleSize(); if (sampleSize > 0) { mInfo.scale_num = 1; mInfo.scale_denom = sampleSize; diff --git a/image/decoders/nsPNGDecoder.cpp b/image/decoders/nsPNGDecoder.cpp index 0e0bd738884c..2c7b14ba0932 100644 --- a/image/decoders/nsPNGDecoder.cpp +++ b/image/decoders/nsPNGDecoder.cpp @@ -178,7 +178,7 @@ nsPNGDecoder::CreateFrame(png_uint_32 aXOffset, png_uint_32 aYOffset, ("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created " "image frame with %dx%d pixels in container %p", aWidth, aHeight, - &mImage)); + GetImage())); #ifdef PNG_APNG_SUPPORTED if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) { From 947de7376b1b45dda2d383f34ee7e440807598ee Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 07:29:03 -0700 Subject: [PATCH 006/113] Bug 1187386 (Part 2) - Rework decoder code to avoid calling Decode::GetImage(). r=tn --HG-- extra : rebase_source : 6fc835a3468e846cb59474efcfea5355914a9dc4 --- image/Decoder.h | 16 +++++++++++ image/DecoderFactory.cpp | 10 ++++++- image/DecoderFactory.h | 14 +++++++++- image/ImageFactory.cpp | 46 ++++++++++++++++---------------- image/RasterImage.cpp | 8 +++--- image/RasterImage.h | 7 ----- image/decoders/nsICODecoder.cpp | 11 ++++---- image/decoders/nsICODecoder.h | 6 +++++ image/decoders/nsJPEGDecoder.cpp | 7 ++--- image/decoders/nsJPEGDecoder.h | 7 +++++ image/decoders/nsPNGDecoder.cpp | 5 ++-- 11 files changed, 91 insertions(+), 46 deletions(-) diff --git a/image/Decoder.h b/image/Decoder.h index 9458f111cc16..a30a0d2b1783 100644 --- a/image/Decoder.h +++ b/image/Decoder.h @@ -127,6 +127,22 @@ public: return NS_ERROR_NOT_AVAILABLE; } + /** + * Set the requested sample size for this decoder. Used to implement the + * -moz-sample-size media fragment. + * + * XXX(seth): Support for -moz-sample-size will be removed in bug 1120056. + */ + virtual void SetSampleSize(int aSampleSize) { } + + /** + * Set the requested resolution for this decoder. Used to implement the + * -moz-resolution media fragment. + * + * XXX(seth): Support for -moz-resolution will be removed in bug 1118926. + */ + virtual void SetResolution(const gfx::IntSize& aResolution) { } + /** * Set whether should send partial invalidations. * diff --git a/image/DecoderFactory.cpp b/image/DecoderFactory.cpp index de12c3d59f5a..6be68b2195d6 100644 --- a/image/DecoderFactory.cpp +++ b/image/DecoderFactory.cpp @@ -110,6 +110,8 @@ DecoderFactory::CreateDecoder(DecoderType aType, SourceBuffer* aSourceBuffer, const Maybe& aTargetSize, uint32_t aFlags, + int aSampleSize, + const IntSize& aResolution, bool aIsRedecode, bool aImageIsTransient, bool aImageIsLocked) @@ -125,6 +127,8 @@ DecoderFactory::CreateDecoder(DecoderType aType, decoder->SetMetadataDecode(false); decoder->SetIterator(aSourceBuffer->Iterator()); decoder->SetFlags(aFlags); + decoder->SetSampleSize(aSampleSize); + decoder->SetResolution(aResolution); decoder->SetSendPartialInvalidations(!aIsRedecode); decoder->SetImageIsTransient(aImageIsTransient); @@ -151,7 +155,9 @@ DecoderFactory::CreateDecoder(DecoderType aType, /* static */ already_AddRefed DecoderFactory::CreateMetadataDecoder(DecoderType aType, RasterImage* aImage, - SourceBuffer* aSourceBuffer) + SourceBuffer* aSourceBuffer, + int aSampleSize, + const IntSize& aResolution) { if (aType == DecoderType::UNKNOWN) { return nullptr; @@ -164,6 +170,8 @@ DecoderFactory::CreateMetadataDecoder(DecoderType aType, // Initialize the decoder. decoder->SetMetadataDecode(true); decoder->SetIterator(aSourceBuffer->Iterator()); + decoder->SetSampleSize(aSampleSize); + decoder->SetResolution(aResolution); decoder->Init(); if (NS_FAILED(decoder->GetDecoderError())) { diff --git a/image/DecoderFactory.h b/image/DecoderFactory.h index 98cd7e2032f9..33ef4228a64a 100644 --- a/image/DecoderFactory.h +++ b/image/DecoderFactory.h @@ -56,6 +56,10 @@ public: * downscale-during-decode. * @param aFlags Flags specifying what type of output the decoder should * produce; see GetDecodeFlags() in RasterImage.h. + * @param aSampleSize The sample size requested using #-moz-samplesize (or 0 + * if none). + * @param aResolution The resolution requested using #-moz-resolution (or an + * empty rect if none). * @param aIsRedecode Specify 'true' if this image has been decoded before. * @param aImageIsTransient Specify 'true' if this image is transient. * @param aImageIsLocked Specify 'true' if this image is locked for the @@ -68,6 +72,8 @@ public: SourceBuffer* aSourceBuffer, const Maybe& aTargetSize, uint32_t aFlags, + int aSampleSize, + const gfx::IntSize& aResolution, bool aIsRedecode, bool aImageIsTransient, bool aImageIsLocked); @@ -83,11 +89,17 @@ public: * notifications as decoding progresses. * @param aSourceBuffer The SourceBuffer which the decoder will read its data * from. + * @param aSampleSize The sample size requested using #-moz-samplesize (or 0 + * if none). + * @param aResolution The resolution requested using #-moz-resolution (or an + * empty rect if none). */ static already_AddRefed CreateMetadataDecoder(DecoderType aType, RasterImage* aImage, - SourceBuffer* aSourceBuffer); + SourceBuffer* aSourceBuffer, + int aSampleSize, + const gfx::IntSize& aResolution); private: virtual ~DecoderFactory() = 0; diff --git a/image/ImageFactory.cpp b/image/ImageFactory.cpp index f5cacffb29ba..3f534cb0efe8 100644 --- a/image/ImageFactory.cpp +++ b/image/ImageFactory.cpp @@ -221,6 +221,29 @@ ImageFactory::CreateRasterImage(nsIRequest* aRequest, aProgressTracker->SetImage(newImage); newImage->SetProgressTracker(aProgressTracker); + nsAutoCString ref; + aURI->GetRef(ref); + net::nsMediaFragmentURIParser parser(ref); + if (parser.HasResolution()) { + newImage->SetRequestedResolution(parser.GetResolution()); + } + + if (parser.HasSampleSize()) { + /* Get our principal */ + nsCOMPtr chan(do_QueryInterface(aRequest)); + nsCOMPtr principal; + if (chan) { + nsContentUtils::GetSecurityManager() + ->GetChannelResultPrincipal(chan, getter_AddRefs(principal)); + } + + if ((principal && + principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED) || + gfxPrefs::ImageMozSampleSizeEnabled()) { + newImage->SetRequestedSampleSize(parser.GetSampleSize()); + } + } + rv = newImage->Init(aMimeType.get(), aImageFlags); NS_ENSURE_SUCCESS(rv, BadImage(newImage)); @@ -245,29 +268,6 @@ ImageFactory::CreateRasterImage(nsIRequest* aRequest, } } - nsAutoCString ref; - aURI->GetRef(ref); - net::nsMediaFragmentURIParser parser(ref); - if (parser.HasResolution()) { - newImage->SetRequestedResolution(parser.GetResolution()); - } - - if (parser.HasSampleSize()) { - /* Get our principal */ - nsCOMPtr chan(do_QueryInterface(aRequest)); - nsCOMPtr principal; - if (chan) { - nsContentUtils::GetSecurityManager() - ->GetChannelResultPrincipal(chan, getter_AddRefs(principal)); - } - - if ((principal && - principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED) || - gfxPrefs::ImageMozSampleSizeEnabled()) { - newImage->SetRequestedSampleSize(parser.GetSampleSize()); - } - } - return newImage.forget(); } diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index 703d2c30c306..ef47635dc010 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -1445,8 +1445,8 @@ RasterImage::Decode(const IntSize& aSize, uint32_t aFlags) // Create a decoder. nsRefPtr decoder = DecoderFactory::CreateDecoder(mDecoderType, this, mSourceBuffer, targetSize, - aFlags, mHasBeenDecoded, mTransient, - imageIsLocked); + aFlags, mRequestedSampleSize, mRequestedResolution, + mHasBeenDecoded, mTransient, imageIsLocked); // Make sure DecoderFactory was able to create a decoder successfully. if (!decoder) { @@ -1499,7 +1499,9 @@ RasterImage::DecodeMetadata(uint32_t aFlags) // Create a decoder. nsRefPtr decoder = - DecoderFactory::CreateMetadataDecoder(mDecoderType, this, mSourceBuffer); + DecoderFactory::CreateMetadataDecoder(mDecoderType, this, mSourceBuffer, + mRequestedSampleSize, + mRequestedResolution); // Make sure DecoderFactory was able to create a decoder successfully. if (!decoder) { diff --git a/image/RasterImage.h b/image/RasterImage.h index 78143a4cf5a6..99afcc0b03da 100644 --- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -269,18 +269,11 @@ public: mRequestedResolution = requestedResolution; } - nsIntSize GetRequestedResolution() { - return mRequestedResolution; - } /* Provide a hint for the requested dimension of the resulting image. */ void SetRequestedSampleSize(int requestedSampleSize) { mRequestedSampleSize = requestedSampleSize; } - int GetRequestedSampleSize() { - return mRequestedSampleSize; - } - nsCString GetURIString() { nsCString spec; if (GetURI()) { diff --git a/image/decoders/nsICODecoder.cpp b/image/decoders/nsICODecoder.cpp index f9e27070ce32..6c87e64a5efa 100644 --- a/image/decoders/nsICODecoder.cpp +++ b/image/decoders/nsICODecoder.cpp @@ -266,9 +266,10 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount) } uint16_t colorDepth = 0; - nsIntSize prefSize = mImage->GetRequestedResolution(); - if (prefSize.width == 0 && prefSize.height == 0) { - prefSize.SizeTo(PREFICONSIZE, PREFICONSIZE); + + // If we didn't get a #-moz-resolution, default to PREFICONSIZE. + if (mResolution.width == 0 && mResolution.height == 0) { + mResolution.SizeTo(PREFICONSIZE, PREFICONSIZE); } // A measure of the difference in size between the entry we've found @@ -306,8 +307,8 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount) // Calculate the delta between this image's size and the desired size, // so we can see if it is better than our current-best option. // In the case of several equally-good images, we use the last one. - int32_t delta = (e.mWidth == 0 ? 256 : e.mWidth) - prefSize.width + - (e.mHeight == 0 ? 256 : e.mHeight) - prefSize.height; + int32_t delta = (e.mWidth == 0 ? 256 : e.mWidth) - mResolution.width + + (e.mHeight == 0 ? 256 : e.mHeight) - mResolution.height; if (e.mBitCount >= colorDepth && ((diff < 0 && delta >= diff) || (delta >= 0 && delta <= diff))) { diff = delta; diff --git a/image/decoders/nsICODecoder.h b/image/decoders/nsICODecoder.h index 83f8c6353a82..a4da031b88bd 100644 --- a/image/decoders/nsICODecoder.h +++ b/image/decoders/nsICODecoder.h @@ -36,6 +36,11 @@ public: return mDirEntry.mHeight == 0 ? 256 : mDirEntry.mHeight; } + virtual void SetResolution(const gfx::IntSize& aResolution) override + { + mResolution = aResolution; + } + virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override; virtual void FinishInternal() override; virtual void FinishWithErrorInternal() override; @@ -76,6 +81,7 @@ private: // Obtains the number of colors from the BPP, mBPP must be filled in uint16_t GetNumColors(); + gfx::IntSize mResolution; // The requested -moz-resolution for this icon. uint16_t mBPP; // Stores the images BPP uint32_t mPos; // Keeps track of the position we have decoded up until uint16_t mNumIcons; // Stores the number of icons in the ICO file diff --git a/image/decoders/nsJPEGDecoder.cpp b/image/decoders/nsJPEGDecoder.cpp index 57e7f0496bc2..ed1eb9aa8363 100644 --- a/image/decoders/nsJPEGDecoder.cpp +++ b/image/decoders/nsJPEGDecoder.cpp @@ -84,6 +84,7 @@ nsJPEGDecoder::nsJPEGDecoder(RasterImage* aImage, Decoder::DecodeStyle aDecodeStyle) : Decoder(aImage) , mDecodeStyle(aDecodeStyle) + , mSampleSize(0) { mState = JPEG_HEADER; mReading = true; @@ -248,10 +249,10 @@ nsJPEGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount) return; // I/O suspension } - int sampleSize = GetImage()->GetRequestedSampleSize(); - if (sampleSize > 0) { + // If we have a sample size specified for -moz-sample-size, use it. + if (mSampleSize > 0) { mInfo.scale_num = 1; - mInfo.scale_denom = sampleSize; + mInfo.scale_denom = mSampleSize; } // Used to set up image size so arrays can be allocated diff --git a/image/decoders/nsJPEGDecoder.h b/image/decoders/nsJPEGDecoder.h index 6647aeb6e815..d476b43e323a 100644 --- a/image/decoders/nsJPEGDecoder.h +++ b/image/decoders/nsJPEGDecoder.h @@ -57,6 +57,11 @@ public: virtual nsresult SetTargetSize(const nsIntSize& aSize) override; + virtual void SetSampleSize(int aSampleSize) override + { + mSampleSize = aSampleSize; + } + virtual void InitInternal() override; virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override; virtual void FinishInternal() override; @@ -103,6 +108,8 @@ public: const Decoder::DecodeStyle mDecodeStyle; uint32_t mCMSMode; + + int mSampleSize; }; } // namespace image diff --git a/image/decoders/nsPNGDecoder.cpp b/image/decoders/nsPNGDecoder.cpp index 2c7b14ba0932..c6da55488624 100644 --- a/image/decoders/nsPNGDecoder.cpp +++ b/image/decoders/nsPNGDecoder.cpp @@ -176,9 +176,8 @@ nsPNGDecoder::CreateFrame(png_uint_32 aXOffset, png_uint_32 aYOffset, MOZ_LOG(GetPNGDecoderAccountingLog(), LogLevel::Debug, ("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created " - "image frame with %dx%d pixels in container %p", - aWidth, aHeight, - GetImage())); + "image frame with %dx%d pixels for decoder %p", + aWidth, aHeight, this)); #ifdef PNG_APNG_SUPPORTED if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) { From a87de31e6aad85ba1659e4fad225764d02f51dbd Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 07:29:07 -0700 Subject: [PATCH 007/113] Bug 1187386 (Part 3) - Don't destroy Decoder::mImage if Decoder::mImage is null. r=tn --HG-- extra : rebase_source : dc22bcc48ece67dd8af9a65aacd74b2f1a56f9a6 --- image/Decoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image/Decoder.cpp b/image/Decoder.cpp index 6f883bf4507d..627c558f7617 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -57,7 +57,7 @@ Decoder::~Decoder() "Destroying Decoder without taking all its invalidations"); mInitialized = false; - if (!NS_IsMainThread()) { + if (mImage && !NS_IsMainThread()) { // Dispatch mImage to main thread to prevent it from being destructed by the // decode thread. nsCOMPtr mainThread = do_GetMainThread(); From 3fdb290471e16f6b0f9015562628e849d40dfcc0 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 07:29:10 -0700 Subject: [PATCH 008/113] Bug 1187386 (Part 4) - Make imgFrame::SetOptimizable() callable from off-main-thread. r=tn --HG-- extra : rebase_source : 726f0b66275f80acd655720a2aa684fb18d9016f --- image/Decoder.cpp | 20 +++++++++++--------- image/imgFrame.cpp | 4 ++-- image/imgFrame.h | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/image/Decoder.cpp b/image/Decoder.cpp index 627c558f7617..93f0895befc2 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -226,6 +226,17 @@ Decoder::CompleteDecode() mProgress |= FLAG_HAS_ERROR; } } + + if (mDecodeDone && !IsMetadataDecode()) { + MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame"); + + // If this image wasn't animated and isn't a transient image, mark its frame + // as optimizable. We don't support optimizing animated images and + // optimizing transient images isn't worth it. + if (!mIsAnimated && !mImageIsTransient && mCurrentFrame) { + mCurrentFrame->SetOptimizable(); + } + } } void @@ -272,15 +283,6 @@ Decoder::Finish() } if (mDecodeDone && !IsMetadataDecode()) { - MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame"); - - // If this image wasn't animated and isn't a transient image, mark its frame - // as optimizable. We don't support optimizing animated images and - // optimizing transient images isn't worth it. - if (!mIsAnimated && !mImageIsTransient && mCurrentFrame) { - mCurrentFrame->SetOptimizable(); - } - mImage->OnDecodingComplete(mIsAnimated); } } diff --git a/image/imgFrame.cpp b/image/imgFrame.cpp index ab062eb75133..f48794d1d76c 100644 --- a/image/imgFrame.cpp +++ b/image/imgFrame.cpp @@ -140,12 +140,12 @@ imgFrame::imgFrame() , mBlendMethod(BlendMethod::OVER) , mHasNoAlpha(false) , mAborted(false) + , mOptimizable(false) , mPalettedImageData(nullptr) , mPaletteDepth(0) , mNonPremult(false) , mSinglePixel(false) , mCompositingFailed(false) - , mOptimizable(false) { static bool hasCheckedOptimize = false; if (!hasCheckedOptimize) { @@ -952,8 +952,8 @@ imgFrame::UnlockImageData() void imgFrame::SetOptimizable() { - MOZ_ASSERT(NS_IsMainThread()); AssertImageDataLocked(); + MonitorAutoLock lock(mMonitor); mOptimizable = true; } diff --git a/image/imgFrame.h b/image/imgFrame.h index 4173db66b95e..89724c46f4ff 100644 --- a/image/imgFrame.h +++ b/image/imgFrame.h @@ -342,6 +342,7 @@ private: // data bool mHasNoAlpha; bool mAborted; + bool mOptimizable; ////////////////////////////////////////////////////////////////////////////// @@ -371,7 +372,6 @@ private: // data bool mSinglePixel; bool mCompositingFailed; - bool mOptimizable; }; /** From 973fa019ef10e45528def99e278d2d37750b2b73 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 07:29:12 -0700 Subject: [PATCH 009/113] Bug 1187386 (Part 5) - Merge Decoder::SetSizeOnImage() into ImageMetadata::SetOnImage(). r=tn --HG-- extra : rebase_source : 5da2023b266e44970fd0fff7442ad8eea4939379 --- image/Decoder.cpp | 23 +++-------------------- image/Decoder.h | 1 - image/ImageMetadata.cpp | 19 ++++++++++++++----- image/ImageMetadata.h | 2 +- 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/image/Decoder.cpp b/image/Decoder.cpp index 93f0895befc2..bcd05ecd0c27 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -274,12 +274,9 @@ Decoder::Finish() } } - // Set image metadata before calling DecodingComplete, because - // DecodingComplete calls Optimize(). - mImageMetadata.SetOnImage(mImage); - - if (HasSize()) { - SetSizeOnImage(); + nsresult rv = mImageMetadata.SetOnImage(mImage); + if (NS_FAILED(rv)) { + PostResizeError(); } if (mDecodeDone && !IsMetadataDecode()) { @@ -409,20 +406,6 @@ Decoder::AllocateFrameInternal(uint32_t aFrameNum, return ref; } -void -Decoder::SetSizeOnImage() -{ - MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size"); - MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation"); - - nsresult rv = mImage->SetSize(mImageMetadata.GetWidth(), - mImageMetadata.GetHeight(), - mImageMetadata.GetOrientation()); - if (NS_FAILED(rv)) { - PostResizeError(); - } -} - /* * Hook stubs. Override these as necessary in decoder implementations. */ diff --git a/image/Decoder.h b/image/Decoder.h index a30a0d2b1783..44066c8dca99 100644 --- a/image/Decoder.h +++ b/image/Decoder.h @@ -253,7 +253,6 @@ public: uint32_t GetDecodeFlags() const { return DecodeFlags(mFlags); } bool HasSize() const { return mImageMetadata.HasSize(); } - void SetSizeOnImage(); nsIntSize GetSize() const { diff --git a/image/ImageMetadata.cpp b/image/ImageMetadata.cpp index e30e208b223f..236228520a3e 100644 --- a/image/ImageMetadata.cpp +++ b/image/ImageMetadata.cpp @@ -14,9 +14,11 @@ namespace mozilla { namespace image { -void -ImageMetadata::SetOnImage(RasterImage* image) +nsresult +ImageMetadata::SetOnImage(RasterImage* aImage) { + nsresult rv = NS_OK; + if (mHotspotX != -1 && mHotspotY != -1) { nsCOMPtr intwrapx = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID); @@ -24,11 +26,18 @@ ImageMetadata::SetOnImage(RasterImage* image) do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID); intwrapx->SetData(mHotspotX); intwrapy->SetData(mHotspotY); - image->Set("hotspotX", intwrapx); - image->Set("hotspotY", intwrapy); + aImage->Set("hotspotX", intwrapx); + aImage->Set("hotspotY", intwrapy); } - image->SetLoopCount(mLoopCount); + aImage->SetLoopCount(mLoopCount); + + if (HasSize()) { + MOZ_ASSERT(HasOrientation(), "Should have orientation"); + rv = aImage->SetSize(GetWidth(), GetHeight(), GetOrientation()); + } + + return rv; } } // namespace image diff --git a/image/ImageMetadata.h b/image/ImageMetadata.h index a8d374bb828e..c257612e0213 100644 --- a/image/ImageMetadata.h +++ b/image/ImageMetadata.h @@ -28,7 +28,7 @@ public: { } // Set the metadata this object represents on an image. - void SetOnImage(RasterImage* image); + nsresult SetOnImage(RasterImage* aImage); void SetHotspot(uint16_t hotspotx, uint16_t hotspoty) { From ffee281e15da6807afebf5150b642ff320d4459d Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 07:29:15 -0700 Subject: [PATCH 010/113] Bug 1187386 (Part 6) - Merge Decoder::Finish() and RasterImage::OnDecodingComplete() into RasterImage::FinalizeDecoder(). r=tn --HG-- extra : rebase_source : e2d1bce9222366f3dff2bf8b1ccb755ba44140ae --- image/DecodePool.cpp | 2 - image/Decoder.cpp | 47 ------------------ image/Decoder.h | 20 +++++--- image/RasterImage.cpp | 109 ++++++++++++++++++++++++++++-------------- image/RasterImage.h | 10 ++-- 5 files changed, 90 insertions(+), 98 deletions(-) diff --git a/image/DecodePool.cpp b/image/DecodePool.cpp index c7bd546ea0e2..09ed7e965f05 100644 --- a/image/DecodePool.cpp +++ b/image/DecodePool.cpp @@ -96,7 +96,6 @@ public: NS_IMETHOD Run() override { MOZ_ASSERT(NS_IsMainThread()); - mDecoder->Finish(); mDecoder->GetImage()->FinalizeDecoder(mDecoder); return NS_OK; } @@ -492,7 +491,6 @@ DecodePool::NotifyDecodeComplete(Decoder* aDecoder) return; } - aDecoder->Finish(); aDecoder->GetImage()->FinalizeDecoder(aDecoder); } diff --git a/image/Decoder.cpp b/image/Decoder.cpp index bcd05ecd0c27..80cb0b9061ae 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -10,8 +10,6 @@ #include "DecodePool.h" #include "GeckoProfiler.h" #include "imgIContainer.h" -#include "nsIConsoleService.h" -#include "nsIScriptError.h" #include "nsProxyRelease.h" #include "nsServiceManagerUtils.h" #include "nsComponentManagerUtils.h" @@ -239,51 +237,6 @@ Decoder::CompleteDecode() } } -void -Decoder::Finish() -{ - MOZ_ASSERT(NS_IsMainThread()); - - MOZ_ASSERT(HasError() || !mInFrame, "Finishing while we're still in a frame"); - - // If we detected an error in CompleteDecode(), log it to the error console. - if (mShouldReportError && !WasAborted()) { - nsCOMPtr consoleService = - do_GetService(NS_CONSOLESERVICE_CONTRACTID); - nsCOMPtr errorObject = - do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); - - if (consoleService && errorObject && !HasDecoderError()) { - nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated.")); - nsAutoString src; - if (mImage->GetURI()) { - nsCString uri; - if (mImage->GetURI()->GetSpecTruncatedTo1k(uri) == ImageURL::TruncatedTo1k) { - msg += NS_LITERAL_STRING(" URI in this note truncated due to length."); - } - src = NS_ConvertUTF8toUTF16(uri); - } - if (NS_SUCCEEDED(errorObject->InitWithWindowID( - msg, - src, - EmptyString(), 0, 0, nsIScriptError::errorFlag, - "Image", mImage->InnerWindowID() - ))) { - consoleService->LogMessage(errorObject); - } - } - } - - nsresult rv = mImageMetadata.SetOnImage(mImage); - if (NS_FAILED(rv)) { - PostResizeError(); - } - - if (mDecodeDone && !IsMetadataDecode()) { - mImage->OnDecodingComplete(mIsAnimated); - } -} - nsresult Decoder::AllocateFrame(uint32_t aFrameNum, const nsIntSize& aTargetSize, diff --git a/image/Decoder.h b/image/Decoder.h index 44066c8dca99..5342058395ca 100644 --- a/image/Decoder.h +++ b/image/Decoder.h @@ -42,12 +42,6 @@ public: */ nsresult Decode(); - /** - * Cleans up the decoder's state and notifies our image about success or - * failure. May only be called on the main thread. - */ - void Finish(); - /** * Given a maximum number of bytes we're willing to decode, @aByteLimit, * returns true if we should attempt to run this decoder synchronously. @@ -107,7 +101,7 @@ public: MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet"); mMetadataDecode = aMetadataDecode; } - bool IsMetadataDecode() { return mMetadataDecode; } + bool IsMetadataDecode() const { return mMetadataDecode; } /** * If this decoder supports downscale-during-decode, sets the target size that @@ -218,19 +212,31 @@ public: return mInFrame ? mFrameCount - 1 : mFrameCount; } + // Did we discover that the image we're decoding is animated? + bool HasAnimation() const { return mIsAnimated; } + // Error tracking bool HasError() const { return HasDataError() || HasDecoderError(); } bool HasDataError() const { return mDataError; } bool HasDecoderError() const { return NS_FAILED(mFailCode); } + bool ShouldReportError() const { return mShouldReportError; } nsresult GetDecoderError() const { return mFailCode; } void PostResizeError() { PostDataError(); } + /// Did we finish decoding enough that calling Decode() again would be useless? bool GetDecodeDone() const { return mDecodeDone || (mMetadataDecode && HasSize()) || HasError() || mDataDone; } + /// Did we finish decoding enough to set |RasterImage::mHasBeenDecoded|? + // XXX(seth): This will be removed in bug 1187401. + bool GetDecodeTotallyDone() const { return mDecodeDone && !IsMetadataDecode(); } + + /// Are we in the middle of a frame right now? Used for assertions only. + bool InFrame() const { return mInFrame; } + /** * Returns true if this decoder was aborted. * diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index ef47635dc010..7efd2f2e61c1 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -21,7 +21,9 @@ #include "ImageRegion.h" #include "Layers.h" #include "LookupResult.h" +#include "nsIConsoleService.h" #include "nsIInputStream.h" +#include "nsIScriptError.h" #include "nsPresContext.h" #include "SourceBuffer.h" #include "SurfaceCache.h" @@ -979,42 +981,6 @@ RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation) return NS_OK; } -void -RasterImage::OnDecodingComplete(bool aIsAnimated) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mError) { - return; - } - - // Flag that we've been decoded before. - mHasBeenDecoded = true; - - if (aIsAnimated) { - if (mAnim) { - mAnim->SetDoneDecoding(true); - } else { - // The OnAddedFrame event that will create mAnim is still in the event - // queue. Wait for it. - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &RasterImage::MarkAnimationDecoded); - NS_DispatchToMainThread(runnable); - } - } -} - -void -RasterImage::MarkAnimationDecoded() -{ - MOZ_ASSERT(mAnim, "Should have an animation now"); - if (!mAnim) { - return; - } - - mAnim->SetDoneDecoding(true); -} - NS_IMETHODIMP RasterImage::SetAnimationMode(uint16_t aAnimationMode) { @@ -2012,9 +1978,40 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aDecoder); + MOZ_ASSERT(aDecoder->HasError() || !aDecoder->InFrame(), + "Finalizing a decoder in the middle of a frame"); + + // If the decoder detected an error, log it to the error console. + if (aDecoder->ShouldReportError() && !aDecoder->WasAborted()) { + ReportDecoderError(aDecoder); + } + + // Record all the metadata the decoder gathered about this image. + nsresult rv = aDecoder->GetImageMetadata().SetOnImage(this); + if (NS_FAILED(rv)) { + aDecoder->PostResizeError(); + } + MOZ_ASSERT(mError || mHasSize || !aDecoder->HasSize(), "Should have handed off size by now"); + if (aDecoder->GetDecodeTotallyDone() && !mError) { + // Flag that we've been decoded before. + mHasBeenDecoded = true; + + if (aDecoder->HasAnimation()) { + if (mAnim) { + mAnim->SetDoneDecoding(true); + } else { + // The OnAddedFrame event that will create mAnim is still in the event + // queue. Wait for it. + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &RasterImage::MarkAnimationDecoded); + NS_DispatchToMainThread(runnable); + } + } + } + // Send out any final notifications. NotifyProgress(aDecoder->TakeProgress(), aDecoder->TakeInvalidRect(), @@ -2071,6 +2068,46 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder) } } +void +RasterImage::MarkAnimationDecoded() +{ + MOZ_ASSERT(mAnim, "Should have an animation now"); + if (!mAnim) { + return; + } + + mAnim->SetDoneDecoding(true); +} + +void +RasterImage::ReportDecoderError(Decoder* aDecoder) +{ + nsCOMPtr consoleService = + do_GetService(NS_CONSOLESERVICE_CONTRACTID); + nsCOMPtr errorObject = + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); + + if (consoleService && errorObject && !aDecoder->HasDecoderError()) { + nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated.")); + nsAutoString src; + if (GetURI()) { + nsCString uri; + if (GetURI()->GetSpecTruncatedTo1k(uri) == ImageURL::TruncatedTo1k) { + msg += NS_LITERAL_STRING(" URI in this note truncated due to length."); + } + src = NS_ConvertUTF8toUTF16(uri); + } + if (NS_SUCCEEDED(errorObject->InitWithWindowID( + msg, + src, + EmptyString(), 0, 0, nsIScriptError::errorFlag, + "Image", InnerWindowID() + ))) { + consoleService->LogMessage(errorObject); + } + } +} + already_AddRefed RasterImage::Unwrap() { diff --git a/image/RasterImage.h b/image/RasterImage.h index 99afcc0b03da..240314e29520 100644 --- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -206,12 +206,6 @@ public: */ void SetLoopCount(int32_t aLoopCount); - /// Notification that the entire image has been decoded. - void OnDecodingComplete(bool aIsAnimated); - - /// Helper method for OnDecodingComplete. - void MarkAnimationDecoded(); - /** * Sends the provided progress notifications to ProgressTracker. * @@ -234,6 +228,10 @@ public: */ void FinalizeDecoder(Decoder* aDecoder); + // Helper methods for FinalizeDecoder. + void MarkAnimationDecoded(); + void ReportDecoderError(Decoder* aDecoder); + ////////////////////////////////////////////////////////////////////////////// // Network callbacks. From 747226a550b316c5f4592a1ccfb5aef04d98c476 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 07:29:18 -0700 Subject: [PATCH 011/113] Bug 1187386 (Part 7) - Eliminate remaining dependencies on a non-null mImage in Decoder. r=tn --HG-- extra : rebase_source : b987299e018353af3bd322f8a857d2f20864b711 --- image/Decoder.cpp | 50 ++++++++++++++++++++++++++--------------------- image/Decoder.h | 3 +++ 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/image/Decoder.cpp b/image/Decoder.cpp index 80cb0b9061ae..3344a52819b5 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -49,9 +49,9 @@ Decoder::Decoder(RasterImage* aImage) Decoder::~Decoder() { - MOZ_ASSERT(mProgress == NoProgress, + MOZ_ASSERT(mProgress == NoProgress || !mImage, "Destroying Decoder without taking all its progress changes"); - MOZ_ASSERT(mInvalidRect.IsEmpty(), + MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage, "Destroying Decoder without taking all its invalidations"); mInitialized = false; @@ -288,7 +288,8 @@ Decoder::AllocateFrameInternal(uint32_t aFrameNum, } const uint32_t bytesPerPixel = aPaletteDepth == 0 ? 4 : 1; - if (!SurfaceCache::CanHold(aFrameRect.Size(), bytesPerPixel)) { + if (ShouldUseSurfaceCache() && + !SurfaceCache::CanHold(aFrameRect.Size(), bytesPerPixel)) { NS_WARNING("Trying to add frame that's too large for the SurfaceCache"); return RawAccessFrameRef(); } @@ -308,24 +309,26 @@ Decoder::AllocateFrameInternal(uint32_t aFrameNum, return RawAccessFrameRef(); } - InsertOutcome outcome = - SurfaceCache::Insert(frame, ImageKey(mImage.get()), - RasterSurfaceKey(aTargetSize, - aDecodeFlags, - aFrameNum), - Lifetime::Persistent); - if (outcome == InsertOutcome::FAILURE) { - // We couldn't insert the surface, almost certainly due to low memory. We - // treat this as a permanent error to help the system recover; otherwise, we - // might just end up attempting to decode this image again immediately. - ref->Abort(); - return RawAccessFrameRef(); - } else if (outcome == InsertOutcome::FAILURE_ALREADY_PRESENT) { - // Another decoder beat us to decoding this frame. We abort this decoder - // rather than treat this as a real error. - mDecodeAborted = true; - ref->Abort(); - return RawAccessFrameRef(); + if (ShouldUseSurfaceCache()) { + InsertOutcome outcome = + SurfaceCache::Insert(frame, ImageKey(mImage.get()), + RasterSurfaceKey(aTargetSize, + aDecodeFlags, + aFrameNum), + Lifetime::Persistent); + if (outcome == InsertOutcome::FAILURE) { + // We couldn't insert the surface, almost certainly due to low memory. We + // treat this as a permanent error to help the system recover; otherwise, + // we might just end up attempting to decode this image again immediately. + ref->Abort(); + return RawAccessFrameRef(); + } else if (outcome == InsertOutcome::FAILURE_ALREADY_PRESENT) { + // Another decoder beat us to decoding this frame. We abort this decoder + // rather than treat this as a real error. + mDecodeAborted = true; + ref->Abort(); + return RawAccessFrameRef(); + } } nsIntRect refreshArea; @@ -354,7 +357,10 @@ Decoder::AllocateFrameInternal(uint32_t aFrameNum, } mFrameCount++; - mImage->OnAddedFrame(mFrameCount, refreshArea); + + if (mImage) { + mImage->OnAddedFrame(mFrameCount, refreshArea); + } return ref; } diff --git a/image/Decoder.h b/image/Decoder.h index 5342058395ca..49fe02553b09 100644 --- a/image/Decoder.h +++ b/image/Decoder.h @@ -237,6 +237,9 @@ public: /// Are we in the middle of a frame right now? Used for assertions only. bool InFrame() const { return mInFrame; } + /// Should we store surfaces created by this decoder in the SurfaceCache? + bool ShouldUseSurfaceCache() const { return bool(mImage); } + /** * Returns true if this decoder was aborted. * From e67031e9bd60b39e6146fd571b6b848135199ea2 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Fri, 31 Jul 2015 13:15:34 -0700 Subject: [PATCH 012/113] Bug 1183677 Import fetch-access-control.py into service worker wpt tests. r=jgraham --- .../resources/fetch-access-control.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-access-control.py diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-access-control.py b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-access-control.py new file mode 100644 index 000000000000..862718ad03f7 --- /dev/null +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-access-control.py @@ -0,0 +1,69 @@ +import base64 +import json + +def main(request, response): + headers = [] + headers.append(('X-ServiceWorker-ServerHeader', 'SetInTheServer')) + + if "ACAOrigin" in request.GET: + for item in request.GET["ACAOrigin"].split(","): + headers.append(("Access-Control-Allow-Origin", item)) + + for suffix in ["Headers", "Methods", "Credentials"]: + query = "ACA%s" % suffix + header = "Access-Control-Allow-%s" % suffix + if query in request.GET: + headers.append((header, request.GET[query])) + + if "ACEHeaders" in request.GET: + headers.append(("Access-Control-Expose-Headers", request.GET[query])) + + if ("Auth" in request.GET and not request.auth.username) or "AuthFail" in request.GET: + status = 401 + headers.append(('WWW-Authenticate', 'Basic realm="Restricted"')) + body = 'Authentication canceled' + return status, headers, body + + if "PNGIMAGE" in request.GET: + headers.append(("Content-Type", "image/png")) + body = base64.decodestring("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1B" + "AACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAhSURBVDhPY3wro/KfgQLABKXJBqMG" + "jBoAAqMGDLwBDAwAEsoCTFWunmQAAAAASUVORK5CYII=") + return headers, body + + + username = request.auth.username if request.auth.username else "undefined" + password = request.auth.password if request.auth.username else "undefined" + cookie = request.cookies['cookie'].value if 'cookie' in request.cookies else "undefined" + + files = [] + for key, values in request.POST.iteritems(): + assert len(values) == 1 + value = values[0] + if not hasattr(value, "file"): + continue + data = value.file.read() + files.append({"key": key, + "name": value.file.name, + "type": value.type, + "error": 0, #TODO, + "size": len(data), + "content": data}) + + get_data = {key:request.GET[key] for key,value in request.GET.iteritems()} + post_data = {key:request.POST[key] for key,value in request.POST.iteritems() + if not hasattr(request.POST[key], "file")} + headers_data = {key:request.headers[key] for key,value in request.headers.iteritems()} + + data = {"jsonpResult": "success", + "method": request.method, + "headers": headers_data, + "body": request.body, + "files": files, + "GET": get_data, + "POST": post_data, + "username": username, + "password": password, + "cookie": cookie} + + return headers, "report( %s )" % json.dumps(data) From dee06cb8d2010b36cdc0c23a48f7b112c1e0573b Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Fri, 31 Jul 2015 13:23:15 -0700 Subject: [PATCH 013/113] Bug 1183845 - Only send activation event if document is non-null. r=bz --- dom/base/nsFocusManager.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index caf6008593ca..550dffebe461 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -1134,12 +1134,14 @@ nsFocusManager::ActivateOrDeactivate(nsPIDOMWindow* aWindow, bool aActive) aWindow->ActivateOrDeactivate(aActive); // Send the activate event. - nsContentUtils::DispatchEventOnlyToChrome(aWindow->GetExtantDoc(), - aWindow, - aActive ? - NS_LITERAL_STRING("activate") : - NS_LITERAL_STRING("deactivate"), - true, true, nullptr); + if (aWindow->GetExtantDoc()) { + nsContentUtils::DispatchEventOnlyToChrome(aWindow->GetExtantDoc(), + aWindow, + aActive ? + NS_LITERAL_STRING("activate") : + NS_LITERAL_STRING("deactivate"), + true, true, nullptr); + } // Look for any remote child frames, iterate over them and send the activation notification. nsContentUtils::CallOnAllRemoteChildren(aWindow, ActivateOrDeactivateChild, From f0948c0e7a83a655bf691371c01739bc71330061 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 31 Jul 2015 13:27:44 -0700 Subject: [PATCH 014/113] Bug 1189490 - Part 0: Add a FIFO queue container type to js/public; r=terrence --- js/public/Fifo.h | 154 +++++++++++++++++++++++++++++++++++++++++++++++ js/src/moz.build | 1 + 2 files changed, 155 insertions(+) create mode 100644 js/public/Fifo.h diff --git a/js/public/Fifo.h b/js/public/Fifo.h new file mode 100644 index 000000000000..de482e7a240f --- /dev/null +++ b/js/public/Fifo.h @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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/. */ + +#ifndef js_Fifo_h +#define js_Fifo_h + +#include "mozilla/Move.h" + +#include "js/Vector.h" + +namespace js { + +// A first-in-first-out queue container type. Fifo calls constructors and +// destructors of all elements added so non-PODs may be used safely. |Fifo| +// stores the first |MinInlineCapacity| elements in-place before resorting to +// dynamic allocation. +// +// T requirements: +// - Either movable or copyable. +// MinInlineCapacity requirements: +// - Must be even. +// AllocPolicy: +// - see "Allocation policies" in AllocPolicy.h +template +class Fifo +{ + static_assert(MinInlineCapacity % 2 == 0, "MinInlineCapacity must be even!"); + + protected: + // An element A is "younger" than an element B if B was inserted into the + // |Fifo| before A was. + // + // Invariant 1: Every element within |front_| is younger than every element + // within |rear_|. + // Invariant 2: Entries within |front_| are sorted from younger to older. + // Invariant 3: Entries within |rear_| are sorted from older to younger. + // Invariant 4: If the |Fifo| is not empty, then |front_| is not empty. + Vector front_; + Vector rear_; + + private: + // Maintain invariants after adding or removing entries. + bool fixup() { + if (!front_.empty()) + return true; + + if (!front_.reserve(rear_.length())) + return false; + + while (!rear_.empty()) { + front_.infallibleAppend(mozilla::Move(rear_.back())); + rear_.popBack(); + } + + return true; + } + + public: + explicit Fifo(AllocPolicy alloc = AllocPolicy()) + : front_(alloc) + , rear_(alloc) + { } + + Fifo(Fifo&& rhs) + : front_(mozilla::Move(rhs.front_)) + , rear_(mozilla::Move(rhs.rear_)) + { } + + Fifo& operator=(Fifo&& rhs) { + MOZ_ASSERT(&rhs != this, "self-move disallowed"); + this->~Fifo(); + new (this) Fifo(mozilla::Move(rhs)); + return *this; + } + + Fifo(const Fifo&) = delete; + Fifo& operator=(const Fifo&) = delete; + + size_t length() const { + MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0); // Invariant 4. + return front_.length() + rear_.length(); + } + + bool empty() const { + MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0); // Invariant 4. + return front_.empty(); + } + + // Push an element to the back of the queue. This method can take either a + // |const T&| or a |T&&|. + template + bool pushBack(U&& u) { + if (!rear_.append(mozilla::Forward(u))) + return false; + if (!fixup()) { + rear_.popBack(); + return false; + } + return true; + } + + // Construct a T in-place at the back of the queue. + template + bool emplaceBack(Args&&... args) { + if (!rear_.emplaceBack(mozilla::Forward(args)...)) + return false; + if (!fixup()) { + rear_.popBack(); + return false; + } + return true; + } + + // Access the element at the front of the queue. + T& front() { + MOZ_ASSERT(!empty()); + return front_.back(); + } + const T& front() const { + MOZ_ASSERT(!empty()); + return front_.back(); + } + + // Remove the front element from the queue. + bool popFront() { + MOZ_ASSERT(!empty()); + T t(mozilla::Move(front())); + front_.popBack(); + if (!fixup()) { + // Attempt to remain in a valid state by reinserting the element + // back at the front. If we can't remain in a valid state in the + // face of OOMs, crash. + if (!front_.append(mozilla::Move(t))) + CrashAtUnhandlableOOM("js::Fifo::popFront"); + return false; + } + return true; + } + + // Clear all elements from the queue. + void clear() { + front_.clear(); + rear_.clear(); + } +}; + +} // namespace js + +#endif /* js_Fifo_h */ diff --git a/js/src/moz.build b/js/src/moz.build index ade842ea3387..5b285414c970 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -108,6 +108,7 @@ EXPORTS.js += [ '../public/Conversions.h', '../public/Date.h', '../public/Debug.h', + '../public/Fifo.h', '../public/GCAPI.h', '../public/HashTable.h', '../public/HeapAPI.h', From 9fab7634d709ae326fbd32fe88794bcaad3e09d6 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 31 Jul 2015 13:27:44 -0700 Subject: [PATCH 015/113] Bug 1189490 - Part 1: Add a StaticTraceable version of the FIFO queue for use with GC things; r=terrence --- js/public/TraceableFifo.h | 124 ++++++++++++++++++++++ js/src/jsapi-tests/testGCExactRooting.cpp | 40 +++++++ js/src/moz.build | 1 + 3 files changed, 165 insertions(+) create mode 100644 js/public/TraceableFifo.h diff --git a/js/public/TraceableFifo.h b/js/public/TraceableFifo.h new file mode 100644 index 000000000000..214dbd268fd5 --- /dev/null +++ b/js/public/TraceableFifo.h @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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/. */ + +#ifndef js_TraceableFifo_h +#define js_TraceableFifo_h + +#include "js/RootingAPI.h" +#include "js/Fifo.h" + +namespace js { + +template struct DefaultTracer; + +// A TraceableFifo is a Fifo with an additional trace method that knows how to +// visit all of the items stored in the Fifo. For Fifos that contain GC things, +// this is usually more convenient than manually iterating and marking the +// contents. +// +// Most types of GC pointers as keys and values can be traced with no extra +// infrastructure. For structs and non-gc-pointer members, ensure that there is +// a specialization of DefaultTracer with an appropriate trace method +// available to handle the custom type. +// +// Note that although this Fifo's trace will deal correctly with moved items, it +// does not itself know when to barrier or trace items. To function properly it +// must either be used with Rooted, or barriered and traced manually. +template > +class TraceableFifo + : public js::Fifo, + public JS::StaticTraceable +{ + using Base = js::Fifo; + + public: + explicit TraceableFifo(AllocPolicy alloc = AllocPolicy()) : Base(alloc) {} + + static void trace(TraceableFifo* tf, JSTracer* trc) { + for (size_t i = 0; i < tf->front_.length(); ++i) + TraceFunc::trace(trc, &tf->front_[i], "fifo element"); + for (size_t i = 0; i < tf->rear_.length(); ++i) + TraceFunc::trace(trc, &tf->rear_[i], "fifo element"); + } +}; + +template +class TraceableFifoOperations +{ + using TF = TraceableFifo; + const TF& fifo() const { return static_cast(this)->extract(); } + + public: + size_t length() const { return fifo().length(); } + bool empty() const { return fifo().empty(); } + const T& front() const { return fifo().front(); } +}; + +template +class MutableTraceableFifoOperations + : public TraceableFifoOperations +{ + using TF = TraceableFifo; + TF& fifo() { return static_cast(this)->extract(); } + + public: + T& front() { return fifo().front(); } + + template bool pushBack(U&& u) { return fifo().pushBack(mozilla::Forward(u)); } + template bool emplaceBack(Args&&... args) { + return fifo().emplaceBack(mozilla::Forward(args...)); + } + + bool popFront() { return fifo().popFront(); } + void clear() { fifo().clear(); } +}; + +template +class RootedBase> + : public MutableTraceableFifoOperations>, A,B,C,D> +{ + using TF = TraceableFifo; + + friend class TraceableFifoOperations, A,B,C,D>; + const TF& extract() const { return *static_cast*>(this)->address(); } + + friend class MutableTraceableFifoOperations, A,B,C,D>; + TF& extract() { return *static_cast*>(this)->address(); } +}; + +template +class MutableHandleBase> + : public MutableTraceableFifoOperations>, A,B,C,D> +{ + using TF = TraceableFifo; + + friend class TraceableFifoOperations, A,B,C,D>; + const TF& extract() const { + return *static_cast*>(this)->address(); + } + + friend class MutableTraceableFifoOperations, A,B,C,D>; + TF& extract() { return *static_cast*>(this)->address(); } +}; + +template +class HandleBase> + : public TraceableFifoOperations>, A,B,C,D> +{ + using TF = TraceableFifo; + + friend class TraceableFifoOperations, A,B,C,D>; + const TF& extract() const { + return *static_cast*>(this)->address(); + } +}; + +} // namespace js + +#endif // js_TraceableFifo_h diff --git a/js/src/jsapi-tests/testGCExactRooting.cpp b/js/src/jsapi-tests/testGCExactRooting.cpp index 3ac3b1b2b4d7..a4e4d07a63ec 100644 --- a/js/src/jsapi-tests/testGCExactRooting.cpp +++ b/js/src/jsapi-tests/testGCExactRooting.cpp @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "js/RootingAPI.h" +#include "js/TraceableFifo.h" #include "js/TraceableHashTable.h" #include "js/TraceableVector.h" @@ -275,6 +276,45 @@ BEGIN_TEST(testGCRootedVector) } END_TEST(testGCRootedVector) +BEGIN_TEST(testTraceableFifo) +{ + using ShapeFifo = TraceableFifo; + JS::Rooted shapes(cx, ShapeFifo(cx)); + CHECK(shapes.empty()); + + for (size_t i = 0; i < 10; ++i) { + RootedObject obj(cx, JS_NewObject(cx, nullptr)); + RootedValue val(cx, UndefinedValue()); + // Construct a unique property name to ensure that the object creates a + // new shape. + char buffer[2]; + buffer[0] = 'a' + i; + buffer[1] = '\0'; + CHECK(JS_SetProperty(cx, obj, buffer, val)); + CHECK(shapes.pushBack(obj->as().lastProperty())); + } + + CHECK(shapes.length() == 10); + + JS_GC(rt); + JS_GC(rt); + + for (size_t i = 0; i < 10; ++i) { + // Check the shape to ensure it did not get collected. + char buffer[2]; + buffer[0] = 'a' + i; + buffer[1] = '\0'; + bool match; + CHECK(JS_StringEqualsAscii(cx, JSID_TO_STRING(shapes.front()->propid()), buffer, &match)); + CHECK(match); + CHECK(shapes.popFront()); + } + + CHECK(shapes.empty()); + return true; +} +END_TEST(testTraceableFifo) + using ShapeVec = TraceableVector; static bool diff --git a/js/src/moz.build b/js/src/moz.build index 5b285414c970..3ff01f114517 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -123,6 +123,7 @@ EXPORTS.js += [ '../public/RootingAPI.h', '../public/SliceBudget.h', '../public/StructuredClone.h', + '../public/TraceableFifo.h', '../public/TraceableHashTable.h', '../public/TraceableVector.h', '../public/TraceKind.h', From cacf0e7718952c14a91713c0b9658d6b1e36538d Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 31 Jul 2015 13:27:44 -0700 Subject: [PATCH 016/113] Bug 1189490 - Part 2: Stop using mozilla::LinkedList for the allocations and tenure promotions logs and use js::TraceableFifo instead; r=terrence --- js/src/vm/Debugger.cpp | 121 ++++++++++------------------------- js/src/vm/Debugger.h | 93 ++++++++++++++++----------- js/src/vm/DebuggerMemory.cpp | 76 ++++++++++++---------- 3 files changed, 133 insertions(+), 157 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 88ed1c149eec..b7a62f7245df 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -357,15 +357,15 @@ Debugger::Debugger(JSContext* cx, NativeObject* dbg) : object(dbg), uncaughtExceptionHook(nullptr), enabled(true), + allowUnobservedAsmJS(false), observedGCs(cx), + tenurePromotionsLog(cx), trackingTenurePromotions(false), - tenurePromotionsLogLength(0), maxTenurePromotionsLogLength(DEFAULT_MAX_LOG_LENGTH), tenurePromotionsLogOverflowed(false), - allowUnobservedAsmJS(false), + allocationsLog(cx), trackingAllocationSites(false), allocationSamplingProbability(1.0), - allocationsLogLength(0), maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH), allocationsLogOverflowed(false), frames(cx->runtime()), @@ -390,8 +390,8 @@ Debugger::Debugger(JSContext* cx, NativeObject* dbg) Debugger::~Debugger() { MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty()); - emptyAllocationsLog(); - emptyTenurePromotionsLog(); + allocationsLog.clear(); + tenurePromotionsLog.clear(); /* * Since the inactive state for this link is a singleton cycle, it's always @@ -1701,7 +1701,7 @@ Debugger::isDebuggee(const JSCompartment* compartment) const return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal()); } -Debugger::TenurePromotionsEntry::TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when) +Debugger::TenurePromotionsLogEntry::TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when) : className(obj.getClass()->name), when(when), frame(getObjectAllocationSite(obj)), @@ -1712,43 +1712,17 @@ Debugger::TenurePromotionsEntry::TenurePromotionsEntry(JSRuntime* rt, JSObject& void Debugger::logTenurePromotion(JSRuntime* rt, JSObject& obj, double when) { - auto* entry = js_new(rt, obj, when); - if (!entry) + if (!tenurePromotionsLog.emplaceBack(rt, obj, when)) CrashAtUnhandlableOOM("Debugger::logTenurePromotion"); - tenurePromotionsLog.insertBack(entry); - if (tenurePromotionsLogLength >= maxTenurePromotionsLogLength) { - js_delete(tenurePromotionsLog.popFirst()); + if (tenurePromotionsLog.length() > maxTenurePromotionsLogLength) { + if (!tenurePromotionsLog.popFront()) + CrashAtUnhandlableOOM("Debugger::logTenurePromotion"); + MOZ_ASSERT(tenurePromotionsLog.length() == maxTenurePromotionsLogLength); tenurePromotionsLogOverflowed = true; - } else { - tenurePromotionsLogLength++; } } -/* static */ Debugger::AllocationSite* -Debugger::AllocationSite::create(JSContext* cx, HandleObject frame, double when, HandleObject obj) -{ - assertSameCompartment(cx, frame); - - RootedAtom ctorName(cx); - { - AutoCompartment ac(cx, obj); - if (!obj->constructorDisplayAtom(cx, &ctorName)) - return nullptr; - } - - AllocationSite* allocSite = cx->new_(frame, when); - if (!allocSite) - return nullptr; - - allocSite->className = obj->getClass()->name; - allocSite->ctorName = ctorName.get(); - allocSite->size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf); - - return allocSite; -} - - bool Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, double when) @@ -1760,38 +1734,34 @@ Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame if (!cx->compartment()->wrap(cx, &wrappedFrame)) return false; - AllocationSite* allocSite = AllocationSite::create(cx, wrappedFrame, when, obj); - if (!allocSite) + RootedAtom ctorName(cx); + { + AutoCompartment ac(cx, obj); + if (!obj->constructorDisplayAtom(cx, &ctorName)) + return nullptr; + } + + auto className = obj->getClass()->name; + auto size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf); + + if (!allocationsLog.emplaceBack(wrappedFrame, when, className, ctorName, size)) + { + ReportOutOfMemory(cx); return false; + } - allocationsLog.insertBack(allocSite); - - if (allocationsLogLength >= maxAllocationsLogLength) { - js_delete(allocationsLog.popFirst()); + if (allocationsLog.length() > maxAllocationsLogLength) { + if (!allocationsLog.popFront()) { + ReportOutOfMemory(cx); + return false; + } + MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength); allocationsLogOverflowed = true; - } else { - allocationsLogLength++; } return true; } -void -Debugger::emptyAllocationsLog() -{ - while (!allocationsLog.isEmpty()) - js_delete(allocationsLog.popFirst()); - allocationsLogLength = 0; -} - -void -Debugger::emptyTenurePromotionsLog() -{ - while (!tenurePromotionsLog.isEmpty()) - js_delete(tenurePromotionsLog.popFirst()); - tenurePromotionsLogLength = 0; -} - JSTrapStatus Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp) { @@ -2332,7 +2302,7 @@ Debugger::removeAllocationsTrackingForAllDebuggees() for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) { Debugger::removeAllocationsTracking(*r.front().get()); } - emptyAllocationsLog(); + allocationsLog.clear(); } @@ -2351,19 +2321,7 @@ Debugger::markCrossCompartmentEdges(JSTracer* trc) // `Debugger::logTenurePromotion`, we can't hold onto CCWs inside the log, // and instead have unwrapped cross-compartment edges. We need to be sure to // mark those here. - traceTenurePromotionsLog(trc); -} - -/* - * Trace every entry in the promoted to tenured heap log. - */ -void -Debugger::traceTenurePromotionsLog(JSTracer* trc) -{ - for (TenurePromotionsEntry* e = tenurePromotionsLog.getFirst(); e; e = e->getNext()) { - if (e->frame) - TraceEdge(trc, &e->frame, "Debugger::tenurePromotionsLog SavedFrame"); - } + TenurePromotionsLog::trace(&tenurePromotionsLog, trc); } /* @@ -2544,17 +2502,8 @@ Debugger::trace(JSTracer* trc) TraceEdge(trc, &frameobj, "live Debugger.Frame"); } - /* - * Mark every allocation site in our allocation log. - */ - for (AllocationSite* s = allocationsLog.getFirst(); s; s = s->getNext()) { - if (s->frame) - TraceEdge(trc, &s->frame, "allocation log SavedFrame"); - if (s->ctorName) - TraceEdge(trc, &s->ctorName, "allocation log constructor name"); - } - - traceTenurePromotionsLog(trc); + AllocationsLog::trace(&allocationsLog, trc); + TenurePromotionsLog::trace(&tenurePromotionsLog, trc); /* Trace the weak map from JSScript instances to Debugger.Script objects. */ scripts.trace(trc); diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 7b20d77e0cea..41b95f59c725 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -24,6 +24,7 @@ #include "js/HashTable.h" #include "vm/GlobalObject.h" #include "vm/SavedStacks.h" +#include "js/TraceableFifo.h" enum JSTrapStatus { JSTRAP_ERROR, @@ -283,63 +284,72 @@ class Debugger : private mozilla::LinkedListElement void logTenurePromotion(JSRuntime* rt, JSObject& obj, double when); static JSObject* getObjectAllocationSite(JSObject& obj); - private: - HeapPtrNativeObject object; /* The Debugger object. Strong reference. */ - WeakGlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */ - JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */ - js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */ - bool enabled; - JSCList breakpoints; /* Circular list of all js::Breakpoints in this debugger */ - - // The set of GC numbers for which one or more of this Debugger's observed - // debuggees participated in. - js::HashSet observedGCs; - - struct TenurePromotionsEntry : public mozilla::LinkedListElement + struct TenurePromotionsLogEntry : public JS::StaticTraceable { - TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when); + TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when); const char* className; double when; RelocatablePtrObject frame; size_t size; + + static void trace(TenurePromotionsLogEntry* e, JSTracer* trc) { + if (e->frame) + TraceEdge(trc, &e->frame, "Debugger::TenurePromotionsLogEntry::frame"); + } }; - using TenurePromotionsLog = mozilla::LinkedList; - TenurePromotionsLog tenurePromotionsLog; - bool trackingTenurePromotions; - size_t tenurePromotionsLogLength; - size_t maxTenurePromotionsLogLength; - bool tenurePromotionsLogOverflowed; - - struct AllocationSite : public mozilla::LinkedListElement + struct AllocationsLogEntry : public JS::StaticTraceable { - AllocationSite(HandleObject frame, double when) + AllocationsLogEntry(HandleObject frame, double when, const char* className, + HandleAtom ctorName, size_t size) : frame(frame), when(when), - className(nullptr), - ctorName(nullptr), - size(0) + className(className), + ctorName(ctorName), + size(size) { MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is()); }; - static AllocationSite* create(JSContext* cx, HandleObject frame, double when, - HandleObject obj); - RelocatablePtrObject frame; double when; const char* className; RelocatablePtrAtom ctorName; size_t size; - }; - typedef mozilla::LinkedList AllocationSiteList; + static void trace(AllocationsLogEntry* e, JSTracer* trc) { + if (e->frame) + TraceEdge(trc, &e->frame, "Debugger::AllocationsLogEntry::frame"); + if (e->ctorName) + TraceEdge(trc, &e->ctorName, "Debugger::AllocationsLogEntry::ctorName"); + } + }; + + private: + HeapPtrNativeObject object; /* The Debugger object. Strong reference. */ + WeakGlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */ + JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */ + js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */ + bool enabled; bool allowUnobservedAsmJS; + JSCList breakpoints; /* Circular list of all js::Breakpoints in this debugger */ + + // The set of GC numbers for which one or more of this Debugger's observed + // debuggees participated in. + js::HashSet observedGCs; + + using TenurePromotionsLog = js::TraceableFifo; + TenurePromotionsLog tenurePromotionsLog; + bool trackingTenurePromotions; + size_t maxTenurePromotionsLogLength; + bool tenurePromotionsLogOverflowed; + + using AllocationsLog = js::TraceableFifo; + + AllocationsLog allocationsLog; bool trackingAllocationSites; double allocationSamplingProbability; - AllocationSiteList allocationsLog; - size_t allocationsLogLength; size_t maxAllocationsLogLength; bool allocationsLogOverflowed; @@ -347,8 +357,6 @@ class Debugger : private mozilla::LinkedListElement bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, double when); - void emptyAllocationsLog(); - void emptyTenurePromotionsLog(); /* * Recompute the set of debuggee zones based on the set of debuggee globals. @@ -503,7 +511,6 @@ class Debugger : private mozilla::LinkedListElement void trace(JSTracer* trc); static void finalize(FreeOp* fop, JSObject* obj); void markCrossCompartmentEdges(JSTracer* tracer); - void traceTenurePromotionsLog(JSTracer* trc); static const Class jsclass; @@ -920,6 +927,20 @@ class Debugger : private mozilla::LinkedListElement Debugger & operator=(const Debugger&) = delete; }; +template<> +struct DefaultTracer { + static void trace(JSTracer* trc, Debugger::TenurePromotionsLogEntry* e, const char*) { + Debugger::TenurePromotionsLogEntry::trace(e, trc); + } +}; + +template<> +struct DefaultTracer { + static void trace(JSTracer* trc, Debugger::AllocationsLogEntry* e, const char*) { + Debugger::AllocationsLogEntry::trace(e, trc); + } +}; + class BreakpointSite { friend class Breakpoint; friend struct ::JSCompartment; diff --git a/js/src/vm/DebuggerMemory.cpp b/js/src/vm/DebuggerMemory.cpp index 08c1ec82f094..2ee25fdb7d78 100644 --- a/js/src/vm/DebuggerMemory.cpp +++ b/js/src/vm/DebuggerMemory.cpp @@ -185,7 +185,7 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp) return false; } - size_t length = dbg->allocationsLogLength; + size_t length = dbg->allocationsLog.length(); RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length)); if (!result) @@ -197,21 +197,21 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp) if (!obj) return false; - // Don't pop the AllocationSite yet. The queue's links are followed by - // the GC to find the AllocationSite, but are not barriered, so we must - // edit them with great care. Use the queue entry in place, and then - // pop and delete together. - Debugger::AllocationSite* allocSite = dbg->allocationsLog.getFirst(); + // Don't pop the AllocationsLogEntry yet. The queue's links are followed + // by the GC to find the AllocationsLogEntry, but are not barriered, so + // we must edit them with great care. Use the queue entry in place, and + // then pop and delete together. + Debugger::AllocationsLogEntry& entry = dbg->allocationsLog.front(); - RootedValue frame(cx, ObjectOrNullValue(allocSite->frame)); + RootedValue frame(cx, ObjectOrNullValue(entry.frame)); if (!DefineProperty(cx, obj, cx->names().frame, frame)) return false; - RootedValue timestampValue(cx, NumberValue(allocSite->when)); + RootedValue timestampValue(cx, NumberValue(entry.when)); if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue)) return false; - RootedString className(cx, Atomize(cx, allocSite->className, strlen(allocSite->className))); + RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className))); if (!className) return false; RootedValue classNameValue(cx, StringValue(className)); @@ -219,26 +219,27 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp) return false; RootedValue ctorName(cx, NullValue()); - if (allocSite->ctorName) - ctorName.setString(allocSite->ctorName); + if (entry.ctorName) + ctorName.setString(entry.ctorName); if (!DefineProperty(cx, obj, cx->names().constructor, ctorName)) return false; - RootedValue size(cx, NumberValue(allocSite->size)); + RootedValue size(cx, NumberValue(entry.size)); if (!DefineProperty(cx, obj, cx->names().size, size)) return false; result->setDenseElement(i, ObjectValue(*obj)); - // Pop the front queue entry, and delete it immediately, so that - // the GC sees the AllocationSite's RelocatablePtr barriers run - // atomically with the change to the graph (the queue link). - MOZ_ALWAYS_TRUE(dbg->allocationsLog.popFirst() == allocSite); - js_delete(allocSite); + // Pop the front queue entry, and delete it immediately, so that the GC + // sees the AllocationsLogEntry's RelocatablePtr barriers run atomically + // with the change to the graph (the queeue link). + if (!dbg->allocationsLog.popFront()) { + ReportOutOfMemory(cx); + return false; + } } dbg->allocationsLogOverflowed = false; - dbg->allocationsLogLength = 0; args.rval().setObject(*result); return true; } @@ -272,9 +273,11 @@ DebuggerMemory::setMaxAllocationsLogLength(JSContext* cx, unsigned argc, Value* Debugger* dbg = memory->getDebugger(); dbg->maxAllocationsLogLength = max; - while (dbg->allocationsLogLength > dbg->maxAllocationsLogLength) { - js_delete(dbg->allocationsLog.getFirst()); - dbg->allocationsLogLength--; + while (dbg->allocationsLog.length() > dbg->maxAllocationsLogLength) { + if (!dbg->allocationsLog.popFront()) { + ReportOutOfMemory(cx); + return false; + } } args.rval().setUndefined(); @@ -352,7 +355,7 @@ DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp return false; } - size_t length = dbg->tenurePromotionsLogLength; + size_t length = dbg->tenurePromotionsLog.length(); RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length)); if (!result) @@ -368,41 +371,42 @@ DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp // followed by the GC to find the TenurePromotionsEntry, but are not // barriered, so we must edit them with great care. Use the queue entry // in place, and then pop and delete together. - auto* entry = dbg->tenurePromotionsLog.getFirst(); + auto& entry = dbg->tenurePromotionsLog.front(); - RootedValue frame(cx, ObjectOrNullValue(entry->frame)); + RootedValue frame(cx, ObjectOrNullValue(entry.frame)); if (!cx->compartment()->wrap(cx, &frame) || !DefineProperty(cx, obj, cx->names().frame, frame)) { return false; } - RootedValue timestampValue(cx, NumberValue(entry->when)); + RootedValue timestampValue(cx, NumberValue(entry.when)); if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue)) return false; - RootedString className(cx, Atomize(cx, entry->className, strlen(entry->className))); + RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className))); if (!className) return false; RootedValue classNameValue(cx, StringValue(className)); if (!DefineProperty(cx, obj, cx->names().class_, classNameValue)) return false; - RootedValue sizeValue(cx, NumberValue(entry->size)); + RootedValue sizeValue(cx, NumberValue(entry.size)); if (!DefineProperty(cx, obj, cx->names().size, sizeValue)) return false; result->setDenseElement(i, ObjectValue(*obj)); - // Pop the front queue entry, and delete it immediately, so that - // the GC sees the TenurePromotionsEntry's RelocatablePtr barriers run + // Pop the front queue entry, and delete it immediately, so that the GC + // sees the TenurePromotionsEntry's RelocatablePtr barriers run // atomically with the change to the graph (the queue link). - MOZ_ALWAYS_TRUE(dbg->tenurePromotionsLog.popFirst() == entry); - js_delete(entry); + if (!dbg->tenurePromotionsLog.popFront()) { + ReportOutOfMemory(cx); + return false; + } } dbg->tenurePromotionsLogOverflowed = false; - dbg->tenurePromotionsLogLength = 0; args.rval().setObject(*result); return true; } @@ -436,9 +440,11 @@ DebuggerMemory::setMaxTenurePromotionsLogLength(JSContext* cx, unsigned argc, Va Debugger* dbg = memory->getDebugger(); dbg->maxTenurePromotionsLogLength = max; - while (dbg->tenurePromotionsLogLength > dbg->maxAllocationsLogLength) { - js_delete(dbg->tenurePromotionsLog.getFirst()); - dbg->tenurePromotionsLogLength--; + while (dbg->tenurePromotionsLog.length() > dbg->maxAllocationsLogLength) { + if (!dbg->tenurePromotionsLog.popFront()) { + ReportOutOfMemory(cx); + return false; + } } args.rval().setUndefined(); From 05d137399655a942a4f3bfb1d0d173a933c3dc4d Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 31 Jul 2015 13:27:44 -0700 Subject: [PATCH 017/113] Bug 1189490 - Follow ups: Move [Traceable]Fifo to js/src/ds; r=terrence --HG-- rename : js/public/Fifo.h => js/src/ds/Fifo.h rename : js/public/TraceableFifo.h => js/src/ds/TraceableFifo.h --- js/{public => src/ds}/Fifo.h | 0 js/{public => src/ds}/TraceableFifo.h | 2 +- js/src/jsapi-tests/testGCExactRooting.cpp | 2 +- js/src/moz.build | 2 -- js/src/vm/Debugger.cpp | 3 +-- js/src/vm/Debugger.h | 2 +- 6 files changed, 4 insertions(+), 7 deletions(-) rename js/{public => src/ds}/Fifo.h (100%) rename js/{public => src/ds}/TraceableFifo.h (99%) diff --git a/js/public/Fifo.h b/js/src/ds/Fifo.h similarity index 100% rename from js/public/Fifo.h rename to js/src/ds/Fifo.h diff --git a/js/public/TraceableFifo.h b/js/src/ds/TraceableFifo.h similarity index 99% rename from js/public/TraceableFifo.h rename to js/src/ds/TraceableFifo.h index 214dbd268fd5..4bd342d20fec 100644 --- a/js/public/TraceableFifo.h +++ b/js/src/ds/TraceableFifo.h @@ -7,8 +7,8 @@ #ifndef js_TraceableFifo_h #define js_TraceableFifo_h +#include "ds/Fifo.h" #include "js/RootingAPI.h" -#include "js/Fifo.h" namespace js { diff --git a/js/src/jsapi-tests/testGCExactRooting.cpp b/js/src/jsapi-tests/testGCExactRooting.cpp index a4e4d07a63ec..d901a2c9eb6f 100644 --- a/js/src/jsapi-tests/testGCExactRooting.cpp +++ b/js/src/jsapi-tests/testGCExactRooting.cpp @@ -5,8 +5,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "ds/TraceableFifo.h" #include "js/RootingAPI.h" -#include "js/TraceableFifo.h" #include "js/TraceableHashTable.h" #include "js/TraceableVector.h" diff --git a/js/src/moz.build b/js/src/moz.build index 3ff01f114517..ade842ea3387 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -108,7 +108,6 @@ EXPORTS.js += [ '../public/Conversions.h', '../public/Date.h', '../public/Debug.h', - '../public/Fifo.h', '../public/GCAPI.h', '../public/HashTable.h', '../public/HeapAPI.h', @@ -123,7 +122,6 @@ EXPORTS.js += [ '../public/RootingAPI.h', '../public/SliceBudget.h', '../public/StructuredClone.h', - '../public/TraceableFifo.h', '../public/TraceableHashTable.h', '../public/TraceableVector.h', '../public/TraceKind.h', diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index b7a62f7245df..0bfab1b5f360 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -1744,8 +1744,7 @@ Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame auto className = obj->getClass()->name; auto size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf); - if (!allocationsLog.emplaceBack(wrappedFrame, when, className, ctorName, size)) - { + if (!allocationsLog.emplaceBack(wrappedFrame, when, className, ctorName, size)) { ReportOutOfMemory(cx); return false; } diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 41b95f59c725..aa7f7e79111f 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -19,12 +19,12 @@ #include "jsweakmap.h" #include "jswrapper.h" +#include "ds/TraceableFifo.h" #include "gc/Barrier.h" #include "js/Debug.h" #include "js/HashTable.h" #include "vm/GlobalObject.h" #include "vm/SavedStacks.h" -#include "js/TraceableFifo.h" enum JSTrapStatus { JSTRAP_ERROR, From a1da098a25c2e635fdb3be364cdf3eb1181a564a Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 31 Jul 2015 17:03:58 -0400 Subject: [PATCH 018/113] Bug 1189261 - Mark tile invalid region in coordinates relative to the tile. r=jrmuizel --- gfx/layers/client/SingleTiledContentClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/layers/client/SingleTiledContentClient.cpp b/gfx/layers/client/SingleTiledContentClient.cpp index aa2daeb6d291..7f33d5adc9e1 100644 --- a/gfx/layers/client/SingleTiledContentClient.cpp +++ b/gfx/layers/client/SingleTiledContentClient.cpp @@ -186,7 +186,7 @@ ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, // Mark the area we just drew into the back buffer as invalid in the front buffer as they're // now out of sync. - mTile.mInvalidFront.OrWith(paintRegion); + mTile.mInvalidFront.OrWith(tileDirtyRegion); // The new buffer is now validated, remove the dirty region from it. mTile.mInvalidBack.SubOut(tileDirtyRegion); From b9f9e00ba35500bff6ac6bd8e0c444e60cb18a0b Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 31 Jul 2015 17:04:43 -0400 Subject: [PATCH 019/113] Bug 1189710 - Use correct offset for LayerRenderState in TiledContentHost. r=jrmuizel --- gfx/layers/composite/TiledContentHost.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/gfx/layers/composite/TiledContentHost.h b/gfx/layers/composite/TiledContentHost.h index 44c547b733d8..1291fb1a0b85 100644 --- a/gfx/layers/composite/TiledContentHost.h +++ b/gfx/layers/composite/TiledContentHost.h @@ -195,14 +195,17 @@ public: TextureHost* host = mTiledBuffer.GetTile(0).mTextureHost; if (host) { MOZ_ASSERT(!mTiledBuffer.GetTile(0).mTextureHostOnWhite, "Component alpha not supported!"); - LayerRenderState state = host->GetRenderState(); - // Offset by the distance between the start of the valid (visible) region and the top-left - // of the tile. gfx::IntPoint offset = mTiledBuffer.GetTileOffset(mTiledBuffer.GetPlacement().TilePosition(0)); - state.SetOffset(offset - GetValidRegion().GetBounds().TopLeft()); - return host->GetRenderState(); + // Don't try to use HWC if the content doesn't start at the top-left of the tile. + if (offset != GetValidRegion().GetBounds().TopLeft()) { + return LayerRenderState(); + } + + LayerRenderState state = host->GetRenderState(); + state.SetOffset(offset); + return state; } } return LayerRenderState(); From 9d73d12d9d6666c26b477ee3e649d620544253ca Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 31 Jul 2015 17:06:04 -0400 Subject: [PATCH 020/113] Backout 104b0bbd714f (Bug 1189399) and re-enable single tiling for B2G since the issues should be resolved now. --- gfx/layers/client/ClientPaintedLayer.cpp | 6 +----- gfx/layers/client/ClientPaintedLayer.h | 9 --------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/gfx/layers/client/ClientPaintedLayer.cpp b/gfx/layers/client/ClientPaintedLayer.cpp index 92c2071fd70a..b6592c8b6f0b 100644 --- a/gfx/layers/client/ClientPaintedLayer.cpp +++ b/gfx/layers/client/ClientPaintedLayer.cpp @@ -152,11 +152,7 @@ already_AddRefed ClientLayerManager::CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint) { NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); - if ( -#ifdef MOZ_B2G - aHint == SCROLLABLE && -#endif - gfxPrefs::LayersTilesEnabled() + if (gfxPrefs::LayersTilesEnabled() #ifndef MOZ_X11 && (AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_OPENGL || AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D9 || diff --git a/gfx/layers/client/ClientPaintedLayer.h b/gfx/layers/client/ClientPaintedLayer.h index 6865f445f1b1..670a1c75fd50 100644 --- a/gfx/layers/client/ClientPaintedLayer.h +++ b/gfx/layers/client/ClientPaintedLayer.h @@ -69,15 +69,6 @@ public: virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) override; - virtual bool IsOptimizedFor(LayerManager::PaintedLayerCreationHint aCreationHint) override - { -#ifdef MOZ_B2G - return aCreationHint == GetCreationHint(); -#else - return true; -#endif - } - virtual void ClearCachedResources() override { if (mContentClient) { From 6cd7de0f3507e1b30b01f93b12b62487fc6afd93 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 31 Jul 2015 14:10:30 -0700 Subject: [PATCH 021/113] Backed out 4 changesets (bug 1189490) for build and SM bustage CLOSED TREE Backed out changeset c0eef95cd0cb (bug 1189490) Backed out changeset 5158c4514c34 (bug 1189490) Backed out changeset 78f80496c70a (bug 1189490) Backed out changeset cebec1f7c9db (bug 1189490) --- js/src/ds/Fifo.h | 154 ---------------------- js/src/ds/TraceableFifo.h | 124 ----------------- js/src/jsapi-tests/testGCExactRooting.cpp | 40 ------ js/src/vm/Debugger.cpp | 120 ++++++++++++----- js/src/vm/Debugger.h | 101 ++++++-------- js/src/vm/DebuggerMemory.cpp | 76 +++++------ 6 files changed, 161 insertions(+), 454 deletions(-) delete mode 100644 js/src/ds/Fifo.h delete mode 100644 js/src/ds/TraceableFifo.h diff --git a/js/src/ds/Fifo.h b/js/src/ds/Fifo.h deleted file mode 100644 index de482e7a240f..000000000000 --- a/js/src/ds/Fifo.h +++ /dev/null @@ -1,154 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * 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/. */ - -#ifndef js_Fifo_h -#define js_Fifo_h - -#include "mozilla/Move.h" - -#include "js/Vector.h" - -namespace js { - -// A first-in-first-out queue container type. Fifo calls constructors and -// destructors of all elements added so non-PODs may be used safely. |Fifo| -// stores the first |MinInlineCapacity| elements in-place before resorting to -// dynamic allocation. -// -// T requirements: -// - Either movable or copyable. -// MinInlineCapacity requirements: -// - Must be even. -// AllocPolicy: -// - see "Allocation policies" in AllocPolicy.h -template -class Fifo -{ - static_assert(MinInlineCapacity % 2 == 0, "MinInlineCapacity must be even!"); - - protected: - // An element A is "younger" than an element B if B was inserted into the - // |Fifo| before A was. - // - // Invariant 1: Every element within |front_| is younger than every element - // within |rear_|. - // Invariant 2: Entries within |front_| are sorted from younger to older. - // Invariant 3: Entries within |rear_| are sorted from older to younger. - // Invariant 4: If the |Fifo| is not empty, then |front_| is not empty. - Vector front_; - Vector rear_; - - private: - // Maintain invariants after adding or removing entries. - bool fixup() { - if (!front_.empty()) - return true; - - if (!front_.reserve(rear_.length())) - return false; - - while (!rear_.empty()) { - front_.infallibleAppend(mozilla::Move(rear_.back())); - rear_.popBack(); - } - - return true; - } - - public: - explicit Fifo(AllocPolicy alloc = AllocPolicy()) - : front_(alloc) - , rear_(alloc) - { } - - Fifo(Fifo&& rhs) - : front_(mozilla::Move(rhs.front_)) - , rear_(mozilla::Move(rhs.rear_)) - { } - - Fifo& operator=(Fifo&& rhs) { - MOZ_ASSERT(&rhs != this, "self-move disallowed"); - this->~Fifo(); - new (this) Fifo(mozilla::Move(rhs)); - return *this; - } - - Fifo(const Fifo&) = delete; - Fifo& operator=(const Fifo&) = delete; - - size_t length() const { - MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0); // Invariant 4. - return front_.length() + rear_.length(); - } - - bool empty() const { - MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0); // Invariant 4. - return front_.empty(); - } - - // Push an element to the back of the queue. This method can take either a - // |const T&| or a |T&&|. - template - bool pushBack(U&& u) { - if (!rear_.append(mozilla::Forward(u))) - return false; - if (!fixup()) { - rear_.popBack(); - return false; - } - return true; - } - - // Construct a T in-place at the back of the queue. - template - bool emplaceBack(Args&&... args) { - if (!rear_.emplaceBack(mozilla::Forward(args)...)) - return false; - if (!fixup()) { - rear_.popBack(); - return false; - } - return true; - } - - // Access the element at the front of the queue. - T& front() { - MOZ_ASSERT(!empty()); - return front_.back(); - } - const T& front() const { - MOZ_ASSERT(!empty()); - return front_.back(); - } - - // Remove the front element from the queue. - bool popFront() { - MOZ_ASSERT(!empty()); - T t(mozilla::Move(front())); - front_.popBack(); - if (!fixup()) { - // Attempt to remain in a valid state by reinserting the element - // back at the front. If we can't remain in a valid state in the - // face of OOMs, crash. - if (!front_.append(mozilla::Move(t))) - CrashAtUnhandlableOOM("js::Fifo::popFront"); - return false; - } - return true; - } - - // Clear all elements from the queue. - void clear() { - front_.clear(); - rear_.clear(); - } -}; - -} // namespace js - -#endif /* js_Fifo_h */ diff --git a/js/src/ds/TraceableFifo.h b/js/src/ds/TraceableFifo.h deleted file mode 100644 index 4bd342d20fec..000000000000 --- a/js/src/ds/TraceableFifo.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * 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/. */ - -#ifndef js_TraceableFifo_h -#define js_TraceableFifo_h - -#include "ds/Fifo.h" -#include "js/RootingAPI.h" - -namespace js { - -template struct DefaultTracer; - -// A TraceableFifo is a Fifo with an additional trace method that knows how to -// visit all of the items stored in the Fifo. For Fifos that contain GC things, -// this is usually more convenient than manually iterating and marking the -// contents. -// -// Most types of GC pointers as keys and values can be traced with no extra -// infrastructure. For structs and non-gc-pointer members, ensure that there is -// a specialization of DefaultTracer with an appropriate trace method -// available to handle the custom type. -// -// Note that although this Fifo's trace will deal correctly with moved items, it -// does not itself know when to barrier or trace items. To function properly it -// must either be used with Rooted, or barriered and traced manually. -template > -class TraceableFifo - : public js::Fifo, - public JS::StaticTraceable -{ - using Base = js::Fifo; - - public: - explicit TraceableFifo(AllocPolicy alloc = AllocPolicy()) : Base(alloc) {} - - static void trace(TraceableFifo* tf, JSTracer* trc) { - for (size_t i = 0; i < tf->front_.length(); ++i) - TraceFunc::trace(trc, &tf->front_[i], "fifo element"); - for (size_t i = 0; i < tf->rear_.length(); ++i) - TraceFunc::trace(trc, &tf->rear_[i], "fifo element"); - } -}; - -template -class TraceableFifoOperations -{ - using TF = TraceableFifo; - const TF& fifo() const { return static_cast(this)->extract(); } - - public: - size_t length() const { return fifo().length(); } - bool empty() const { return fifo().empty(); } - const T& front() const { return fifo().front(); } -}; - -template -class MutableTraceableFifoOperations - : public TraceableFifoOperations -{ - using TF = TraceableFifo; - TF& fifo() { return static_cast(this)->extract(); } - - public: - T& front() { return fifo().front(); } - - template bool pushBack(U&& u) { return fifo().pushBack(mozilla::Forward(u)); } - template bool emplaceBack(Args&&... args) { - return fifo().emplaceBack(mozilla::Forward(args...)); - } - - bool popFront() { return fifo().popFront(); } - void clear() { fifo().clear(); } -}; - -template -class RootedBase> - : public MutableTraceableFifoOperations>, A,B,C,D> -{ - using TF = TraceableFifo; - - friend class TraceableFifoOperations, A,B,C,D>; - const TF& extract() const { return *static_cast*>(this)->address(); } - - friend class MutableTraceableFifoOperations, A,B,C,D>; - TF& extract() { return *static_cast*>(this)->address(); } -}; - -template -class MutableHandleBase> - : public MutableTraceableFifoOperations>, A,B,C,D> -{ - using TF = TraceableFifo; - - friend class TraceableFifoOperations, A,B,C,D>; - const TF& extract() const { - return *static_cast*>(this)->address(); - } - - friend class MutableTraceableFifoOperations, A,B,C,D>; - TF& extract() { return *static_cast*>(this)->address(); } -}; - -template -class HandleBase> - : public TraceableFifoOperations>, A,B,C,D> -{ - using TF = TraceableFifo; - - friend class TraceableFifoOperations, A,B,C,D>; - const TF& extract() const { - return *static_cast*>(this)->address(); - } -}; - -} // namespace js - -#endif // js_TraceableFifo_h diff --git a/js/src/jsapi-tests/testGCExactRooting.cpp b/js/src/jsapi-tests/testGCExactRooting.cpp index d901a2c9eb6f..3ac3b1b2b4d7 100644 --- a/js/src/jsapi-tests/testGCExactRooting.cpp +++ b/js/src/jsapi-tests/testGCExactRooting.cpp @@ -5,7 +5,6 @@ * 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 "ds/TraceableFifo.h" #include "js/RootingAPI.h" #include "js/TraceableHashTable.h" #include "js/TraceableVector.h" @@ -276,45 +275,6 @@ BEGIN_TEST(testGCRootedVector) } END_TEST(testGCRootedVector) -BEGIN_TEST(testTraceableFifo) -{ - using ShapeFifo = TraceableFifo; - JS::Rooted shapes(cx, ShapeFifo(cx)); - CHECK(shapes.empty()); - - for (size_t i = 0; i < 10; ++i) { - RootedObject obj(cx, JS_NewObject(cx, nullptr)); - RootedValue val(cx, UndefinedValue()); - // Construct a unique property name to ensure that the object creates a - // new shape. - char buffer[2]; - buffer[0] = 'a' + i; - buffer[1] = '\0'; - CHECK(JS_SetProperty(cx, obj, buffer, val)); - CHECK(shapes.pushBack(obj->as().lastProperty())); - } - - CHECK(shapes.length() == 10); - - JS_GC(rt); - JS_GC(rt); - - for (size_t i = 0; i < 10; ++i) { - // Check the shape to ensure it did not get collected. - char buffer[2]; - buffer[0] = 'a' + i; - buffer[1] = '\0'; - bool match; - CHECK(JS_StringEqualsAscii(cx, JSID_TO_STRING(shapes.front()->propid()), buffer, &match)); - CHECK(match); - CHECK(shapes.popFront()); - } - - CHECK(shapes.empty()); - return true; -} -END_TEST(testTraceableFifo) - using ShapeVec = TraceableVector; static bool diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 0bfab1b5f360..88ed1c149eec 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -357,15 +357,15 @@ Debugger::Debugger(JSContext* cx, NativeObject* dbg) : object(dbg), uncaughtExceptionHook(nullptr), enabled(true), - allowUnobservedAsmJS(false), observedGCs(cx), - tenurePromotionsLog(cx), trackingTenurePromotions(false), + tenurePromotionsLogLength(0), maxTenurePromotionsLogLength(DEFAULT_MAX_LOG_LENGTH), tenurePromotionsLogOverflowed(false), - allocationsLog(cx), + allowUnobservedAsmJS(false), trackingAllocationSites(false), allocationSamplingProbability(1.0), + allocationsLogLength(0), maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH), allocationsLogOverflowed(false), frames(cx->runtime()), @@ -390,8 +390,8 @@ Debugger::Debugger(JSContext* cx, NativeObject* dbg) Debugger::~Debugger() { MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty()); - allocationsLog.clear(); - tenurePromotionsLog.clear(); + emptyAllocationsLog(); + emptyTenurePromotionsLog(); /* * Since the inactive state for this link is a singleton cycle, it's always @@ -1701,7 +1701,7 @@ Debugger::isDebuggee(const JSCompartment* compartment) const return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal()); } -Debugger::TenurePromotionsLogEntry::TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when) +Debugger::TenurePromotionsEntry::TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when) : className(obj.getClass()->name), when(when), frame(getObjectAllocationSite(obj)), @@ -1712,17 +1712,43 @@ Debugger::TenurePromotionsLogEntry::TenurePromotionsLogEntry(JSRuntime* rt, JSOb void Debugger::logTenurePromotion(JSRuntime* rt, JSObject& obj, double when) { - if (!tenurePromotionsLog.emplaceBack(rt, obj, when)) + auto* entry = js_new(rt, obj, when); + if (!entry) CrashAtUnhandlableOOM("Debugger::logTenurePromotion"); - if (tenurePromotionsLog.length() > maxTenurePromotionsLogLength) { - if (!tenurePromotionsLog.popFront()) - CrashAtUnhandlableOOM("Debugger::logTenurePromotion"); - MOZ_ASSERT(tenurePromotionsLog.length() == maxTenurePromotionsLogLength); + tenurePromotionsLog.insertBack(entry); + if (tenurePromotionsLogLength >= maxTenurePromotionsLogLength) { + js_delete(tenurePromotionsLog.popFirst()); tenurePromotionsLogOverflowed = true; + } else { + tenurePromotionsLogLength++; } } +/* static */ Debugger::AllocationSite* +Debugger::AllocationSite::create(JSContext* cx, HandleObject frame, double when, HandleObject obj) +{ + assertSameCompartment(cx, frame); + + RootedAtom ctorName(cx); + { + AutoCompartment ac(cx, obj); + if (!obj->constructorDisplayAtom(cx, &ctorName)) + return nullptr; + } + + AllocationSite* allocSite = cx->new_(frame, when); + if (!allocSite) + return nullptr; + + allocSite->className = obj->getClass()->name; + allocSite->ctorName = ctorName.get(); + allocSite->size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf); + + return allocSite; +} + + bool Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, double when) @@ -1734,33 +1760,38 @@ Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame if (!cx->compartment()->wrap(cx, &wrappedFrame)) return false; - RootedAtom ctorName(cx); - { - AutoCompartment ac(cx, obj); - if (!obj->constructorDisplayAtom(cx, &ctorName)) - return nullptr; - } - - auto className = obj->getClass()->name; - auto size = JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf); - - if (!allocationsLog.emplaceBack(wrappedFrame, when, className, ctorName, size)) { - ReportOutOfMemory(cx); + AllocationSite* allocSite = AllocationSite::create(cx, wrappedFrame, when, obj); + if (!allocSite) return false; - } - if (allocationsLog.length() > maxAllocationsLogLength) { - if (!allocationsLog.popFront()) { - ReportOutOfMemory(cx); - return false; - } - MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength); + allocationsLog.insertBack(allocSite); + + if (allocationsLogLength >= maxAllocationsLogLength) { + js_delete(allocationsLog.popFirst()); allocationsLogOverflowed = true; + } else { + allocationsLogLength++; } return true; } +void +Debugger::emptyAllocationsLog() +{ + while (!allocationsLog.isEmpty()) + js_delete(allocationsLog.popFirst()); + allocationsLogLength = 0; +} + +void +Debugger::emptyTenurePromotionsLog() +{ + while (!tenurePromotionsLog.isEmpty()) + js_delete(tenurePromotionsLog.popFirst()); + tenurePromotionsLogLength = 0; +} + JSTrapStatus Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp) { @@ -2301,7 +2332,7 @@ Debugger::removeAllocationsTrackingForAllDebuggees() for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) { Debugger::removeAllocationsTracking(*r.front().get()); } - allocationsLog.clear(); + emptyAllocationsLog(); } @@ -2320,7 +2351,19 @@ Debugger::markCrossCompartmentEdges(JSTracer* trc) // `Debugger::logTenurePromotion`, we can't hold onto CCWs inside the log, // and instead have unwrapped cross-compartment edges. We need to be sure to // mark those here. - TenurePromotionsLog::trace(&tenurePromotionsLog, trc); + traceTenurePromotionsLog(trc); +} + +/* + * Trace every entry in the promoted to tenured heap log. + */ +void +Debugger::traceTenurePromotionsLog(JSTracer* trc) +{ + for (TenurePromotionsEntry* e = tenurePromotionsLog.getFirst(); e; e = e->getNext()) { + if (e->frame) + TraceEdge(trc, &e->frame, "Debugger::tenurePromotionsLog SavedFrame"); + } } /* @@ -2501,8 +2544,17 @@ Debugger::trace(JSTracer* trc) TraceEdge(trc, &frameobj, "live Debugger.Frame"); } - AllocationsLog::trace(&allocationsLog, trc); - TenurePromotionsLog::trace(&tenurePromotionsLog, trc); + /* + * Mark every allocation site in our allocation log. + */ + for (AllocationSite* s = allocationsLog.getFirst(); s; s = s->getNext()) { + if (s->frame) + TraceEdge(trc, &s->frame, "allocation log SavedFrame"); + if (s->ctorName) + TraceEdge(trc, &s->ctorName, "allocation log constructor name"); + } + + traceTenurePromotionsLog(trc); /* Trace the weak map from JSScript instances to Debugger.Script objects. */ scripts.trace(trc); diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index aa7f7e79111f..7b20d77e0cea 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -19,7 +19,6 @@ #include "jsweakmap.h" #include "jswrapper.h" -#include "ds/TraceableFifo.h" #include "gc/Barrier.h" #include "js/Debug.h" #include "js/HashTable.h" @@ -284,72 +283,63 @@ class Debugger : private mozilla::LinkedListElement void logTenurePromotion(JSRuntime* rt, JSObject& obj, double when); static JSObject* getObjectAllocationSite(JSObject& obj); - struct TenurePromotionsLogEntry : public JS::StaticTraceable - { - TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when); - - const char* className; - double when; - RelocatablePtrObject frame; - size_t size; - - static void trace(TenurePromotionsLogEntry* e, JSTracer* trc) { - if (e->frame) - TraceEdge(trc, &e->frame, "Debugger::TenurePromotionsLogEntry::frame"); - } - }; - - struct AllocationsLogEntry : public JS::StaticTraceable - { - AllocationsLogEntry(HandleObject frame, double when, const char* className, - HandleAtom ctorName, size_t size) - : frame(frame), - when(when), - className(className), - ctorName(ctorName), - size(size) - { - MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is()); - }; - - RelocatablePtrObject frame; - double when; - const char* className; - RelocatablePtrAtom ctorName; - size_t size; - - static void trace(AllocationsLogEntry* e, JSTracer* trc) { - if (e->frame) - TraceEdge(trc, &e->frame, "Debugger::AllocationsLogEntry::frame"); - if (e->ctorName) - TraceEdge(trc, &e->ctorName, "Debugger::AllocationsLogEntry::ctorName"); - } - }; - private: HeapPtrNativeObject object; /* The Debugger object. Strong reference. */ WeakGlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */ JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */ js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */ bool enabled; - bool allowUnobservedAsmJS; JSCList breakpoints; /* Circular list of all js::Breakpoints in this debugger */ // The set of GC numbers for which one or more of this Debugger's observed // debuggees participated in. js::HashSet observedGCs; - using TenurePromotionsLog = js::TraceableFifo; + struct TenurePromotionsEntry : public mozilla::LinkedListElement + { + TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when); + + const char* className; + double when; + RelocatablePtrObject frame; + size_t size; + }; + + using TenurePromotionsLog = mozilla::LinkedList; TenurePromotionsLog tenurePromotionsLog; bool trackingTenurePromotions; + size_t tenurePromotionsLogLength; size_t maxTenurePromotionsLogLength; bool tenurePromotionsLogOverflowed; - using AllocationsLog = js::TraceableFifo; + struct AllocationSite : public mozilla::LinkedListElement + { + AllocationSite(HandleObject frame, double when) + : frame(frame), + when(when), + className(nullptr), + ctorName(nullptr), + size(0) + { + MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is()); + }; - AllocationsLog allocationsLog; + static AllocationSite* create(JSContext* cx, HandleObject frame, double when, + HandleObject obj); + + RelocatablePtrObject frame; + double when; + const char* className; + RelocatablePtrAtom ctorName; + size_t size; + }; + typedef mozilla::LinkedList AllocationSiteList; + + bool allowUnobservedAsmJS; bool trackingAllocationSites; double allocationSamplingProbability; + AllocationSiteList allocationsLog; + size_t allocationsLogLength; size_t maxAllocationsLogLength; bool allocationsLogOverflowed; @@ -357,6 +347,8 @@ class Debugger : private mozilla::LinkedListElement bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, double when); + void emptyAllocationsLog(); + void emptyTenurePromotionsLog(); /* * Recompute the set of debuggee zones based on the set of debuggee globals. @@ -511,6 +503,7 @@ class Debugger : private mozilla::LinkedListElement void trace(JSTracer* trc); static void finalize(FreeOp* fop, JSObject* obj); void markCrossCompartmentEdges(JSTracer* tracer); + void traceTenurePromotionsLog(JSTracer* trc); static const Class jsclass; @@ -927,20 +920,6 @@ class Debugger : private mozilla::LinkedListElement Debugger & operator=(const Debugger&) = delete; }; -template<> -struct DefaultTracer { - static void trace(JSTracer* trc, Debugger::TenurePromotionsLogEntry* e, const char*) { - Debugger::TenurePromotionsLogEntry::trace(e, trc); - } -}; - -template<> -struct DefaultTracer { - static void trace(JSTracer* trc, Debugger::AllocationsLogEntry* e, const char*) { - Debugger::AllocationsLogEntry::trace(e, trc); - } -}; - class BreakpointSite { friend class Breakpoint; friend struct ::JSCompartment; diff --git a/js/src/vm/DebuggerMemory.cpp b/js/src/vm/DebuggerMemory.cpp index 2ee25fdb7d78..08c1ec82f094 100644 --- a/js/src/vm/DebuggerMemory.cpp +++ b/js/src/vm/DebuggerMemory.cpp @@ -185,7 +185,7 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp) return false; } - size_t length = dbg->allocationsLog.length(); + size_t length = dbg->allocationsLogLength; RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length)); if (!result) @@ -197,21 +197,21 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp) if (!obj) return false; - // Don't pop the AllocationsLogEntry yet. The queue's links are followed - // by the GC to find the AllocationsLogEntry, but are not barriered, so - // we must edit them with great care. Use the queue entry in place, and - // then pop and delete together. - Debugger::AllocationsLogEntry& entry = dbg->allocationsLog.front(); + // Don't pop the AllocationSite yet. The queue's links are followed by + // the GC to find the AllocationSite, but are not barriered, so we must + // edit them with great care. Use the queue entry in place, and then + // pop and delete together. + Debugger::AllocationSite* allocSite = dbg->allocationsLog.getFirst(); - RootedValue frame(cx, ObjectOrNullValue(entry.frame)); + RootedValue frame(cx, ObjectOrNullValue(allocSite->frame)); if (!DefineProperty(cx, obj, cx->names().frame, frame)) return false; - RootedValue timestampValue(cx, NumberValue(entry.when)); + RootedValue timestampValue(cx, NumberValue(allocSite->when)); if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue)) return false; - RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className))); + RootedString className(cx, Atomize(cx, allocSite->className, strlen(allocSite->className))); if (!className) return false; RootedValue classNameValue(cx, StringValue(className)); @@ -219,27 +219,26 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp) return false; RootedValue ctorName(cx, NullValue()); - if (entry.ctorName) - ctorName.setString(entry.ctorName); + if (allocSite->ctorName) + ctorName.setString(allocSite->ctorName); if (!DefineProperty(cx, obj, cx->names().constructor, ctorName)) return false; - RootedValue size(cx, NumberValue(entry.size)); + RootedValue size(cx, NumberValue(allocSite->size)); if (!DefineProperty(cx, obj, cx->names().size, size)) return false; result->setDenseElement(i, ObjectValue(*obj)); - // Pop the front queue entry, and delete it immediately, so that the GC - // sees the AllocationsLogEntry's RelocatablePtr barriers run atomically - // with the change to the graph (the queeue link). - if (!dbg->allocationsLog.popFront()) { - ReportOutOfMemory(cx); - return false; - } + // Pop the front queue entry, and delete it immediately, so that + // the GC sees the AllocationSite's RelocatablePtr barriers run + // atomically with the change to the graph (the queue link). + MOZ_ALWAYS_TRUE(dbg->allocationsLog.popFirst() == allocSite); + js_delete(allocSite); } dbg->allocationsLogOverflowed = false; + dbg->allocationsLogLength = 0; args.rval().setObject(*result); return true; } @@ -273,11 +272,9 @@ DebuggerMemory::setMaxAllocationsLogLength(JSContext* cx, unsigned argc, Value* Debugger* dbg = memory->getDebugger(); dbg->maxAllocationsLogLength = max; - while (dbg->allocationsLog.length() > dbg->maxAllocationsLogLength) { - if (!dbg->allocationsLog.popFront()) { - ReportOutOfMemory(cx); - return false; - } + while (dbg->allocationsLogLength > dbg->maxAllocationsLogLength) { + js_delete(dbg->allocationsLog.getFirst()); + dbg->allocationsLogLength--; } args.rval().setUndefined(); @@ -355,7 +352,7 @@ DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp return false; } - size_t length = dbg->tenurePromotionsLog.length(); + size_t length = dbg->tenurePromotionsLogLength; RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length)); if (!result) @@ -371,42 +368,41 @@ DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp // followed by the GC to find the TenurePromotionsEntry, but are not // barriered, so we must edit them with great care. Use the queue entry // in place, and then pop and delete together. - auto& entry = dbg->tenurePromotionsLog.front(); + auto* entry = dbg->tenurePromotionsLog.getFirst(); - RootedValue frame(cx, ObjectOrNullValue(entry.frame)); + RootedValue frame(cx, ObjectOrNullValue(entry->frame)); if (!cx->compartment()->wrap(cx, &frame) || !DefineProperty(cx, obj, cx->names().frame, frame)) { return false; } - RootedValue timestampValue(cx, NumberValue(entry.when)); + RootedValue timestampValue(cx, NumberValue(entry->when)); if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue)) return false; - RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className))); + RootedString className(cx, Atomize(cx, entry->className, strlen(entry->className))); if (!className) return false; RootedValue classNameValue(cx, StringValue(className)); if (!DefineProperty(cx, obj, cx->names().class_, classNameValue)) return false; - RootedValue sizeValue(cx, NumberValue(entry.size)); + RootedValue sizeValue(cx, NumberValue(entry->size)); if (!DefineProperty(cx, obj, cx->names().size, sizeValue)) return false; result->setDenseElement(i, ObjectValue(*obj)); - // Pop the front queue entry, and delete it immediately, so that the GC - // sees the TenurePromotionsEntry's RelocatablePtr barriers run + // Pop the front queue entry, and delete it immediately, so that + // the GC sees the TenurePromotionsEntry's RelocatablePtr barriers run // atomically with the change to the graph (the queue link). - if (!dbg->tenurePromotionsLog.popFront()) { - ReportOutOfMemory(cx); - return false; - } + MOZ_ALWAYS_TRUE(dbg->tenurePromotionsLog.popFirst() == entry); + js_delete(entry); } dbg->tenurePromotionsLogOverflowed = false; + dbg->tenurePromotionsLogLength = 0; args.rval().setObject(*result); return true; } @@ -440,11 +436,9 @@ DebuggerMemory::setMaxTenurePromotionsLogLength(JSContext* cx, unsigned argc, Va Debugger* dbg = memory->getDebugger(); dbg->maxTenurePromotionsLogLength = max; - while (dbg->tenurePromotionsLog.length() > dbg->maxAllocationsLogLength) { - if (!dbg->tenurePromotionsLog.popFront()) { - ReportOutOfMemory(cx); - return false; - } + while (dbg->tenurePromotionsLogLength > dbg->maxAllocationsLogLength) { + js_delete(dbg->tenurePromotionsLog.getFirst()); + dbg->tenurePromotionsLogLength--; } args.rval().setUndefined(); From 9c202fac817f6676cb37daf4334a4f7c34374ed6 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Sat, 1 Aug 2015 00:13:26 +0200 Subject: [PATCH 022/113] Bug 1129313 - Part 2: self-host MapIteratorObject#next(). r=jandem --- js/src/builtin/Array.js | 24 +++---- js/src/builtin/Map.js | 53 ++++++++++++++ js/src/builtin/MapObject.cpp | 104 ++++++++-------------------- js/src/builtin/MapObject.h | 34 +++++++++ js/src/builtin/SelfHostingDefines.h | 12 ++++ js/src/builtin/String.js | 13 ++-- js/src/builtin/Utilities.js | 1 + js/src/jit/MCallOptimize.cpp | 2 + js/src/jscntxt.h | 1 + js/src/vm/SelfHosting.cpp | 32 +++++++++ 10 files changed, 176 insertions(+), 100 deletions(-) diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 230da14adb76..63f1f50e50e8 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -624,21 +624,13 @@ function ArrayIncludes(searchElement, fromIndex = 0) { return false; } -#define ARRAY_ITERATOR_SLOT_ITERATED_OBJECT 0 -#define ARRAY_ITERATOR_SLOT_NEXT_INDEX 1 -#define ARRAY_ITERATOR_SLOT_ITEM_KIND 2 - -#define ITEM_KIND_VALUE 0 -#define ITEM_KIND_KEY_AND_VALUE 1 -#define ITEM_KIND_KEY 2 - // ES6 draft specification, section 22.1.5.1, version 2013-09-05. function CreateArrayIteratorAt(obj, kind, n) { var iteratedObject = ToObject(obj); var iterator = NewArrayIterator(); - UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT, iteratedObject); - UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_NEXT_INDEX, n); - UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITEM_KIND, kind); + UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_TARGET, iteratedObject); + UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, n); + UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_ITEM_KIND, kind); return iterator; } function CreateArrayIterator(obj, kind) { @@ -655,22 +647,22 @@ function ArrayIteratorNext() { "ArrayIteratorNext"); } - var a = UnsafeGetObjectFromReservedSlot(this, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT); + var a = UnsafeGetObjectFromReservedSlot(this, ITERATOR_SLOT_TARGET); // The index might not be an integer, so we have to do a generic get here. - var index = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX); - var itemKind = UnsafeGetInt32FromReservedSlot(this, ARRAY_ITERATOR_SLOT_ITEM_KIND); + var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); + var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); var result = { value: undefined, done: false }; // FIXME: This should be ToLength, which clamps at 2**53. Bug 924058. if (index >= TO_UINT32(a.length)) { // When the above is changed to ToLength, use +1/0 here instead // of MAX_UINT32. - UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, 0xffffffff); + UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, 0xffffffff); result.done = true; return result; } - UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, index + 1); + UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1); if (itemKind === ITEM_KIND_VALUE) { result.value = a[index]; diff --git a/js/src/builtin/Map.js b/js/src/builtin/Map.js index 6bb7283a9d81..79a8605775fd 100644 --- a/js/src/builtin/Map.js +++ b/js/src/builtin/Map.js @@ -33,6 +33,59 @@ function MapForEach(callbackfn, thisArg = undefined) { } } +var iteratorTemp = { mapIterationResultPair : null }; + +function MapIteratorNext() { + // Step 1. + var O = this; + + // Steps 2-3. + if (!IsObject(O) || !IsMapIterator(O)) + return callFunction(CallMapIteratorMethodIfWrapped, O, "MapIteratorNext"); + + // Steps 4-5 (implemented in _GetNextMapEntryForIterator). + // Steps 8-9 (omitted). + + var mapIterationResultPair = iteratorTemp.mapIterationResultPair; + if (!mapIterationResultPair) { + mapIterationResultPair = iteratorTemp.mapIterationResultPair = NewDenseArray(2); + mapIterationResultPair[0] = null; + mapIterationResultPair[1] = null; + } + + var retVal = {value: undefined, done: true}; + + // Step 10.a, 11. + var done = _GetNextMapEntryForIterator(O, mapIterationResultPair); + if (!done) { + // Steps 10.b-c (omitted). + + // Step 6. + var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); + + var result; + if (itemKind === ITEM_KIND_KEY) { + // Step 10.d.i. + result = mapIterationResultPair[0]; + } else if (itemKind === ITEM_KIND_VALUE) { + // Step 10.d.ii. + result = mapIterationResultPair[1]; + } else { + // Step 10.d.iii. + assert(itemKind === ITEM_KIND_KEY_AND_VALUE, itemKind); + result = [mapIterationResultPair[0], mapIterationResultPair[1]]; + } + + mapIterationResultPair[0] = null; + mapIterationResultPair[1] = null; + retVal.value = result; + retVal.done = false; + } + + // Steps 7, 12. + return retVal; +} + // ES6 final draft 23.1.2.2. function MapSpecies() { // Step 1. diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index 9a2533af56d1..3e7df2800c3c 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -102,25 +102,6 @@ HashableValue::mark(JSTracer* trc) const namespace { -class MapIteratorObject : public NativeObject -{ - public: - static const Class class_; - - enum { TargetSlot, KindSlot, RangeSlot, SlotCount }; - static const JSFunctionSpec methods[]; - static MapIteratorObject* create(JSContext* cx, HandleObject mapobj, ValueMap* data, - MapObject::IteratorKind kind); - static bool next(JSContext* cx, unsigned argc, Value* vp); - static void finalize(FreeOp* fop, JSObject* obj); - - private: - static inline bool is(HandleValue v); - inline ValueMap::Range* range(); - inline MapObject::IteratorKind kind() const; - static bool next_impl(JSContext* cx, CallArgs args); -}; - } /* anonymous namespace */ const Class MapIteratorObject::class_ = { @@ -140,14 +121,15 @@ const Class MapIteratorObject::class_ = { const JSFunctionSpec MapIteratorObject::methods[] = { JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), - JS_FN("next", next, 0, 0), + JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0), JS_FS_END }; -inline ValueMap::Range* -MapIteratorObject::range() +static inline ValueMap::Range* +MapIteratorObjectRange(NativeObject* obj) { - return static_cast(getSlot(RangeSlot).toPrivate()); + MOZ_ASSERT(obj->is()); + return static_cast(obj->getSlot(MapIteratorObject::RangeSlot).toPrivate()); } inline MapObject::IteratorKind @@ -194,75 +176,46 @@ MapIteratorObject::create(JSContext* cx, HandleObject mapobj, ValueMap* data, return nullptr; } iterobj->setSlot(TargetSlot, ObjectValue(*mapobj)); - iterobj->setSlot(KindSlot, Int32Value(int32_t(kind))); iterobj->setSlot(RangeSlot, PrivateValue(range)); + iterobj->setSlot(KindSlot, Int32Value(int32_t(kind))); return iterobj; } void MapIteratorObject::finalize(FreeOp* fop, JSObject* obj) { - fop->delete_(obj->as().range()); + fop->delete_(MapIteratorObjectRange(static_cast(obj))); } bool -MapIteratorObject::is(HandleValue v) +MapIteratorObject::next(JSContext* cx, Handle mapIterator, + HandleArrayObject resultPairObj) { - return v.isObject() && v.toObject().hasClass(&class_); -} - -bool -MapIteratorObject::next_impl(JSContext* cx, CallArgs args) -{ - MapIteratorObject& thisobj = args.thisv().toObject().as(); - ValueMap::Range* range = thisobj.range(); - RootedValue value(cx); - bool done; + MOZ_ASSERT(resultPairObj->getDenseInitializedLength() == 2); + ValueMap::Range* range = MapIteratorObjectRange(mapIterator); if (!range || range->empty()) { js_delete(range); - thisobj.setReservedSlot(RangeSlot, PrivateValue(nullptr)); - value.setUndefined(); - done = true; - } else { - switch (thisobj.kind()) { - case MapObject::Keys: - value = range->front().key.get(); - break; - - case MapObject::Values: - value = range->front().value; - break; - - case MapObject::Entries: { - JS::AutoValueArray<2> pair(cx); - pair[0].set(range->front().key.get()); - pair[1].set(range->front().value); - - JSObject* pairobj = NewDenseCopiedArray(cx, pair.length(), pair.begin()); - if (!pairobj) - return false; - value.setObject(*pairobj); - break; - } - } - range->popFront(); - done = false; + mapIterator->setReservedSlot(RangeSlot, PrivateValue(nullptr)); + return true; } + switch (mapIterator->kind()) { + case MapObject::Keys: + resultPairObj->setDenseElementWithType(cx, 0, range->front().key.get()); + break; - RootedObject result(cx, CreateItrResultObject(cx, value, done)); - if (!result) - return false; - args.rval().setObject(*result); + case MapObject::Values: + resultPairObj->setDenseElementWithType(cx, 1, range->front().value); + break; - return true; -} - -bool -MapIteratorObject::next(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, is, next_impl, args); + case MapObject::Entries: { + resultPairObj->setDenseElementWithType(cx, 0, range->front().key.get()); + resultPairObj->setDenseElementWithType(cx, 1, range->front().value); + break; + } + } + range->popFront(); + return false; } @@ -1455,7 +1408,6 @@ js::InitSetClass(JSContext* cx, HandleObject obj) } const JSFunctionSpec selfhosting_collection_iterator_methods[] = { - JS_FN("std_Map_iterator_next", MapIteratorObject::next, 0, 0), JS_FN("std_Set_iterator_next", SetIteratorObject::next, 0, 0), JS_FS_END }; diff --git a/js/src/builtin/MapObject.h b/js/src/builtin/MapObject.h index 9919011afddc..748e6e8cd850 100644 --- a/js/src/builtin/MapObject.h +++ b/js/src/builtin/MapObject.h @@ -9,6 +9,7 @@ #include "jsobj.h" +#include "builtin/SelfHostingDefines.h" #include "vm/Runtime.h" namespace js { @@ -88,6 +89,13 @@ typedef OrderedHashSet mapIterator, + HandleArrayObject resultPairObj); + + private: + inline MapObject::IteratorKind kind() const; +}; + class SetObject : public NativeObject { public: enum IteratorKind { Values, Entries }; diff --git a/js/src/builtin/SelfHostingDefines.h b/js/src/builtin/SelfHostingDefines.h index 71f7ffd492a9..e11fc82a39a8 100644 --- a/js/src/builtin/SelfHostingDefines.h +++ b/js/src/builtin/SelfHostingDefines.h @@ -13,6 +13,7 @@ #define TO_INT32(x) ((x) | 0) #define TO_UINT32(x) ((x) >>> 0) #define IS_UINT32(x) ((x) >>> 0 === (x)) +#define MAX_NUMERIC_INDEX 0x1fffffffffffff // == Math.pow(2, 53) - 1 // Unforgeable versions of ARRAY.push(ELEMENT) and ARRAY.slice. #define ARRAY_PUSH(ARRAY, ELEMENT) \ @@ -32,4 +33,15 @@ // Stores the private WeakMap slot used for WeakSets #define WEAKSET_MAP_SLOT 0 +#define ITERATOR_SLOT_TARGET 0 +// Used for collection iterators. +#define ITERATOR_SLOT_RANGE 1 +// Used for list, i.e. Array and String, iterators. +#define ITERATOR_SLOT_NEXT_INDEX 1 +#define ITERATOR_SLOT_ITEM_KIND 2 + +#define ITEM_KIND_KEY 0 +#define ITEM_KIND_VALUE 1 +#define ITEM_KIND_KEY_AND_VALUE 2 + #endif diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js index dd3b60ca8b3d..e1dc06f07b23 100644 --- a/js/src/builtin/String.js +++ b/js/src/builtin/String.js @@ -189,16 +189,13 @@ function String_repeat(count) { return T; } -#define STRING_ITERATOR_SLOT_ITERATED_STRING 0 -#define STRING_ITERATOR_SLOT_NEXT_INDEX 1 - // ES6 draft specification, section 21.1.3.27, version 2013-09-27. function String_iterator() { RequireObjectCoercible(this); var S = ToString(this); var iterator = NewStringIterator(); - UnsafeSetReservedSlot(iterator, STRING_ITERATOR_SLOT_ITERATED_STRING, S); - UnsafeSetReservedSlot(iterator, STRING_ITERATOR_SLOT_NEXT_INDEX, 0); + UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_TARGET, S); + UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, 0); return iterator; } @@ -212,11 +209,11 @@ function StringIteratorNext() { "StringIteratorNext"); } - var S = UnsafeGetStringFromReservedSlot(this, STRING_ITERATOR_SLOT_ITERATED_STRING); + var S = UnsafeGetStringFromReservedSlot(this, ITERATOR_SLOT_TARGET); // We know that JSString::MAX_LENGTH <= INT32_MAX (and assert this in // SelfHostring.cpp) so our current index can never be anything other than // an Int32Value. - var index = UnsafeGetInt32FromReservedSlot(this, STRING_ITERATOR_SLOT_NEXT_INDEX); + var index = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); var size = S.length; var result = { value: undefined, done: false }; @@ -234,7 +231,7 @@ function StringIteratorNext() { } } - UnsafeSetReservedSlot(this, STRING_ITERATOR_SLOT_NEXT_INDEX, index + charCount); + UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + charCount); result.value = callFunction(std_String_substring, S, index, index + charCount); return result; diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index 184855a315d0..fc97461a4e1d 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -44,6 +44,7 @@ var std_String_substring = String_substring; var std_WeakMap = WeakMap; // StopIteration is a bare constructor without properties or methods. var std_StopIteration = StopIteration; +var std_Map_iterator_next = MapIteratorNext; /********** List specification type **********/ diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 6abf8404db1d..56446de08ca7 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -224,6 +224,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineSubstringKernel(callInfo); if (native == intrinsic_IsArrayIterator) return inlineHasClass(callInfo, &ArrayIteratorObject::class_); + if (native == intrinsic_IsMapIterator) + return inlineHasClass(callInfo, &MapIteratorObject::class_); if (native == intrinsic_IsStringIterator) return inlineHasClass(callInfo, &StringIteratorObject::class_); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 1f58af95bd87..a4f051645cd2 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -769,6 +769,7 @@ bool intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_IsMapIterator(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsStringIterator(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsArrayBuffer(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index d36c7a443316..ac09c6695660 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -20,6 +20,7 @@ #include "selfhosted.out.h" #include "builtin/Intl.h" +#include "builtin/MapObject.h" #include "builtin/Object.h" #include "builtin/Reflect.h" #include "builtin/SelfHostingDefines.h" @@ -479,6 +480,32 @@ js::intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp) return true; } +bool +js::intrinsic_IsMapIterator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + args.rval().setBoolean(args[0].toObject().is()); + return true; +} + +bool +intrinsic_GetNextMapEntryForIterator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].toObject().is()); + MOZ_ASSERT(args[1].isObject()); + + Rooted mapIterator(cx, &args[0].toObject().as()); + RootedArrayObject result(cx, &args[1].toObject().as()); + + args.rval().setBoolean(MapIteratorObject::next(cx, mapIterator, result)); + return true; +} + static bool intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp) { @@ -1341,6 +1368,11 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("CallArrayIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2,0), + JS_FN("IsMapIterator", intrinsic_IsMapIterator, 1,0), + JS_FN("_GetNextMapEntryForIterator", intrinsic_GetNextMapEntryForIterator, 3,0), + JS_FN("CallMapIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod>, 2,0), + JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0), JS_FN("IsStringIterator", intrinsic_IsStringIterator, 1,0), From ccc7d405814ead3ba5b0d425f1b429a74eb24003 Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Fri, 31 Jul 2015 23:21:28 +0100 Subject: [PATCH 023/113] Bug 1187846 Stack layout doesn't honour min/max sizes for positioned elements r=Enn --- layout/xul/nsStackLayout.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/layout/xul/nsStackLayout.cpp b/layout/xul/nsStackLayout.cpp index b4f243cd3c6a..49a6904b216e 100644 --- a/layout/xul/nsStackLayout.cpp +++ b/layout/xul/nsStackLayout.cpp @@ -298,37 +298,39 @@ nsStackLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState) // // Margins on the child are also included in the edge offsets if (offsetSpecified) { + nsSize min = child->GetMinSize(aState); + nsSize max = child->GetMaxSize(aState); if (offsetSpecified & SPECIFIED_LEFT) { childRect.x = clientRect.x + offset.left + margin.left; if (offsetSpecified & SPECIFIED_RIGHT) { - nsSize min = child->GetMinSize(aState); - nsSize max = child->GetMaxSize(aState); nscoord width = clientRect.width - offset.LeftRight() - margin.LeftRight(); childRect.width = clamped(width, min.width, max.width); } else { - childRect.width = child->GetPrefSize(aState).width; + nscoord width = child->GetPrefSize(aState).width; + childRect.width = clamped(width, min.width, max.width); } } else if (offsetSpecified & SPECIFIED_RIGHT) { - childRect.width = child->GetPrefSize(aState).width; + nscoord width = child->GetPrefSize(aState).width; + childRect.width = clamped(width, min.width, max.width); childRect.x = clientRect.XMost() - offset.right - margin.right - childRect.width; } if (offsetSpecified & SPECIFIED_TOP) { childRect.y = clientRect.y + offset.top + margin.top; if (offsetSpecified & SPECIFIED_BOTTOM) { - nsSize min = child->GetMinSize(aState); - nsSize max = child->GetMaxSize(aState); nscoord height = clientRect.height - offset.TopBottom() - margin.TopBottom(); childRect.height = clamped(height, min.height, max.height); } else { - childRect.height = child->GetPrefSize(aState).height; + nscoord height = child->GetPrefSize(aState).height; + childRect.height = clamped(height, min.height, max.height); } } else if (offsetSpecified & SPECIFIED_BOTTOM) { - childRect.height = child->GetPrefSize(aState).height; + nscoord height = child->GetPrefSize(aState).height; + childRect.height = clamped(height, min.height, max.height); childRect.y = clientRect.YMost() - offset.bottom - margin.bottom - childRect.height; } } From 2f85d4858f7c236a8e3fa3d2b3bd3608193ae42b Mon Sep 17 00:00:00 2001 From: Jan Gerber Date: Fri, 31 Jul 2015 12:08:15 +0200 Subject: [PATCH 024/113] Bug 1187247: [MSE] P1. Continue parsing MediaSegment if buffer starts with SimpleBlock/Block. r=kinetik Apply to WebM streams. --- dom/media/mediasource/ContainerParser.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dom/media/mediasource/ContainerParser.cpp b/dom/media/mediasource/ContainerParser.cpp index bab0cfbc5d4e..395246b1a9a5 100644 --- a/dom/media/mediasource/ContainerParser.cpp +++ b/dom/media/mediasource/ContainerParser.cpp @@ -177,6 +177,16 @@ public: (*aData)[3] == 0x6b) { return true; } + // 0xa3 // SimpleBlock + if (aData->Length() >= 1 && + (*aData)[0] == 0xa3) { + return true; + } + // 0xa1 // Block + if (aData->Length() >= 1 && + (*aData)[0] == 0xa1) { + return true; + } return false; } From 15e7adedaf7e4985f5487153c54f20a9328e736a Mon Sep 17 00:00:00 2001 From: Jan Gerber Date: Tue, 21 Jul 2015 12:54:50 +0200 Subject: [PATCH 025/113] Bug 1187247: [MSE] P2. Enable WebM in MediaSource. r=jya --- dom/media/mediasource/MediaSource.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index 2d2a7e158b93..a2268d315db6 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -99,8 +99,7 @@ IsTypeSupported(const nsAString& aType) } return NS_OK; } else if (DecoderTraits::IsWebMType(mimeTypeUTF8)) { - if (!Preferences::GetBool("media.mediasource.webm.enabled", false) || - Preferences::GetBool("media.mediasource.format-reader", false)) { + if (!Preferences::GetBool("media.mediasource.webm.enabled", false)) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } if (hasCodecs && From 6560d4f70df6e3103af58bc64738e56d7d1a04c2 Mon Sep 17 00:00:00 2001 From: Drew Willcoxon Date: Fri, 31 Jul 2015 16:13:01 -0700 Subject: [PATCH 026/113] Bug 1188665 - Make nsIDOMWindowUtils.disableDialogs() disable onbeforeunload dialogs. r=bz --- layout/base/nsDocumentViewer.cpp | 16 +++--- layout/base/tests/browser.ini | 1 + .../browser_disableDialogs_onbeforeunload.js | 54 +++++++++++++++++++ 3 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 layout/base/tests/browser_disableDialogs_onbeforeunload.js diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 555a28033aa4..f650395f009c 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -17,7 +17,6 @@ #include "nsIDocumentViewerPrint.h" #include "nsIDOMBeforeUnloadEvent.h" #include "nsIDocument.h" -#include "nsIDOMWindowUtils.h" #include "nsPresContext.h" #include "nsIPresShell.h" #include "nsStyleSet.h" @@ -69,6 +68,7 @@ #include "nsIClipboardHelper.h" #include "nsPIDOMWindow.h" +#include "nsGlobalWindow.h" #include "nsDOMNavigationTiming.h" #include "nsPIWindowRoot.h" #include "nsJSEnvironment.h" @@ -1115,23 +1115,23 @@ nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow, // onbeforeunload event, don't let that happen. (see also bug#331040) nsRefPtr kungFuDeathGrip(this); + bool dialogsAreEnabled = false; { // Never permit popups from the beforeunload handler, no matter // how we get here. nsAutoPopupStatePusher popupStatePusher(openAbused, true); // Never permit dialogs from the beforeunload handler - nsCOMPtr utils = do_GetInterface(window); - bool dialogsWereEnabled = false; - utils->AreDialogsEnabled(&dialogsWereEnabled); - utils->DisableDialogs(); + nsGlobalWindow *globalWindow = static_cast(window); + dialogsAreEnabled = globalWindow->AreDialogsEnabled(); + globalWindow->DisableDialogs(); mInPermitUnload = true; EventDispatcher::DispatchDOMEvent(window, nullptr, event, mPresContext, nullptr); mInPermitUnload = false; - if (dialogsWereEnabled) { - utils->EnableDialogs(); + if (dialogsAreEnabled) { + globalWindow->EnableDialogs(); } } @@ -1139,7 +1139,7 @@ nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow, nsAutoString text; beforeUnload->GetReturnValue(text); - if (!sIsBeforeUnloadDisabled && *aShouldPrompt && + if (!sIsBeforeUnloadDisabled && *aShouldPrompt && dialogsAreEnabled && (event->GetInternalNSEvent()->mFlags.mDefaultPrevented || !text.IsEmpty())) { // Ask the user if it's ok to unload the current page diff --git a/layout/base/tests/browser.ini b/layout/base/tests/browser.ini index fb291a4860f5..802b7a6b8d3d 100644 --- a/layout/base/tests/browser.ini +++ b/layout/base/tests/browser.ini @@ -2,3 +2,4 @@ [browser_bug617076.js] skip-if = e10s # Bug ?????? - test touches content (TypeError: doc.documentElement is null) +[browser_disableDialogs_onbeforeunload.js] diff --git a/layout/base/tests/browser_disableDialogs_onbeforeunload.js b/layout/base/tests/browser_disableDialogs_onbeforeunload.js new file mode 100644 index 000000000000..8a2c12f0dab1 --- /dev/null +++ b/layout/base/tests/browser_disableDialogs_onbeforeunload.js @@ -0,0 +1,54 @@ +function pageScript() { + window.addEventListener("beforeunload", function (event) { + var str = "Some text that causes the beforeunload dialog to be shown"; + event.returnValue = str; + return str; + }, true); +} + +const PAGE_URL = + "data:text/html," + encodeURIComponent(""); + +add_task(function* enableDialogs() { + // The onbeforeunload dialog should appear. + let dialogShown = false; + function onDialogShown(node) { + dialogShown = true; + let dismissButton = node.ui.button0; + dismissButton.click(); + } + let obsName = "tabmodal-dialog-loaded"; + Services.obs.addObserver(onDialogShown, obsName, false); + yield openPage(true); + Services.obs.removeObserver(onDialogShown, obsName); + Assert.ok(dialogShown); +}); + +add_task(function* disableDialogs() { + // The onbeforeunload dialog should NOT appear. + yield openPage(false); + info("If we time out here, then the dialog was shown..."); +}); + +function* openPage(enableDialogs) { + // Open about:blank in a new tab. + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) { + // Load the content script in the frame. + let methodName = enableDialogs ? "enableDialogs" : "disableDialogs"; + yield ContentTask.spawn(browser, methodName, function* (name) { + Components.utils.import("resource://gre/modules/Services.jsm"); + Services.obs.addObserver(doc => { + if (content && doc == content.document) { + content.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils)[name](); + } + }, "document-element-inserted", false); + }); + // Load the page. + yield BrowserTestUtils.loadURI(browser, PAGE_URL); + yield BrowserTestUtils.browserLoaded(browser); + // And then navigate away. + yield BrowserTestUtils.loadURI(browser, "http://example.com/"); + yield BrowserTestUtils.browserLoaded(browser); + }); +} From f933dadde93648b1eede232666e690cd06650dbd Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Fri, 31 Jul 2015 16:29:50 -0700 Subject: [PATCH 027/113] Bug 1023344: Tweak asserts() annotation for crashtest 876074-1.html, to allow for occasional no-assertion & extra-assertion. --- layout/generic/crashtests/crashtests.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index c0bddd7f8601..eac821418e3f 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -540,7 +540,7 @@ load 862947-1.html load 863935.html needs-focus pref(accessibility.browsewithcaret,true) load 868906.html load 866547-1.html -asserts-if(Android,0-4) asserts-if(!Android,1-4) load 876074-1.html # bug 876749 +asserts(0-5) load 876074-1.html # bug 876749 load 885009-1.html load 893496-1.html load 893523.html From 39885629aeeeba53aef26415fe057b25a0a3e5e6 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 31 Jul 2015 06:52:13 +0900 Subject: [PATCH 028/113] Bug 1189526 - Add screentopng to mochitest test archive on Gtk+3 builds. r=mshal --- testing/mochitest/Makefile.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index d51f24820e51..df2442c558f0 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -45,9 +45,11 @@ ifeq ($(OS_ARCH),Linux) TEST_HARNESS_BINS += fix_linux_stack.py endif -ifeq (gtk2_1,$(MOZ_WIDGET_TOOLKIT)_$(MOZ_X11)) +ifdef MOZ_X11 +ifneq (,$(filter gtk%,$(MOZ_WIDGET_TOOLKIT))) TEST_HARNESS_BINS += screentopng endif +endif ifeq (windows,$(MOZ_WIDGET_TOOLKIT)) TEST_HARNESS_BINS += screenshot$(BIN_SUFFIX) From 3f28e2523e72c4c24cbe6459fd12755eb6515799 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 31 Jul 2015 17:03:37 -0700 Subject: [PATCH 029/113] Bug 1189964 - Fix a crash handling drags on dying windows. r=smaug --- dom/ipc/TabParent.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 3fbde67767cc..9c1212b04098 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -3237,9 +3237,15 @@ TabParent::RecvInvokeDragSession(nsTArray&& aTransfers, const int32_t& aDragAreaX, const int32_t& aDragAreaY) { mInitialDataTransferItems.Clear(); - nsPresContext* pc = mFrameElement->OwnerDoc()->GetShell()->GetPresContext(); - EventStateManager* esm = pc->EventStateManager(); + nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell(); + if (!shell) { + if (Manager()->IsContentParent()) { + Manager()->AsContentParent()->SendEndDragSession(true, true); + } + return true; + } + EventStateManager* esm = shell->GetPresContext()->EventStateManager(); for (uint32_t i = 0; i < aTransfers.Length(); ++i) { auto& items = aTransfers[i].items(); nsTArray* itemArray = mInitialDataTransferItems.AppendElement(); From b22c011fa845dc27c7f61533a00d2366485243f2 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 31 Jul 2015 17:03:37 -0700 Subject: [PATCH 030/113] Bug 940206 - Implement Navigator.registerContentHandler and Navigator.registerProtocolHandler for e10s. r=dao --- browser/base/content/test/general/browser.ini | 1 - .../components/feeds/WebContentConverter.js | 328 +++++++++++------- browser/components/feeds/test/mochitest.ini | 5 +- .../feeds/test/test_registerHandler.html | 46 +-- browser/components/nsBrowserGlue.js | 4 + .../privatebrowsing/test/browser/browser.ini | 1 - browser/modules/Feeds.jsm | 28 +- .../sidebar/nsIWebContentHandlerRegistrar.idl | 8 +- 8 files changed, 262 insertions(+), 159 deletions(-) diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 3fb0035bf3c8..f4320fdd7641 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -473,7 +473,6 @@ skip-if = buildapp == 'mulet' [browser_zbug569342.js] skip-if = e10s # Bug 1094240 - has findbar-related failures [browser_registerProtocolHandler_notification.js] -skip-if = e10s # Bug 940206 - nsIWebContentHandlerRegistrar::registerProtocolHandler doesn't work in e10s [browser_no_mcb_on_http_site.js] [browser_bug1104165-switchtab-decodeuri.js] [browser_bug1003461-switchtab-override.js] diff --git a/browser/components/feeds/WebContentConverter.js b/browser/components/feeds/WebContentConverter.js index 479a5fdf2923..d928ff5d353e 100644 --- a/browser/components/feeds/WebContentConverter.js +++ b/browser/components/feeds/WebContentConverter.js @@ -3,6 +3,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); @@ -132,6 +133,83 @@ ServiceInfo.prototype = { } }; +const Utils = { + makeURI(aURL, aOriginCharset, aBaseURI) { + return Services.io.newURI(aURL, aOriginCharset, aBaseURI); + }, + + checkAndGetURI(aURIString, aContentWindow) { + try { + let baseURI = aContentWindow.document.baseURIObject; + var uri = this.makeURI(aURIString, null, baseURI); + } catch (ex) { + throw NS_ERROR_DOM_SYNTAX_ERR; + } + + // For security reasons we reject non-http(s) urls (see bug 354316), + // we may need to revise this once we support more content types + // XXX this should be a "security exception" according to spec, but that + // isn't defined yet. + if (uri.scheme != "http" && uri.scheme != "https") + throw("Permission denied to add " + uri.spec + " as a content or protocol handler"); + + // We also reject handlers registered from a different host (see bug 402287) + // The pref allows us to test the feature + var pb = Services.prefs; + if ((!pb.prefHasUserValue(PREF_ALLOW_DIFFERENT_HOST) || + !pb.getBoolPref(PREF_ALLOW_DIFFERENT_HOST)) && + aContentWindow.location.hostname != uri.host) + throw("Permission denied to add " + uri.spec + " as a content or protocol handler"); + + // If the uri doesn't contain '%s', it won't be a good handler + if (uri.spec.indexOf("%s") < 0) + throw NS_ERROR_DOM_SYNTAX_ERR; + + return uri; + }, + + // NB: Throws if aProtocol is not allowed. + checkProtocolHandlerAllowed(aProtocol, aURIString) { + // First, check to make sure this isn't already handled internally (we don't + // want to let them take over, say "chrome"). + var handler = Services.io.getProtocolHandler(aProtocol); + if (!(handler instanceof Ci.nsIExternalProtocolHandler)) { + // This is handled internally, so we don't want them to register + // XXX this should be a "security exception" according to spec, but that + // isn't defined yet. + throw(`Permission denied to add ${aURIString} as a protocol handler`); + } + + // check if it is in the black list + var pb = Services.prefs; + var allowed; + try { + allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "." + aProtocol); + } + catch (e) { + allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "-default"); + } + if (!allowed) { + // XXX this should be a "security exception" according to spec + throw(`Not allowed to register a protocol handler for ${aProtocol}`); + } + }, + + /** + * Mappings from known feed types to our internal content type. + */ + _mappings: { + "application/rss+xml": TYPE_MAYBE_FEED, + "application/atom+xml": TYPE_MAYBE_FEED, + }, + + resolveContentType(aContentType) { + if (aContentType in this._mappings) + return this._mappings[aContentType]; + return aContentType; + } +}; + function WebContentConverterRegistrar() { this._contentTypes = { }; this._autoHandleContentTypes = { }; @@ -139,9 +217,7 @@ function WebContentConverterRegistrar() { WebContentConverterRegistrar.prototype = { get stringBundle() { - var sb = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(STRING_BUNDLE_URI); + var sb = Services.strings.createBundle(STRING_BUNDLE_URI); delete WebContentConverterRegistrar.prototype.stringBundle; return WebContentConverterRegistrar.prototype.stringBundle = sb; }, @@ -159,7 +235,7 @@ WebContentConverterRegistrar.prototype = { */ getAutoHandler: function WCCR_getAutoHandler(contentType) { - contentType = this._resolveContentType(contentType); + contentType = Utils.resolveContentType(contentType); if (contentType in this._autoHandleContentTypes) return this._autoHandleContentTypes[contentType]; return null; @@ -172,19 +248,17 @@ WebContentConverterRegistrar.prototype = { function WCCR_setAutoHandler(contentType, handler) { if (handler && !this._typeIsRegistered(contentType, handler.uri)) throw Cr.NS_ERROR_NOT_AVAILABLE; - - contentType = this._resolveContentType(contentType); + + contentType = Utils.resolveContentType(contentType); this._setAutoHandler(contentType, handler); - - var ps = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); + + var ps = Services.prefs; var autoBranch = ps.getBranch(PREF_CONTENTHANDLERS_AUTO); if (handler) autoBranch.setCharPref(contentType, handler.uri); else if (autoBranch.prefHasUserValue(contentType)) autoBranch.clearUserPref(contentType); - + ps.savePrefFile(null); }, @@ -218,7 +292,7 @@ WebContentConverterRegistrar.prototype = { loadPreferredHandler: function WCCR_loadPreferredHandler(request) { var channel = request.QueryInterface(Ci.nsIChannel); - var contentType = this._resolveContentType(channel.contentType); + var contentType = Utils.resolveContentType(channel.contentType); var handler = this.getAutoHandler(contentType); if (handler) { request.cancel(Cr.NS_ERROR_FAILURE); @@ -268,15 +342,7 @@ WebContentConverterRegistrar.prototype = { this._contentTypes[contentType].filter(notURI); } }, - - /** - * - */ - _mappings: { - "application/rss+xml": TYPE_MAYBE_FEED, - "application/atom+xml": TYPE_MAYBE_FEED, - }, - + /** * These are types for which there is a separate content converter aside * from our built in generic one. We should not automatically register @@ -285,57 +351,6 @@ WebContentConverterRegistrar.prototype = { _blockedTypes: { "application/vnd.mozilla.maybe.feed": true, }, - - /** - * Determines the "internal" content type based on the _mappings. - * @param contentType - * @returns The resolved contentType value. - */ - _resolveContentType: - function WCCR__resolveContentType(contentType) { - if (contentType in this._mappings) - return this._mappings[contentType]; - return contentType; - }, - - _makeURI: function(aURL, aOriginCharset, aBaseURI) { - var ioService = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - return ioService.newURI(aURL, aOriginCharset, aBaseURI); - }, - - _checkAndGetURI: - function WCCR_checkAndGetURI(aURIString, aContentWindow) - { - try { - let baseURI = aContentWindow.document.baseURIObject; - var uri = this._makeURI(aURIString, null, baseURI); - } catch (ex) { - // not supposed to throw according to spec - return; - } - - // For security reasons we reject non-http(s) urls (see bug 354316), - // we may need to revise this once we support more content types - // XXX this should be a "security exception" according to spec, but that - // isn't defined yet. - if (uri.scheme != "http" && uri.scheme != "https") - throw("Permission denied to add " + uri.spec + " as a content or protocol handler"); - - // We also reject handlers registered from a different host (see bug 402287) - // The pref allows us to test the feature - var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); - if ((!pb.prefHasUserValue(PREF_ALLOW_DIFFERENT_HOST) || - !pb.getBoolPref(PREF_ALLOW_DIFFERENT_HOST)) && - aContentWindow.location.hostname != uri.host) - throw("Permission denied to add " + uri.spec + " as a content or protocol handler"); - - // If the uri doesn't contain '%s', it won't be a good handler - if (uri.spec.indexOf("%s") < 0) - throw NS_ERROR_DOM_SYNTAX_ERR; - - return uri; - }, /** * Determines if a web handler is already registered. @@ -366,52 +381,41 @@ WebContentConverterRegistrar.prototype = { * See nsIWebContentHandlerRegistrar */ registerProtocolHandler: - function WCCR_registerProtocolHandler(aProtocol, aURIString, aTitle, aContentWindow) { + function WCCR_registerProtocolHandler(aProtocol, aURIString, aTitle, aBrowserOrWindow) { LOG("registerProtocolHandler(" + aProtocol + "," + aURIString + "," + aTitle + ")"); - - var uri = this._checkAndGetURI(aURIString, aContentWindow); + var haveWindow = (aBrowserOrWindow instanceof Ci.nsIDOMWindow); + var uri; + if (haveWindow) { + uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow); + } else { + // aURIString must not be a relative URI. + uri = Utils.makeURI(aURIString, null); + } // If the protocol handler is already registered, just return early. if (this._protocolHandlerRegistered(aProtocol, uri.spec)) { return; } - var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow); - if (PrivateBrowsingUtils.isWindowPrivate(browserWindow)) { + var browser; + if (haveWindow) { + let browserWindow = + this._getBrowserWindowForContentWindow(aBrowserOrWindow); + browser = this._getBrowserForContentWindow(browserWindow, + aBrowserOrWindow); + } else { + browser = aBrowserOrWindow; + } + if (PrivateBrowsingUtils.isBrowserPrivate(browser)) { // Inside the private browsing mode, we don't want to alert the user to save // a protocol handler. We log it to the error console so that web developers // would have some way to tell what's going wrong. - Cc["@mozilla.org/consoleservice;1"]. - getService(Ci.nsIConsoleService). + Services.console. logStringMessage("Web page denied access to register a protocol handler inside private browsing mode"); return; } - - // First, check to make sure this isn't already handled internally (we don't - // want to let them take over, say "chrome"). - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var handler = ios.getProtocolHandler(aProtocol); - if (!(handler instanceof Ci.nsIExternalProtocolHandler)) { - // This is handled internally, so we don't want them to register - // XXX this should be a "security exception" according to spec, but that - // isn't defined yet. - throw("Permission denied to add " + aURIString + "as a protocol handler"); - } - // check if it is in the black list - var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); - var allowed; - try { - allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "." + aProtocol); - } - catch (e) { - allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "-default"); - } - if (!allowed) { - // XXX this should be a "security exception" according to spec - throw("Not allowed to register a protocol handler for " + aProtocol); - } + Utils.checkProtocolHandlerAllowed(aProtocol, aURIString); // Now Ask the user and provide the proper callback var message = this._getFormattedString("addProtocolHandler", @@ -451,8 +455,7 @@ WebContentConverterRegistrar.prototype = { hs.store(handlerInfo); } }; - var browserElement = this._getBrowserForContentWindow(browserWindow, aContentWindow); - var notificationBox = browserWindow.gBrowser.getNotificationBox(browserElement); + var notificationBox = browser.getTabBrowser().getNotificationBox(browser); notificationBox.appendNotification(message, notificationValue, notificationIcon, @@ -465,23 +468,34 @@ WebContentConverterRegistrar.prototype = { * If a DOM window is provided, then the request came from content, so we * prompt the user to confirm the registration. */ - registerContentHandler: - function WCCR_registerContentHandler(aContentType, aURIString, aTitle, aContentWindow) { + registerContentHandler: + function WCCR_registerContentHandler(aContentType, aURIString, aTitle, aWindowOrBrowser) { LOG("registerContentHandler(" + aContentType + "," + aURIString + "," + aTitle + ")"); // We only support feed types at present. // XXX this should be a "security exception" according to spec, but that // isn't defined yet. - var contentType = this._resolveContentType(aContentType); + var contentType = Utils.resolveContentType(aContentType); if (contentType != TYPE_MAYBE_FEED) return; - if (aContentWindow) { - var uri = this._checkAndGetURI(aURIString, aContentWindow); - - var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow); - var browserElement = this._getBrowserForContentWindow(browserWindow, aContentWindow); - var notificationBox = browserWindow.gBrowser.getNotificationBox(browserElement); + if (aWindowOrBrowser) { + var haveWindow = (aWindowOrBrowser instanceof Ci.nsIDOMWindow); + var uri; + var notificationBox; + if (haveWindow) { + uri = Utils.checkAndGetURI(aURIString, aWindowOrBrowser); + + var browserWindow = this._getBrowserWindowForContentWindow(aWindowOrBrowser); + var browserElement = this._getBrowserForContentWindow(browserWindow, aWindowOrBrowser); + notificationBox = browserElement.getTabBrowser().getNotificationBox(browserElement); + } else { + // uri was vetted in the content process. + uri = Utils.makeURI(aURIString, null); + notificationBox = aWindowOrBrowser.getTabBrowser() + .getNotificationBox(aWindowOrBrowser); + } + this._appendFeedReaderNotification(uri, aTitle, notificationBox); } else @@ -612,9 +626,7 @@ WebContentConverterRegistrar.prototype = { */ _saveContentHandlerToPrefs: function WCCR__saveContentHandlerToPrefs(contentType, uri, title) { - var ps = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); + var ps = Services.prefs; var i = 0; var typeBranch = null; while (true) { @@ -694,8 +706,7 @@ WebContentConverterRegistrar.prototype = { if (contentType == TYPE_MAYBE_FEED) { // Make the new handler the last-selected reader in the preview page // and make sure the preview page is shown the next time a feed is visited - var pb = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService).getBranch(null); + var pb = Services.prefs.getBranch(null); pb.setCharPref(PREF_SELECTED_READER, "web"); var supportsString = @@ -800,9 +811,7 @@ WebContentConverterRegistrar.prototype = { * preferences. */ _init: function WCCR__init() { - var ps = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); + var ps = Services.prefs; var kids = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH) .getChildList(""); @@ -850,9 +859,7 @@ WebContentConverterRegistrar.prototype = { * See nsIObserver */ observe: function WCCR_observe(subject, topic, data) { - var os = - Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); + var os = Services.obs; switch (topic) { case "app-startup": os.addObserver(this, "browser-ui-startup-complete", false); @@ -890,4 +897,71 @@ WebContentConverterRegistrar.prototype = { }] }; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrar]); +function WebContentConverterRegistrarContent() { +} + +WebContentConverterRegistrarContent.prototype = { + /** + * See nsIWebContentHandlerRegistrar + */ + registerContentHandler(aContentType, aURIString, aTitle, aBrowserOrWindow) { + // aBrowserOrWindow must be a window. + let messageManager = aBrowserOrWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsITabChild) + .messageManager; + + let uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow); + if (Utils.resolveContentType(aContentType) != TYPE_MAYBE_FEED) { + return; + } + + messageManager.sendAsyncMessage("WCCR:registerContentHandler", + { contentType: aContentType, + uri: uri.spec, + title: aTitle }); + }, + + registerProtocolHandler(aProtocol, aURIString, aTitle, aBrowserOrWindow) { + // aBrowserOrWindow must be a window. + let messageManager = aBrowserOrWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsITabChild) + .messageManager; + + let uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow); + Utils.checkProtocolHandlerAllowed(aProtocol, aURIString); + + messageManager.sendAsyncMessage("WCCR:registerProtocolHandler", + { protocol: aProtocol, + uri: uri.spec, + title: aTitle }); + }, + + /** + * See nsIFactory + */ + createInstance: function WCCR_createInstance(outer, iid) { + if (outer != null) + throw Cr.NS_ERROR_NO_AGGREGATION; + return this.QueryInterface(iid); + }, + + classID: WCCR_CLASSID, + + /** + * See nsISupports + */ + QueryInterface: XPCOMUtils.generateQI( + [Ci.nsIWebContentHandlerRegistrar, + Ci.nsIFactory]) +}; + +this.NSGetFactory = + (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) ? + XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrarContent]) : + XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrar]); diff --git a/browser/components/feeds/test/mochitest.ini b/browser/components/feeds/test/mochitest.ini index 5737d31c81c4..e8d89d35b663 100644 --- a/browser/components/feeds/test/mochitest.ini +++ b/browser/components/feeds/test/mochitest.ini @@ -1,5 +1,5 @@ [DEFAULT] -skip-if = buildapp == 'mulet' || buildapp == 'b2g' || e10s +skip-if = buildapp == 'mulet' || buildapp == 'b2g' support-files = bug368464-data.xml bug408328-data.xml @@ -10,6 +10,9 @@ support-files = valid-unsniffable-feed.xml [test_bug436801.html] +skip-if = e10s [test_bug494328.html] +skip-if = e10s [test_bug589543.html] +skip-if = e10s [test_registerHandler.html] diff --git a/browser/components/feeds/test/test_registerHandler.html b/browser/components/feeds/test/test_registerHandler.html index 0d7688022384..5241caf0e5d6 100644 --- a/browser/components/feeds/test/test_registerHandler.html +++ b/browser/components/feeds/test/test_registerHandler.html @@ -39,45 +39,45 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=402788 ok(navigator.registerContentHandler, "navigator.registerContentHandler should be defined"); // testing a generic case - is(true, testRegisterHandler(true, "foo", "http://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler should work"); - is(true, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler should work"); + is(testRegisterHandler(true, "foo", "http://mochi.test:8888/%s", "Foo handler"), true, "registering a foo protocol handler should work"); + is(testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/%s", "Foo handler"), true, "registering a foo content handler should work"); // testing with wrong uris - is(false, testRegisterHandler(true, "foo", "http://mochi.test:8888/", "Foo handler"), "a protocol handler uri should contain %s"); - is(false, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/", "Foo handler"), "a content handler uri should contain %s"); + is(testRegisterHandler(true, "foo", "http://mochi.test:8888/", "Foo handler"), false, "a protocol handler uri should contain %s"); + is(testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/", "Foo handler"), false, "a content handler uri should contain %s"); // the spec explicitly allows relative urls to be passed - is(true, testRegisterHandler(true, "foo", "foo/%s", "Foo handler"), "a protocol handler uri should be valid"); - is(true, testRegisterHandler(false, "application/rss+xml", "foo/%s", "Foo handler"), "a content handler uri should be valid"); + is(testRegisterHandler(true, "foo", "foo/%s", "Foo handler"), true, "a protocol handler uri should be valid"); + is(testRegisterHandler(false, "application/rss+xml", "foo/%s", "Foo handler"), true, "a content handler uri should be valid"); // we should only accept to register when the handler has the same host as the current page (bug 402287) - is(false, testRegisterHandler(true, "foo", "http://remotehost:8888/%s", "Foo handler"), "registering a foo protocol handler with a different host should not work"); - is(false, testRegisterHandler(false, "application/rss+xml", "http://remotehost:8888/%s", "Foo handler"), "registering a foo content handler with a different host should not work"); + is(testRegisterHandler(true, "foo", "http://remotehost:8888/%s", "Foo handler"), false, "registering a foo protocol handler with a different host should not work"); + is(testRegisterHandler(false, "application/rss+xml", "http://remotehost:8888/%s", "Foo handler"), false, "registering a foo content handler with a different host should not work"); // restriction to http(s) for the uri of the handler (bug 401343) // https should work (http already tested in the generic case) - is(true, testRegisterHandler(true, "foo", "https://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with https scheme should work"); - is(true, testRegisterHandler(false, "application/rss+xml", "https://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with https scheme should work"); + is(testRegisterHandler(true, "foo", "https://mochi.test:8888/%s", "Foo handler"), true, "registering a foo protocol handler with https scheme should work"); + is(testRegisterHandler(false, "application/rss+xml", "https://mochi.test:8888/%s", "Foo handler"), true, "registering a foo content handler with https scheme should work"); // ftp should not work - is(false, testRegisterHandler(true, "foo", "ftp://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with ftp scheme should not work"); - is(false, testRegisterHandler(false, "application/rss+xml", "ftp://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with ftp scheme should not work"); + is(testRegisterHandler(true, "foo", "ftp://mochi.test:8888/%s", "Foo handler"), false, "registering a foo protocol handler with ftp scheme should not work"); + is(testRegisterHandler(false, "application/rss+xml", "ftp://mochi.test:8888/%s", "Foo handler"), false, "registering a foo content handler with ftp scheme should not work"); // chrome should not work - is(false, testRegisterHandler(true, "foo", "chrome://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with chrome scheme should not work"); - is(false, testRegisterHandler(false, "application/rss+xml", "chrome://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with chrome scheme should not work"); + is(testRegisterHandler(true, "foo", "chrome://mochi.test:8888/%s", "Foo handler"), false, "registering a foo protocol handler with chrome scheme should not work"); + is(testRegisterHandler(false, "application/rss+xml", "chrome://mochi.test:8888/%s", "Foo handler"), false, "registering a foo content handler with chrome scheme should not work"); // foo should not work - is(false, testRegisterHandler(true, "foo", "foo://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with foo scheme should not work"); - is(false, testRegisterHandler(false, "application/rss+xml", "foo://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with foo scheme should not work"); + is(testRegisterHandler(true, "foo", "foo://mochi.test:8888/%s", "Foo handler"), false, "registering a foo protocol handler with foo scheme should not work"); + is(testRegisterHandler(false, "application/rss+xml", "foo://mochi.test:8888/%s", "Foo handler"), false, "registering a foo content handler with foo scheme should not work"); // for security reasons, protocol handlers should never be registered for some schemes (chrome, vbscript, ...) (bug 402788) - is(false, testRegisterHandler(true, "chrome", "http://mochi.test:8888/%s", "chrome handler"), "registering a chrome protocol handler should not work"); - is(false, testRegisterHandler(true, "vbscript", "http://mochi.test:8888/%s", "vbscript handler"), "registering a vbscript protocol handler should not work"); - is(false, testRegisterHandler(true, "javascript", "http://mochi.test:8888/%s", "javascript handler"), "registering a javascript protocol handler should not work"); - is(false, testRegisterHandler(true, "moz-icon", "http://mochi.test:8888/%s", "moz-icon handler"), "registering a moz-icon protocol handler should not work"); + is(testRegisterHandler(true, "chrome", "http://mochi.test:8888/%s", "chrome handler"), false, "registering a chrome protocol handler should not work"); + is(testRegisterHandler(true, "vbscript", "http://mochi.test:8888/%s", "vbscript handler"), false, "registering a vbscript protocol handler should not work"); + is(testRegisterHandler(true, "javascript", "http://mochi.test:8888/%s", "javascript handler"), false, "registering a javascript protocol handler should not work"); + is(testRegisterHandler(true, "moz-icon", "http://mochi.test:8888/%s", "moz-icon handler"), false, "registering a moz-icon protocol handler should not work"); // for security reasons, content handlers should never be registered for some types (html, ...) - is(true, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/%s", "Foo handler"), "registering rss content handlers should work"); - is(true, testRegisterHandler(false, "application/atom+xml", "http://mochi.test:8888/%s", "Foo handler"), "registering atom content handlers should work"); - todo(false, testRegisterHandler(false, "text/html", "http://mochi.test:8888/%s", "Foo handler"), "registering html content handlers should not work"); // bug 403798 + is(testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/%s", "Foo handler"), true, "registering rss content handlers should work"); + is(testRegisterHandler(false, "application/atom+xml", "http://mochi.test:8888/%s", "Foo handler"), true, "registering atom content handlers should work"); + todo_is(testRegisterHandler(false, "text/html", "http://mochi.test:8888/%s", "Foo handler"), false, "registering html content handlers should not work"); // bug 403798 diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 0dde37130e41..aad0240f2252 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -94,6 +94,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "RemotePrompt", XPCOMUtils.defineLazyModuleGetter(this, "ContentPrefServiceParent", "resource://gre/modules/ContentPrefServiceParent.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Feeds", + "resource:///modules/Feeds.jsm"); + XPCOMUtils.defineLazyModuleGetter(this, "SelfSupportBackend", "resource:///modules/SelfSupportBackend.jsm"); @@ -762,6 +765,7 @@ BrowserGlue.prototype = { ContentClick.init(); RemotePrompt.init(); + Feeds.init(); ContentPrefServiceParent.init(); LoginManagerParent.init(); diff --git a/browser/components/privatebrowsing/test/browser/browser.ini b/browser/components/privatebrowsing/test/browser/browser.ini index 4004c622beb8..187ec5c5ac33 100644 --- a/browser/components/privatebrowsing/test/browser/browser.ini +++ b/browser/components/privatebrowsing/test/browser/browser.ini @@ -38,7 +38,6 @@ support-files = [browser_privatebrowsing_placestitle.js] [browser_privatebrowsing_popupblocker.js] [browser_privatebrowsing_protocolhandler.js] -skip-if = e10s # Bug 940206 - nsIWebContentHandlerRegistrar::registerProtocolHandler doesn't work in e10s [browser_privatebrowsing_sidebar.js] [browser_privatebrowsing_theming.js] [browser_privatebrowsing_ui.js] diff --git a/browser/modules/Feeds.jsm b/browser/modules/Feeds.jsm index e9fcedf44cfb..b2bdbe0422b9 100644 --- a/browser/modules/Feeds.jsm +++ b/browser/modules/Feeds.jsm @@ -12,9 +12,35 @@ Components.utils.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"); -const Ci = Components.interfaces; +const { interfaces: Ci, classes: Cc } = Components; this.Feeds = { + init() { + let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager); + mm.addMessageListener("WCCR:registerProtocolHandler", this); + }, + + receiveMessage(aMessage) { + switch (aMessage.name) { + case "WCCR:registerProtocolHandler": { + let data = aMessage.data; + let registrar = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]. + getService(Ci.nsIWebContentHandlerRegistrar); + registrar.registerProtocolHandler(data.protocol, data.uri, data.title, + aMessage.target); + break; + } + + case "WCCR:registerContentHandler": { + let data = aMessage.data; + let registrar = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]. + getService(Ci.nsIWebContentHandlerRegistrar); + registrar.registerContentHandler(data.contentType, data.uri, data.title, + aMessage.target); + break; + } + } + }, /** * isValidFeed: checks whether the given data represents a valid feed. diff --git a/dom/interfaces/sidebar/nsIWebContentHandlerRegistrar.idl b/dom/interfaces/sidebar/nsIWebContentHandlerRegistrar.idl index 6ad589205bd1..4cc571b4ec2b 100644 --- a/dom/interfaces/sidebar/nsIWebContentHandlerRegistrar.idl +++ b/dom/interfaces/sidebar/nsIWebContentHandlerRegistrar.idl @@ -6,8 +6,6 @@ #include "nsISupports.idl" -interface nsIDOMWindow; - /** * nsIWebContentHandlerRegistrar * @@ -19,7 +17,7 @@ interface nsIDOMWindow; * can invoke it. */ -[scriptable, uuid(e6a75410-c93e-42bf-84ca-a5c3ec34a2f1)] +[scriptable, uuid(65a3fafd-0e4a-4b06-8b4e-6a611da63d98)] interface nsIWebContentHandlerRegistrar : nsISupports { /** @@ -30,11 +28,11 @@ interface nsIWebContentHandlerRegistrar : nsISupports void registerContentHandler(in DOMString mimeType, in DOMString uri, in DOMString title, - in nsIDOMWindow contentWindow); + in nsISupports windowOrBrowser); void registerProtocolHandler(in DOMString protocol, in DOMString uri, in DOMString title, - in nsIDOMWindow contentWindow); + in nsISupports windowOrBrowser); }; %{ C++ From 59fc46007319d32ccb6abccaca35e5a92337aefa Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 31 Jul 2015 17:09:28 -0700 Subject: [PATCH 031/113] Bug 1189964 - Fix bustage. r=bustage CLOSED TREE --HG-- extra : amend_source : c36fd335a82e19124f639c572b2348e612d104a1 --- dom/ipc/TabParent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 9c1212b04098..3971f04c1469 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -3240,7 +3240,7 @@ TabParent::RecvInvokeDragSession(nsTArray&& aTransfers, nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell(); if (!shell) { if (Manager()->IsContentParent()) { - Manager()->AsContentParent()->SendEndDragSession(true, true); + unused << Manager()->AsContentParent()->SendEndDragSession(true, true); } return true; } From 7624cf6395ad448a65522e097e1e5e11edd81808 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sat, 1 Aug 2015 10:49:19 +1000 Subject: [PATCH 032/113] Bug 1164725 - Convert flags in nsIDocument into bit fields. r=smaug --HG-- extra : source : 49e416beae3c398890d13eb8690b9fa90ed79135 --- dom/base/nsIDocument.h | 96 +++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 6f0a747d2f06..c52934110d3a 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2694,136 +2694,128 @@ protected: mozilla::dom::VisibilityState mVisibilityState; // True if BIDI is enabled. - bool mBidiEnabled; + bool mBidiEnabled : 1; // True if a MathML element has ever been owned by this document. - bool mMathMLEnabled; + bool mMathMLEnabled : 1; // True if this document is the initial document for a window. This should // basically be true only for documents that exist in newly-opened windows or // documents created to satisfy a GetDocument() on a window when there's no // document in it. - bool mIsInitialDocumentInWindow; + bool mIsInitialDocumentInWindow : 1; // True if we're loaded as data and therefor has any dangerous stuff, such // as scripts and plugins, disabled. - bool mLoadedAsData; + bool mLoadedAsData : 1; // This flag is only set in XMLDocument, for e.g. documents used in XBL. We // don't want animations to play in such documents, so we need to store the // flag here so that we can check it in nsDocument::GetAnimationController. - bool mLoadedAsInteractiveData; + bool mLoadedAsInteractiveData : 1; // If true, whoever is creating the document has gotten it to the // point where it's safe to start layout on it. - bool mMayStartLayout; + bool mMayStartLayout : 1; // True iff we've ever fired a DOMTitleChanged event for this document - bool mHaveFiredTitleChange; + bool mHaveFiredTitleChange : 1; // True iff IsShowing() should be returning true - bool mIsShowing; + bool mIsShowing : 1; // True iff the document "page" is not hidden (i.e. currently in the // bfcache) - bool mVisible; + bool mVisible : 1; // True if our content viewer has been removed from the docshell // (it may still be displayed, but in zombie state). Form control data // has been saved. - bool mRemovedFromDocShell; + bool mRemovedFromDocShell : 1; // True iff DNS prefetch is allowed for this document. Note that if the // document has no window, DNS prefetch won't be performed no matter what. - bool mAllowDNSPrefetch; + bool mAllowDNSPrefetch : 1; // True when this document is a static clone of a normal document - bool mIsStaticDocument; + bool mIsStaticDocument : 1; // True while this document is being cloned to a static document. - bool mCreatingStaticClone; + bool mCreatingStaticClone : 1; // True iff the document is being unlinked or deleted. - bool mInUnlinkOrDeletion; + bool mInUnlinkOrDeletion : 1; // True if document has ever had script handling object. - bool mHasHadScriptHandlingObject; + bool mHasHadScriptHandlingObject : 1; // True if we're an SVG document being used as an image. - bool mIsBeingUsedAsImage; + bool mIsBeingUsedAsImage : 1; // True is this document is synthetic : stand alone image, video, audio // file, etc. - bool mIsSyntheticDocument; + bool mIsSyntheticDocument : 1; // True if this document has links whose state needs updating - bool mHasLinksToUpdate; + bool mHasLinksToUpdate : 1; // True if a layout flush might not be a no-op - bool mNeedLayoutFlush; + bool mNeedLayoutFlush : 1; // True if a style flush might not be a no-op - bool mNeedStyleFlush; + bool mNeedStyleFlush : 1; // True if a DOMMutationObserver is perhaps attached to a node in the document. - bool mMayHaveDOMMutationObservers; + bool mMayHaveDOMMutationObservers : 1; // True if an nsIAnimationObserver is perhaps attached to a node in the document. - bool mMayHaveAnimationObservers; + bool mMayHaveAnimationObservers : 1; // True if a document has loaded Mixed Active Script (see nsMixedContentBlocker.cpp) - bool mHasMixedActiveContentLoaded; + bool mHasMixedActiveContentLoaded : 1; // True if a document has blocked Mixed Active Script (see nsMixedContentBlocker.cpp) - bool mHasMixedActiveContentBlocked; + bool mHasMixedActiveContentBlocked : 1; // True if a document has loaded Mixed Display/Passive Content (see nsMixedContentBlocker.cpp) - bool mHasMixedDisplayContentLoaded; + bool mHasMixedDisplayContentLoaded : 1; // True if a document has blocked Mixed Display/Passive Content (see nsMixedContentBlocker.cpp) - bool mHasMixedDisplayContentBlocked; + bool mHasMixedDisplayContentBlocked : 1; // True if a document has blocked Tracking Content - bool mHasTrackingContentBlocked; + bool mHasTrackingContentBlocked : 1; // True if a document has loaded Tracking Content - bool mHasTrackingContentLoaded; + bool mHasTrackingContentLoaded : 1; // True if DisallowBFCaching has been called on this document. - bool mBFCacheDisallowed; + bool mBFCacheDisallowed : 1; // If true, we have an input encoding. If this is false, then the // document was created entirely in memory - bool mHaveInputEncoding; + bool mHaveInputEncoding : 1; - bool mHasHadDefaultView; + bool mHasHadDefaultView : 1; // Whether style sheet change events will be dispatched for this document - bool mStyleSheetChangeEventsEnabled; + bool mStyleSheetChangeEventsEnabled : 1; // Whether the document was created by a srcdoc iframe. - bool mIsSrcdocDocument; + bool mIsSrcdocDocument : 1; // Records whether we've done a document.open. If this is true, it's possible // for nodes from this document to have outdated wrappers in their wrapper // caches. - bool mDidDocumentOpen; - -#ifdef DEBUG - /** - * This is true while FlushPendingLinkUpdates executes. Calls to - * [Un]RegisterPendingLinkUpdate will assert when this is true. - */ - bool mIsLinkUpdateRegistrationsForbidden; -#endif + bool mDidDocumentOpen : 1; // Is the current mFontFaceSet valid? - bool mFontFaceSetDirty; + bool mFontFaceSetDirty : 1; // Has GetUserFontSet() been called? - bool mGetUserFontSetCalled; + bool mGetUserFontSetCalled : 1; // Do we currently have an event posted to call FlushUserFontSet? - bool mPostedFlushUserFontSet; + bool mPostedFlushUserFontSet : 1; enum Type { eUnknown, // should never be used @@ -2834,17 +2826,25 @@ protected: eXUL }; - uint8_t mType; + Type mType; uint8_t mDefaultElementType; - enum { + enum Tri { eTriUnset = 0, eTriFalse, eTriTrue }; - uint8_t mAllowXULXBL; + Tri mAllowXULXBL; + +#ifdef DEBUG + /** + * This is true while FlushPendingLinkUpdates executes. Calls to + * [Un]RegisterPendingLinkUpdate will assert when this is true. + */ + bool mIsLinkUpdateRegistrationsForbidden; +#endif // The document's script global object, the object from which the // document can get its script context and scope. This is the From 13a6670d6dab02fcc72eb480dc6364c8a09fb260 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sat, 1 Aug 2015 10:49:19 +1000 Subject: [PATCH 033/113] Bug 1173930 - Not invoke FullscreenChange callback on OS X if state is not changed. r=smichaud --HG-- extra : source : 7d61c973225d4f65b4bb7e3c0eec584b98f16bf5 --- widget/cocoa/nsCocoaWindow.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index ca22ad837064..e3220ea00054 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -1354,12 +1354,13 @@ nsCocoaWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage, void nsCocoaWindow::EnteredFullScreen(bool aFullScreen, bool aNativeMode) { mInFullScreenTransition = false; + bool wasInFullscreen = mInFullScreenMode; mInFullScreenMode = aFullScreen; if (aNativeMode || mInNativeFullScreenMode) { mInNativeFullScreenMode = aFullScreen; } DispatchSizeModeEvent(); - if (mWidgetListener) { + if (mWidgetListener && wasInFullscreen != aFullScreen) { mWidgetListener->FullscreenChanged(aFullScreen); } } From c17342d0b405f480155682ce0a9c4784290355ac Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 18:10:23 -0700 Subject: [PATCH 034/113] Bug 1187546 - Make it possible to ask image decoders to only decode the first frame. r=tn --- image/Decoder.cpp | 1 + image/Decoder.h | 13 +++++++++++++ image/decoders/nsGIFDecoder2.cpp | 7 +++++++ image/decoders/nsICODecoder.cpp | 6 ++++++ image/decoders/nsPNGDecoder.cpp | 11 ++++++++++- image/decoders/nsPNGDecoder.h | 1 + 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/image/Decoder.cpp b/image/Decoder.cpp index 3344a52819b5..1b1aff691738 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -38,6 +38,7 @@ Decoder::Decoder(RasterImage* aImage) , mSendPartialInvalidations(false) , mImageIsTransient(false) , mImageIsLocked(false) + , mFirstFrameDecode(false) , mInFrame(false) , mIsAnimated(false) , mDataDone(false) diff --git a/image/Decoder.h b/image/Decoder.h index 49fe02553b09..11757f1a7343 100644 --- a/image/Decoder.h +++ b/image/Decoder.h @@ -194,6 +194,18 @@ public: bool ImageIsLocked() const { return mImageIsLocked; } + + /** + * Set whether we should stop decoding after the first frame. + */ + void SetIsFirstFrameDecode() + { + MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet"); + mFirstFrameDecode = true; + } + + bool IsFirstFrameDecode() const { return mFirstFrameDecode; } + size_t BytesDecoded() const { return mBytesDecoded; } // The amount of time we've spent inside Write() so far for this decoder. @@ -443,6 +455,7 @@ private: bool mSendPartialInvalidations : 1; bool mImageIsTransient : 1; bool mImageIsLocked : 1; + bool mFirstFrameDecode : 1; bool mInFrame : 1; bool mIsAnimated : 1; bool mDataDone : 1; diff --git a/image/decoders/nsGIFDecoder2.cpp b/image/decoders/nsGIFDecoder2.cpp index 9773c8a14a2f..c87c60e74e01 100644 --- a/image/decoders/nsGIFDecoder2.cpp +++ b/image/decoders/nsGIFDecoder2.cpp @@ -906,6 +906,13 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount) break; case gif_image_header: { + if (mGIFStruct.images_decoded > 0 && IsFirstFrameDecode()) { + // We're about to get a second frame, but we only want the first. Stop + // decoding now. + mGIFStruct.state = gif_done; + break; + } + // Get image offsets, with respect to the screen origin mGIFStruct.x_offset = GETINT16(q); mGIFStruct.y_offset = GETINT16(q + 2); diff --git a/image/decoders/nsICODecoder.cpp b/image/decoders/nsICODecoder.cpp index 6c87e64a5efa..643a09b54a2f 100644 --- a/image/decoders/nsICODecoder.cpp +++ b/image/decoders/nsICODecoder.cpp @@ -361,6 +361,9 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount) mContainedDecoder = new nsPNGDecoder(mImage); mContainedDecoder->SetMetadataDecode(IsMetadataDecode()); mContainedDecoder->SetSendPartialInvalidations(mSendPartialInvalidations); + if (mFirstFrameDecode) { + mContainedDecoder->SetIsFirstFrameDecode(); + } mContainedDecoder->Init(); if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE)) { return; @@ -438,6 +441,9 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount) bmpDecoder->SetUseAlphaData(true); mContainedDecoder->SetMetadataDecode(IsMetadataDecode()); mContainedDecoder->SetSendPartialInvalidations(mSendPartialInvalidations); + if (mFirstFrameDecode) { + mContainedDecoder->SetIsFirstFrameDecode(); + } mContainedDecoder->Init(); // The ICO format when containing a BMP does not include the 14 byte diff --git a/image/decoders/nsPNGDecoder.cpp b/image/decoders/nsPNGDecoder.cpp index c6da55488624..13c814a641cf 100644 --- a/image/decoders/nsPNGDecoder.cpp +++ b/image/decoders/nsPNGDecoder.cpp @@ -115,6 +115,7 @@ nsPNGDecoder::nsPNGDecoder(RasterImage* aImage) mHeaderBytesRead(0), mCMSMode(0), mChannels(0), mFrameIsHidden(false), mDisablePremultipliedAlpha(false), + mSuccessfulEarlyFinish(false), mNumFrames(0) { } @@ -375,7 +376,7 @@ nsPNGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount) // We might not really know what caused the error, but it makes more // sense to blame the data. - if (!HasError()) { + if (!mSuccessfulEarlyFinish && !HasError()) { PostDataError(); } @@ -827,6 +828,14 @@ nsPNGDecoder::frame_info_callback(png_structp png_ptr, png_uint_32 frame_num) // old frame is done decoder->EndImageFrame(); + if (!decoder->mFrameIsHidden && decoder->IsFirstFrameDecode()) { + // We're about to get a second non-hidden frame, but we only want the first. + // Stop decoding now. + decoder->PostDecodeDone(); + decoder->mSuccessfulEarlyFinish = true; + png_longjmp(decoder->mPNG, 1); + } + // Only the first frame can be hidden, so unhide unconditionally here. decoder->mFrameIsHidden = false; diff --git a/image/decoders/nsPNGDecoder.h b/image/decoders/nsPNGDecoder.h index 01867b903170..af8fdf365d4a 100644 --- a/image/decoders/nsPNGDecoder.h +++ b/image/decoders/nsPNGDecoder.h @@ -95,6 +95,7 @@ public: uint8_t mChannels; bool mFrameIsHidden; bool mDisablePremultipliedAlpha; + bool mSuccessfulEarlyFinish; struct AnimFrameInfo { From ecb9be44a0a7d2550906817f524bf2f0d92cb7d5 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 18:10:26 -0700 Subject: [PATCH 035/113] No bug - Remove obsolete comment in SourceBuffer.h. r=me --- image/SourceBuffer.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/image/SourceBuffer.h b/image/SourceBuffer.h index 07b217c594a9..0e3cb9d5c2de 100644 --- a/image/SourceBuffer.h +++ b/image/SourceBuffer.h @@ -207,12 +207,6 @@ private: * keep a list of consumers which are waiting for new data, and to resume them * when the producer appends more. All consumers must implement the IResumable * interface to make this possible. - * - * XXX(seth): We should add support for compacting a SourceBuffer. To do this, - * we need to have SourceBuffer keep track of how many live - * SourceBufferIterator's point to it. When the SourceBuffer is complete and no - * live SourceBufferIterator's for it remain, we can compact its contents into a - * single chunk. */ class SourceBuffer final { From edf105e8f66466a8c7cbd9f8dc17fe9abd3f3aea Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 18:10:29 -0700 Subject: [PATCH 036/113] Bug 1181863 (Part 1) - Add support for reading from nsIInputStreams directly to SourceBuffer. r=tn --- image/RasterImage.cpp | 48 ++++++------------------------------------ image/RasterImage.h | 6 ------ image/SourceBuffer.cpp | 41 ++++++++++++++++++++++++++++++++++++ image/SourceBuffer.h | 5 +++++ 4 files changed, 53 insertions(+), 47 deletions(-) diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index 7efd2f2e61c1..62e644e6851a 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -1188,20 +1188,16 @@ RasterImage::NotifyForLoadEvent(Progress aProgress) nsresult RasterImage::OnImageDataAvailable(nsIRequest*, nsISupports*, - nsIInputStream* aInStr, - uint64_t aOffset, + nsIInputStream* aInputStream, + uint64_t, uint32_t aCount) { - nsresult rv; + nsresult rv = mSourceBuffer->AppendFromInputStream(aInputStream, aCount); + MOZ_ASSERT(rv == NS_OK || rv == NS_ERROR_OUT_OF_MEMORY); - // WriteToSourceBuffer always consumes everything it gets if it doesn't run - // out of memory. - uint32_t bytesRead; - rv = aInStr->ReadSegments(WriteToSourceBuffer, this, aCount, &bytesRead); - - MOZ_ASSERT(bytesRead == aCount || HasError() || NS_FAILED(rv), - "WriteToSourceBuffer should consume everything if ReadSegments succeeds or " - "the image must be in error!"); + if (MOZ_UNLIKELY(rv == NS_ERROR_OUT_OF_MEMORY)) { + DoError(); + } return rv; } @@ -1902,36 +1898,6 @@ RasterImage::HandleErrorWorker::Run() return NS_OK; } -// nsIInputStream callback to copy the incoming image data directly to the -// RasterImage without processing. The RasterImage is passed as the closure. -// Always reads everything it gets, even if the data is erroneous. -NS_METHOD -RasterImage::WriteToSourceBuffer(nsIInputStream* /* unused */, - void* aClosure, - const char* aFromRawSegment, - uint32_t /* unused */, - uint32_t aCount, - uint32_t* aWriteCount) -{ - // Retrieve the RasterImage - RasterImage* image = static_cast(aClosure); - - // Copy the source data. Unless we hit OOM, we squelch the return value - // here, because returning an error means that ReadSegments stops - // reading data, violating our invariant that we read everything we get. - // If we hit OOM then we fail and the load is aborted. - nsresult rv = image->mSourceBuffer->Append(aFromRawSegment, aCount); - if (rv == NS_ERROR_OUT_OF_MEMORY) { - image->DoError(); - return rv; - } - - // We wrote everything we got - *aWriteCount = aCount; - - return NS_OK; -} - bool RasterImage::ShouldAnimate() { diff --git a/image/RasterImage.h b/image/RasterImage.h index 240314e29520..13a1392b2974 100644 --- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -170,12 +170,6 @@ public: // Methods inherited from Image virtual void OnSurfaceDiscarded() override; - // Raster-specific methods - static NS_METHOD WriteToSourceBuffer(nsIInputStream* aIn, void* aClosure, - const char* aFromRawSegment, - uint32_t aToOffset, uint32_t aCount, - uint32_t* aWriteCount); - /* The total number of frames in this image. */ uint32_t GetNumFrames() const { return mFrameCount; } diff --git a/image/SourceBuffer.cpp b/image/SourceBuffer.cpp index 7b425e94943c..f283c7f79f15 100644 --- a/image/SourceBuffer.cpp +++ b/image/SourceBuffer.cpp @@ -9,6 +9,7 @@ #include #include #include "mozilla/Likely.h" +#include "nsIInputStream.h" #include "MainThreadUtils.h" #include "SurfaceCache.h" @@ -354,6 +355,46 @@ SourceBuffer::Append(const char* aData, size_t aLength) return NS_OK; } +static NS_METHOD +AppendToSourceBuffer(nsIInputStream*, + void* aClosure, + const char* aFromRawSegment, + uint32_t, + uint32_t aCount, + uint32_t* aWriteCount) +{ + SourceBuffer* sourceBuffer = static_cast(aClosure); + + // Copy the source data. Unless we hit OOM, we squelch the return value here, + // because returning an error means that ReadSegments stops reading data, and + // we want to ensure that we read everything we get. If we hit OOM then we + // return a failed status to the caller. + nsresult rv = sourceBuffer->Append(aFromRawSegment, aCount); + if (rv == NS_ERROR_OUT_OF_MEMORY) { + return rv; + } + + // Report that we wrote everything we got. + *aWriteCount = aCount; + + return NS_OK; +} + +nsresult +SourceBuffer::AppendFromInputStream(nsIInputStream* aInputStream, + uint32_t aCount) +{ + uint32_t bytesRead; + nsresult rv = aInputStream->ReadSegments(AppendToSourceBuffer, this, + aCount, &bytesRead); + + MOZ_ASSERT(bytesRead == aCount || rv == NS_ERROR_OUT_OF_MEMORY, + "AppendToSourceBuffer should consume everything unless " + "we run out of memory"); + + return rv; +} + void SourceBuffer::Complete(nsresult aStatus) { diff --git a/image/SourceBuffer.h b/image/SourceBuffer.h index 0e3cb9d5c2de..cbac16b074dc 100644 --- a/image/SourceBuffer.h +++ b/image/SourceBuffer.h @@ -21,6 +21,8 @@ #include "mozilla/nsRefPtr.h" #include "nsTArray.h" +class nsIInputStream; + namespace mozilla { namespace image { @@ -229,6 +231,9 @@ public: /// Append the provided data to the buffer. nsresult Append(const char* aData, size_t aLength); + /// Append the data available on the provided nsIInputStream to the buffer. + nsresult AppendFromInputStream(nsIInputStream* aInputStream, uint32_t aCount); + /** * Mark the buffer complete, with a status that will be available to * consumers. Further calls to Append() are forbidden after Complete(). From 9f17da1f588631f0530680b8bc284eee5ac97eb7 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 18:10:31 -0700 Subject: [PATCH 037/113] Bug 1181863 (Part 2) - Add ImageOps::DecodeToSurface() to allow image decoding without involving any main-thread-only objects. r=tn --- image/Decoder.cpp | 5 +++ image/Decoder.h | 3 -- image/DecoderFactory.cpp | 34 +++++++++++++++++ image/DecoderFactory.h | 5 +++ image/ImageOps.cpp | 80 ++++++++++++++++++++++++++++++++++++++-- image/ImageOps.h | 23 ++++++++++++ image/imgTools.cpp | 2 + 7 files changed, 146 insertions(+), 6 deletions(-) diff --git a/image/Decoder.cpp b/image/Decoder.cpp index 1b1aff691738..1d95d65f86e4 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -82,6 +82,11 @@ Decoder::Init() // No re-initializing MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!"); + // It doesn't make sense to decode anything but the first frame if we can't + // store anything in the SurfaceCache, since only the last frame we decode + // will be retrievable. + MOZ_ASSERT(ShouldUseSurfaceCache() || IsFirstFrameDecode()); + // Implementation-specific initialization InitInternal(); diff --git a/image/Decoder.h b/image/Decoder.h index 11757f1a7343..9ec512c9e497 100644 --- a/image/Decoder.h +++ b/image/Decoder.h @@ -290,9 +290,6 @@ public: */ RasterImage* GetImage() const { MOZ_ASSERT(mImage); return mImage.get(); } - // XXX(seth): This should be removed once we can optimize imgFrame objects - // off-main-thread. It only exists to support the code in Finish() for - // nsICODecoder. RawAccessFrameRef GetCurrentFrameRef() { return mCurrentFrame ? mCurrentFrame->RawAccessRef() diff --git a/image/DecoderFactory.cpp b/image/DecoderFactory.cpp index 6be68b2195d6..3ad135f171a0 100644 --- a/image/DecoderFactory.cpp +++ b/image/DecoderFactory.cpp @@ -181,5 +181,39 @@ DecoderFactory::CreateMetadataDecoder(DecoderType aType, return decoder.forget(); } +/* static */ already_AddRefed +DecoderFactory::CreateAnonymousDecoder(DecoderType aType, + SourceBuffer* aSourceBuffer, + uint32_t aFlags) +{ + if (aType == DecoderType::UNKNOWN) { + return nullptr; + } + + nsRefPtr decoder = + GetDecoder(aType, /* aImage = */ nullptr, /* aIsRedecode = */ false); + MOZ_ASSERT(decoder, "Should have a decoder now"); + + // Initialize the decoder. + decoder->SetMetadataDecode(false); + decoder->SetIterator(aSourceBuffer->Iterator()); + decoder->SetFlags(aFlags); + decoder->SetImageIsTransient(true); + + // Without an image, the decoder can't store anything in the SurfaceCache, so + // callers will only be able to retrieve the most recent frame via + // Decoder::GetCurrentFrame(). That means that anonymous decoders should + // always be first-frame-only decoders, because nobody ever wants the *last* + // frame. + decoder->SetIsFirstFrameDecode(); + + decoder->Init(); + if (NS_FAILED(decoder->GetDecoderError())) { + return nullptr; + } + + return decoder.forget(); +} + } // namespace image } // namespace mozilla diff --git a/image/DecoderFactory.h b/image/DecoderFactory.h index 33ef4228a64a..a47f1af2e517 100644 --- a/image/DecoderFactory.h +++ b/image/DecoderFactory.h @@ -101,6 +101,11 @@ public: int aSampleSize, const gfx::IntSize& aResolution); + static already_AddRefed + CreateAnonymousDecoder(DecoderType aType, + SourceBuffer* aSourceBuffer, + uint32_t aFlags); + private: virtual ~DecoderFactory() = 0; diff --git a/image/ImageOps.cpp b/image/ImageOps.cpp index 350dc6cd2112..83c6a480c686 100644 --- a/image/ImageOps.cpp +++ b/image/ImageOps.cpp @@ -4,14 +4,23 @@ * 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 "imgIContainer.h" +#include "ImageOps.h" + +#include "mozilla/gfx/2D.h" + #include "ClippedImage.h" +#include "DecodePool.h" +#include "Decoder.h" +#include "DecoderFactory.h" #include "DynamicImage.h" #include "FrozenImage.h" -#include "OrientedImage.h" #include "Image.h" +#include "imgIContainer.h" +#include "nsStreamUtils.h" +#include "OrientedImage.h" +#include "SourceBuffer.h" -#include "ImageOps.h" +using namespace mozilla::gfx; namespace mozilla { namespace image { @@ -68,5 +77,70 @@ ImageOps::CreateFromDrawable(gfxDrawable* aDrawable) return drawableImage.forget(); } +/* static */ already_AddRefed +ImageOps::DecodeToSurface(nsIInputStream* aInputStream, + const nsACString& aMimeType, + uint32_t aFlags) +{ + MOZ_ASSERT(aInputStream); + + nsresult rv; + + // Prepare the input stream. + nsCOMPtr inputStream = aInputStream; + if (!NS_InputStreamIsBuffered(aInputStream)) { + nsCOMPtr bufStream; + rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), + aInputStream, 1024); + if (NS_SUCCEEDED(rv)) { + inputStream = bufStream; + } + } + + // Figure out how much data we've been passed. + uint64_t length; + rv = inputStream->Available(&length); + if (NS_FAILED(rv) || length > UINT32_MAX) { + return nullptr; + } + + // Write the data into a SourceBuffer. + nsRefPtr sourceBuffer = new SourceBuffer(); + sourceBuffer->ExpectLength(length); + rv = sourceBuffer->AppendFromInputStream(inputStream, length); + if (NS_FAILED(rv)) { + return nullptr; + } + sourceBuffer->Complete(NS_OK); + + // Create a decoder. + DecoderType decoderType = + DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get()); + nsRefPtr decoder = + DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, aFlags); + if (!decoder) { + return nullptr; + } + + // Run the decoder synchronously. + decoder->Decode(); + if (!decoder->GetDecodeDone() || decoder->HasError()) { + return nullptr; + } + + // Pull out the surface. + RawAccessFrameRef frame = decoder->GetCurrentFrameRef(); + if (!frame) { + return nullptr; + } + + RefPtr surface = frame->GetSurface(); + if (!surface) { + return nullptr; + } + + return surface.forget(); +} + } // namespace image } // namespace mozilla diff --git a/image/ImageOps.h b/image/ImageOps.h index d080a54e4d96..6f941a71c36c 100644 --- a/image/ImageOps.h +++ b/image/ImageOps.h @@ -12,8 +12,14 @@ class gfxDrawable; class imgIContainer; +class nsIInputStream; namespace mozilla { + +namespace gfx { +class SourceSurface; +} + namespace image { class Image; @@ -61,6 +67,23 @@ public: static already_AddRefed CreateFromDrawable(gfxDrawable* aDrawable); + /** + * Decodes an image from an nsIInputStream directly into a SourceSurface, + * without ever creating an Image or imgIContainer (which are mostly + * main-thread-only). That means that this function may be called + * off-main-thread. + * + * @param aInputStream An input stream containing an encoded image. + * @param aMimeType The MIME type of the image. + * @param aFlags Flags of the imgIContainer::FLAG_DECODE_* variety. + * @return A SourceSurface containing the first frame of the image at its + * intrinsic size, or nullptr if the image cannot be decoded. + */ + already_AddRefed + DecodeToSurface(nsIInputStream* aInputStream, + const nsACString& aMimeType, + uint32_t aFlags); + private: // This is a static utility class, so disallow instantiation. virtual ~ImageOps() = 0; diff --git a/image/imgTools.cpp b/image/imgTools.cpp index 1cfd23bad455..5fd101fa4e4d 100644 --- a/image/imgTools.cpp +++ b/image/imgTools.cpp @@ -61,6 +61,8 @@ imgTools::DecodeImage(nsIInputStream* aInStr, const nsACString& aMimeType, imgIContainer** aContainer) { + MOZ_ASSERT(NS_IsMainThread()); + nsresult rv; NS_ENSURE_ARG_POINTER(aInStr); From b925bc97d1f57ac61232e72a98dd663d99442f03 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Fri, 31 Jul 2015 18:10:34 -0700 Subject: [PATCH 038/113] Bug 1181863 (Part 3) - Add tests for DecodeToSurface(). r=tn --- image/ImageOps.cpp | 3 +- image/ImageOps.h | 2 +- image/moz.build | 2 + image/test/gtest/Common.cpp | 157 +++++++++++++++++++++++ image/test/gtest/Common.h | 91 +++++++++++++ image/test/gtest/TestDecodeToSurface.cpp | 119 +++++++++++++++++ image/test/gtest/corrupt.jpg | Bin 0 -> 2477 bytes image/test/gtest/first-frame-green.gif | Bin 0 -> 317 bytes image/test/gtest/first-frame-green.png | Bin 0 -> 364 bytes image/test/gtest/green.bmp | Bin 0 -> 30138 bytes image/test/gtest/green.gif | Bin 0 -> 156 bytes image/test/gtest/green.ico | Bin 0 -> 41662 bytes image/test/gtest/green.jpg | Bin 0 -> 361 bytes image/test/gtest/green.png | Bin 0 -> 255 bytes image/test/gtest/moz.build | 31 +++++ 15 files changed, 402 insertions(+), 3 deletions(-) create mode 100644 image/test/gtest/Common.cpp create mode 100644 image/test/gtest/Common.h create mode 100644 image/test/gtest/TestDecodeToSurface.cpp create mode 100644 image/test/gtest/corrupt.jpg create mode 100644 image/test/gtest/first-frame-green.gif create mode 100644 image/test/gtest/first-frame-green.png create mode 100644 image/test/gtest/green.bmp create mode 100644 image/test/gtest/green.gif create mode 100644 image/test/gtest/green.ico create mode 100644 image/test/gtest/green.jpg create mode 100644 image/test/gtest/green.png create mode 100644 image/test/gtest/moz.build diff --git a/image/ImageOps.cpp b/image/ImageOps.cpp index 83c6a480c686..0a1e460c88f9 100644 --- a/image/ImageOps.cpp +++ b/image/ImageOps.cpp @@ -6,8 +6,6 @@ #include "ImageOps.h" -#include "mozilla/gfx/2D.h" - #include "ClippedImage.h" #include "DecodePool.h" #include "Decoder.h" @@ -16,6 +14,7 @@ #include "FrozenImage.h" #include "Image.h" #include "imgIContainer.h" +#include "mozilla/gfx/2D.h" #include "nsStreamUtils.h" #include "OrientedImage.h" #include "SourceBuffer.h" diff --git a/image/ImageOps.h b/image/ImageOps.h index 6f941a71c36c..9dcfc286f8ce 100644 --- a/image/ImageOps.h +++ b/image/ImageOps.h @@ -79,7 +79,7 @@ public: * @return A SourceSurface containing the first frame of the image at its * intrinsic size, or nullptr if the image cannot be decoded. */ - already_AddRefed + static already_AddRefed DecodeToSurface(nsIInputStream* aInputStream, const nsACString& aMimeType, uint32_t aFlags); diff --git a/image/moz.build b/image/moz.build index 9f860c56b70b..866e9801498d 100644 --- a/image/moz.build +++ b/image/moz.build @@ -5,6 +5,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DIRS += ['build', 'decoders', 'encoders'] +if CONFIG['ENABLE_TESTS']: + DIRS += ['test/gtest'] with Files('**'): BUG_COMPONENT = ('Core', 'ImageLib') diff --git a/image/test/gtest/Common.cpp b/image/test/gtest/Common.cpp new file mode 100644 index 000000000000..2519cb4d2991 --- /dev/null +++ b/image/test/gtest/Common.cpp @@ -0,0 +1,157 @@ +/* -*- 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 "Common.h" + +#include +#include "gtest/gtest.h" + +#include "nsDirectoryServiceDefs.h" +#include "nsIDirectoryService.h" +#include "nsIFile.h" +#include "nsIInputStream.h" +#include "nsIProperties.h" +#include "nsNetUtil.h" +#include "nsRefPtr.h" +#include "nsString.h" + +namespace mozilla { + +using namespace gfx; + +using std::abs; + +/////////////////////////////////////////////////////////////////////////////// +// Helpers +/////////////////////////////////////////////////////////////////////////////// + +// These macros work like gtest's ASSERT_* macros, except that they can be used +// in functions that return values. +#define ASSERT_TRUE_OR_RETURN(e, rv) \ + EXPECT_TRUE(e); \ + if (!(e)) { \ + return rv; \ + } + +#define ASSERT_EQ_OR_RETURN(a, b, rv) \ + EXPECT_EQ(a, b); \ + if ((a) != (b)) { \ + return rv; \ + } + +#define ASSERT_LE_OR_RETURN(a, b, rv) \ + EXPECT_LE(a, b); \ + if (!((a) <= (b))) { \ + return rv; \ + } + +already_AddRefed +LoadFile(const char* aRelativePath) +{ + nsresult rv; + + nsCOMPtr dirService = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); + ASSERT_TRUE_OR_RETURN(dirService != nullptr, nullptr); + + // Retrieve the current working directory. + nsCOMPtr file; + rv = dirService->Get(NS_OS_CURRENT_WORKING_DIR, + NS_GET_IID(nsIFile), getter_AddRefs(file)); + ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr); + + // Construct the final path by appending the working path to the current + // working directory. + file->AppendNative(nsAutoCString(aRelativePath)); + + // Construct an input stream for the requested file. + nsCOMPtr inputStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), file); + ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr); + + return inputStream.forget(); +} + +bool +IsSolidColor(SourceSurface* aSurface, BGRAColor aColor, bool aFuzzy) +{ + nsRefPtr dataSurface = aSurface->GetDataSurface(); + ASSERT_TRUE_OR_RETURN(dataSurface != nullptr, false); + + ASSERT_EQ_OR_RETURN(dataSurface->Stride(), aSurface->GetSize().width * 4, + false); + + DataSourceSurface::ScopedMap mapping(dataSurface, + DataSourceSurface::MapType::READ); + ASSERT_TRUE_OR_RETURN(mapping.IsMapped(), false); + + uint8_t* data = dataSurface->GetData(); + ASSERT_TRUE_OR_RETURN(data != nullptr, false); + + int32_t length = dataSurface->Stride() * aSurface->GetSize().height; + for (int32_t i = 0 ; i < length ; i += 4) { + if (aFuzzy) { + ASSERT_LE_OR_RETURN(abs(aColor.mBlue - data[i + 0]), 1, false); + ASSERT_LE_OR_RETURN(abs(aColor.mGreen - data[i + 1]), 1, false); + ASSERT_LE_OR_RETURN(abs(aColor.mRed - data[i + 2]), 1, false); + ASSERT_LE_OR_RETURN(abs(aColor.mAlpha - data[i + 3]), 1, false); + } else { + ASSERT_EQ_OR_RETURN(aColor.mBlue, data[i + 0], false); + ASSERT_EQ_OR_RETURN(aColor.mGreen, data[i + 1], false); + ASSERT_EQ_OR_RETURN(aColor.mRed, data[i + 2], false); + ASSERT_EQ_OR_RETURN(aColor.mAlpha, data[i + 3], false); + } + } + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Test Data +/////////////////////////////////////////////////////////////////////////////// + +ImageTestCase GreenPNGTestCase() +{ + return ImageTestCase("green.png", "image/png", IntSize(100, 100)); +} + +ImageTestCase GreenGIFTestCase() +{ + return ImageTestCase("green.gif", "image/gif", IntSize(100, 100)); +} + +ImageTestCase GreenJPGTestCase() +{ + return ImageTestCase("green.jpg", "image/jpeg", IntSize(100, 100), + /* aFuzzy = */ true); +} + +ImageTestCase GreenBMPTestCase() +{ + return ImageTestCase("green.bmp", "image/bmp", IntSize(100, 100)); +} + +ImageTestCase GreenICOTestCase() +{ + return ImageTestCase("green.ico", "image/x-icon", IntSize(100, 100)); +} + +ImageTestCase GreenFirstFrameAnimatedGIFTestCase() +{ + return ImageTestCase("first-frame-green.gif", "image/gif", IntSize(100, 100)); +} + +ImageTestCase GreenFirstFrameAnimatedPNGTestCase() +{ + return ImageTestCase("first-frame-green.png", "image/png", IntSize(100, 100)); +} + +ImageTestCase CorruptTestCase() +{ + return ImageTestCase("corrupt.jpg", "image/jpeg", IntSize(100, 100)); +} + +} // namespace mozilla diff --git a/image/test/gtest/Common.h b/image/test/gtest/Common.h new file mode 100644 index 000000000000..b9d8efa81426 --- /dev/null +++ b/image/test/gtest/Common.h @@ -0,0 +1,91 @@ +/* -*- 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/. */ + +#ifndef mozilla_image_test_gtest_Common_h +#define mozilla_image_test_gtest_Common_h + +#include "mozilla/gfx/2D.h" +#include "nsCOMPtr.h" + +class nsIInputStream; + +namespace mozilla { + +/////////////////////////////////////////////////////////////////////////////// +// Types +/////////////////////////////////////////////////////////////////////////////// + +struct ImageTestCase +{ + ImageTestCase(const char* aPath, + const char* aMimeType, + gfx::IntSize aSize, + bool aFuzzy = false) + : mPath(aPath) + , mMimeType(aMimeType) + , mSize(aSize) + , mFuzzy(aFuzzy) + { } + + const char* mPath; + const char* mMimeType; + gfx::IntSize mSize; + bool mFuzzy; +}; + +struct BGRAColor +{ + BGRAColor(uint8_t aBlue, uint8_t aGreen, uint8_t aRed, uint8_t aAlpha) + : mBlue(aBlue) + , mGreen(aGreen) + , mRed(aRed) + , mAlpha(aAlpha) + { } + + static BGRAColor Green() { return BGRAColor(0x00, 0xFF, 0x00, 0xFF); } + + uint8_t mBlue; + uint8_t mGreen; + uint8_t mRed; + uint8_t mAlpha; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Helpers +/////////////////////////////////////////////////////////////////////////////// + +/// Loads a file from the current directory. @return an nsIInputStream for it. +already_AddRefed LoadFile(const char* aRelativePath); + +/** + * @returns true if every pixel of @aSurface is @aColor. + * + * If @aFuzzy is true, a tolerance of 1 is allowed in each color component. This + * may be necessary for tests that involve JPEG images. + */ +bool IsSolidColor(gfx::SourceSurface* aSurface, + BGRAColor aColor, + bool aFuzzy = false); + + +/////////////////////////////////////////////////////////////////////////////// +// Test Data +/////////////////////////////////////////////////////////////////////////////// + +ImageTestCase GreenPNGTestCase(); +ImageTestCase GreenGIFTestCase(); +ImageTestCase GreenJPGTestCase(); +ImageTestCase GreenBMPTestCase(); +ImageTestCase GreenICOTestCase(); + +ImageTestCase GreenFirstFrameAnimatedGIFTestCase(); +ImageTestCase GreenFirstFrameAnimatedPNGTestCase(); + +ImageTestCase CorruptTestCase(); + +} // namespace mozilla + +#endif // mozilla_image_test_gtest_Common_h diff --git a/image/test/gtest/TestDecodeToSurface.cpp b/image/test/gtest/TestDecodeToSurface.cpp new file mode 100644 index 000000000000..973413655726 --- /dev/null +++ b/image/test/gtest/TestDecodeToSurface.cpp @@ -0,0 +1,119 @@ +/* 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 "gtest/gtest.h" + +#include "Common.h" +#include "imgIContainer.h" +#include "imgITools.h" +#include "ImageOps.h" +#include "mozilla/gfx/2D.h" +#include "nsComponentManagerUtils.h" +#include "nsCOMPtr.h" +#include "nsIInputStream.h" +#include "nsIRunnable.h" +#include "nsIThread.h" +#include "nsRefPtr.h" +#include "nsString.h" +#include "nsThreadUtils.h" + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::image; + + +TEST(ImageDecodeToSurface, ImageModuleAvailable) +{ + // We can run into problems if XPCOM modules get initialized in the wrong + // order. It's important that this test run first, both as a sanity check and + // to ensure we get the module initialization order we want. + nsCOMPtr imgTools = + do_CreateInstance("@mozilla.org/image/tools;1"); + EXPECT_TRUE(imgTools != nullptr); +} + +class DecodeToSurfaceRunnable : public nsRunnable +{ +public: + DecodeToSurfaceRunnable(nsIInputStream* aInputStream, + const ImageTestCase& aTestCase) + : mInputStream(aInputStream) + , mTestCase(aTestCase) + { } + + NS_IMETHOD Run() + { + Go(); + return NS_OK; + } + + void Go() + { + nsRefPtr surface = + ImageOps::DecodeToSurface(mInputStream, + nsAutoCString(mTestCase.mMimeType), + imgIContainer::DECODE_FLAGS_DEFAULT); + ASSERT_TRUE(surface != nullptr); + + EXPECT_EQ(SurfaceType::DATA, surface->GetType()); + EXPECT_TRUE(surface->GetFormat() == SurfaceFormat::B8G8R8X8 || + surface->GetFormat() == SurfaceFormat::B8G8R8A8); + EXPECT_EQ(mTestCase.mSize, surface->GetSize()); + + EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green(), mTestCase.mFuzzy)); + } + +private: + nsCOMPtr mInputStream; + ImageTestCase mTestCase; +}; + +static void +RunDecodeToSurface(const ImageTestCase& aTestCase) +{ + nsCOMPtr inputStream = LoadFile(aTestCase.mPath); + ASSERT_TRUE(inputStream != nullptr); + + nsCOMPtr thread; + nsresult rv = NS_NewThread(getter_AddRefs(thread), nullptr); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + // We run the DecodeToSurface tests off-main-thread to ensure that + // DecodeToSurface doesn't require any main-thread-only code. + nsCOMPtr runnable = + new DecodeToSurfaceRunnable(inputStream, aTestCase); + thread->Dispatch(runnable, nsIThread::DISPATCH_SYNC); + + thread->Shutdown(); +} + +TEST(ImageDecodeToSurface, PNG) { RunDecodeToSurface(GreenPNGTestCase()); } +TEST(ImageDecodeToSurface, GIF) { RunDecodeToSurface(GreenGIFTestCase()); } +TEST(ImageDecodeToSurface, JPG) { RunDecodeToSurface(GreenJPGTestCase()); } +TEST(ImageDecodeToSurface, BMP) { RunDecodeToSurface(GreenBMPTestCase()); } +TEST(ImageDecodeToSurface, ICO) { RunDecodeToSurface(GreenICOTestCase()); } + +TEST(ImageDecodeToSurface, AnimatedGIF) +{ + RunDecodeToSurface(GreenFirstFrameAnimatedGIFTestCase()); +} + +TEST(ImageDecodeToSurface, AnimatedPNG) +{ + RunDecodeToSurface(GreenFirstFrameAnimatedPNGTestCase()); +} + +TEST(ImageDecodeToSurface, Corrupt) +{ + ImageTestCase testCase = CorruptTestCase(); + + nsCOMPtr inputStream = LoadFile(testCase.mPath); + ASSERT_TRUE(inputStream != nullptr); + + nsRefPtr surface = + ImageOps::DecodeToSurface(inputStream, + nsAutoCString(testCase.mMimeType), + imgIContainer::DECODE_FLAGS_DEFAULT); + EXPECT_TRUE(surface == nullptr); +} diff --git a/image/test/gtest/corrupt.jpg b/image/test/gtest/corrupt.jpg new file mode 100644 index 0000000000000000000000000000000000000000..555a416d7d8e6c0f835a756994a8efca17c55cd8 GIT binary patch literal 2477 zcmbW!c|6qJ9tZH>7-I&B35hIWC|SzBWC@Qn9{avE2~p-jM#z>*O19EuWQa({E^9PM zj5SfnFqRBOqh<_Z44&NSdG71p*X#au@BN(D`Tldh=e)k}*V&ucn*)Tb&LGbKAT9s^ zaTc&Q1(*X6FgG_h7{VzK2!sc^4+iB#kdL2tA6)Rj0k|MsNcf+>>&wB zDH$0VVKF&`oHRmGT1NWUBOuOIC=YZ$47Oid1TG@|KWDE4fWrWLz#a^e1GwNIFdVc; z0%SOILO_27_-8;|U~bNoFkU`>PD7&*zy$(>xj6H40G#f4&N;vhha8YqH{}s_xB!(4 z6VbStSqPIit9>Kt_<@Pg^u^rbLNL)fe@#rz7<67D}x_bHs=D%5-u{?{ka&mTY zb#wRd^z*+M5P0cwQ25n|$f#@4F^RX6?%Ykjm+}z%C<~YUI0s+!toV5ep|q^LuD+qM z>1A`vtIjS`cTaC$|G>!T*vE15#3Y3}zwr6X;?h^z^6J|9kBv>%7JK`b3j~1wW^wA@ zu>WwuIW8{Fw{Sy$xj_L7`8zNiFadVtl%8cr;iye{lG1kyUq7-GY{P7$ z2+}zOQ+ccE_5O8?z7ehc^I3HJpr6eK-vpKdn1O;0>O$LrzQt~FNgwz#c4OaH+qw+DjUtOQZ~{UXT*8FK-Xl( zTbI1Z+BRXS{rc{o&^hRl6Q^(tgO6;qaMb-U&)OT%5DY7GsPVa2SMi*j?pQSY}rg^orD$jOUl@ zC$3@biCbyg1~zr$Jz;zv$Bx&I4A#b1%+Sg(4?Nn&#&^OKA86>&$fBfTY>>3V%eK{X z-z-8G)xOSS#7*g=DJ!%32mIUXWCI-%r?iKbW4vGCiWFKB;|6z%hh@```HI*suaSIR zN*JkvM(t#G75`gigQ97~M72{l`;IJZROm|J3Sy~~TBQvWA&PfgqH%v@sQ1ca^A?O} zDl9EV(!1Y=I%K}-=x~_ed;2jsptiNS0W_lYf}h}>kw~L^>^RR1&K^X)QzIx-KY`An zjWudNESQI!$Qj0+e|&nFn?*ylG3%+>$%c@MAoBK;L7M=-lV;9y0~kW&DbW3)(p|o~ z=G1v^;Rp$Zzptbtuh|N?tO0&!@%1q(by(05yOyP?G-jJGx zIk%FZBd+*O0(Iw<5uetzS-(wetJ2hzKHbxaC1GdY-{V-gwcyKL;5#1 zxn;}X=P9C(CNYI=i!Q5N$rp#M%llR(W@e(kFvDHZsS%EPKva#3sj9=m(D(bBNo}|j zcVD4$MC(=QWANh0%x57k00VJyS|f}+(rz>M6eo7}oZaK4!eulqb9KdZ)h-bpLCk*N zibN}ZWz5$K7f&35LK#b=P0DXt1B)jue19TFwWBxqJp$Zu9`v+*qe328ZbxNf^H`$2 zx1CC+5%+yVG*Y6pN*Y=kneXxogNepxJs!klY22T$@g3LOs3cY2 zM`~0X*=r%q& zfH=4ooR*QgtehHb;!)=(o*dQ_btPWeugh2(zex{#9p88N6f~R>)S_fQE7MrvfJvR3 zL0y>N(S)rRi@}^jD8GYywSTbas`GaHY1=kgS%WNfG}a5c<}(L$t>~}x(Kc#w5iQI- z6Je`w_&AC!wgD3BO;iT4h*68>aaQAtW4jJo>1ESF@Sar3aO+%Nd%ceS1?i3J!;~>w ziu>nMwtUEQWavI@w%-(T`V$@7dO?rl$#6q6wEv9cL+u)5*xU zl6wGLPdsF+(jAXfNTE!yvxP5+K|!YU0w;V`0$MwL)N2~3JN6muiD4PYRmLtQMpQ^S zncwiTP5XMoAn?KUtjNVat3fs;CWYNIv>c1P-872i&qzx%t)lFM?aab#!o%72c$>zX;=9lb~d(-<|FyMY>Q~;8f}%D4;6fO zp)zRn>o&biRkVwc!(AJ8qpS1{!zh-Fo7w}a`ga9_dQB!4U$!(hyXKmll)w2s-qHb; zax2Z*VQH*&X4pEnLl=Kl=2E8ot9nC)8Dey%ZaZ~!wcGRkk6gX;af6~zr?i63<{H6k z>)y;i=_I`N`=-X6{#a|V+qYtZ|5)87yU1i*mAa@IPe@l>`5EJx*V#K(xbr&c^5O2h LYJvK_NxpvoIw+iI literal 0 HcmV?d00001 diff --git a/image/test/gtest/first-frame-green.gif b/image/test/gtest/first-frame-green.gif new file mode 100644 index 0000000000000000000000000000000000000000..cd3c7d3db89085bbdbcb41247792bfc5fde9c492 GIT binary patch literal 317 zcmZ?wbhEHbOkqf2_`txx@E?d76o0ZXaDnLm+bCU^SwR4`P&|OOncV3?A57l@3@bD^11%4_xZ1L-~aLRaJ3&<^s&Q7 zYxg(%)P8t~3SOWn5Zl3x8 literal 0 HcmV?d00001 diff --git a/image/test/gtest/first-frame-green.png b/image/test/gtest/first-frame-green.png new file mode 100644 index 0000000000000000000000000000000000000000..115f035d897859b9ad8e47cb158d252e621096c2 GIT binary patch literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^DIm7q0dH}%P0@|lsk;Di3whd_g1)-eLv zU>|_30uf;Acs8Ye2eKX0QXE5oG!v4Q8~-i5{;1#r(=v8^wt?kN{!dx*-%nA(&o~BX zh;)f-L`iUdT1k0gQ7VHYFeDWmi;GiplX6mv6pZx@;#_rPfXWy=UHx3vIVCg!0L9y1 Aga7~l literal 0 HcmV?d00001 diff --git a/image/test/gtest/green.bmp b/image/test/gtest/green.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f79dd672adfd43be0906e2ce5bb600b215517d27 GIT binary patch literal 30138 zcmeIuuMUAx6aesR%!X(-$pkaesDdeyA`60jFJPLWD2l9k3=_SChmd7H*WWDv?(R9e z=bYUWeuL3*5#rgTd6~xS6`4?t{dPJpm!1(DG2KFV-bZyC49D};>DrneYL)u>lIeH5 z}D?{?)5OTjo-11)e+3i7q009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly KK!5;&zXa|~egVP& literal 0 HcmV?d00001 diff --git a/image/test/gtest/green.gif b/image/test/gtest/green.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef215dfc94751a15cca5909a443632f5468041b1 GIT binary patch literal 156 zcmZ?wbhEHbOkqf2_`txx@E?d76o0aSC0DsJgtdHOB?;yGJxb#K16=Qn@b zBadm%I+wjVwe21E@lQV2zx6)en^ZQ;t0ulgyQxa#q^j zZrPHdfAb8jE>C~}0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs p0RjXF5FkK+009C72oNAZfB*pk1PBlyK!Cs}5J!Lj0Rja63*4#e+dKdO literal 0 HcmV?d00001 diff --git a/image/test/gtest/green.jpg b/image/test/gtest/green.jpg new file mode 100644 index 0000000000000000000000000000000000000000..48c454d27c9c46574597f80fdd64ecd4d254577d GIT binary patch literal 361 zcmbV{Jr2S!3`RdUC5n@@by{Fz;vx)4mEas4hWjwGFmMQd7H$%pvEATBijpUPv6CP9 z138X|5kwU>x{%-4!&HT`8>O~&!CAYo?7U~k>|+;wXajSTnzj>3;$uJbGU!>#BUF;6 zb{dqxLG6#^83E)O5% literal 0 HcmV?d00001 diff --git a/image/test/gtest/green.png b/image/test/gtest/green.png new file mode 100644 index 0000000000000000000000000000000000000000..7df25f33bdb26cc7f41138501ab399059839137d GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^DImFdgVmrY2>Os;yW$XlS$JWm(LkcwMxFBmd1FmNz0$ozjU>fm{HrGS|QSGZD6PEz%r zro*}k!eEMoFdB8hjHM3Kz>FXTpc Date: Thu, 30 Jul 2015 22:05:18 -0400 Subject: [PATCH 039/113] Bug 1189590 - Make the about:accounts start page RTL aware; r=jaws --- browser/base/content/aboutaccounts/aboutaccounts.xhtml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/browser/base/content/aboutaccounts/aboutaccounts.xhtml b/browser/base/content/aboutaccounts/aboutaccounts.xhtml index 1dd0e3f1e555..b4906db6464e 100644 --- a/browser/base/content/aboutaccounts/aboutaccounts.xhtml +++ b/browser/base/content/aboutaccounts/aboutaccounts.xhtml @@ -7,13 +7,15 @@ %htmlDTD; %brandDTD; + + %globalDTD; %aboutAccountsDTD; %syncBrandDTD; ]> - + &syncBrand.fullName.label; From 509e505cbb6ead654eeeafc1e3affcebd42a3578 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Sat, 1 Aug 2015 00:07:04 -0400 Subject: [PATCH 040/113] Bug 1181863 - Part 4: Fix the build bustage --- image/test/gtest/Common.cpp | 2 +- image/test/gtest/TestDecodeToSurface.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/image/test/gtest/Common.cpp b/image/test/gtest/Common.cpp index 2519cb4d2991..0cd30c61ce84 100644 --- a/image/test/gtest/Common.cpp +++ b/image/test/gtest/Common.cpp @@ -14,7 +14,7 @@ #include "nsIInputStream.h" #include "nsIProperties.h" #include "nsNetUtil.h" -#include "nsRefPtr.h" +#include "mozilla/nsRefPtr.h" #include "nsString.h" namespace mozilla { diff --git a/image/test/gtest/TestDecodeToSurface.cpp b/image/test/gtest/TestDecodeToSurface.cpp index 973413655726..5a4696f8cdbf 100644 --- a/image/test/gtest/TestDecodeToSurface.cpp +++ b/image/test/gtest/TestDecodeToSurface.cpp @@ -14,7 +14,7 @@ #include "nsIInputStream.h" #include "nsIRunnable.h" #include "nsIThread.h" -#include "nsRefPtr.h" +#include "mozilla/nsRefPtr.h" #include "nsString.h" #include "nsThreadUtils.h" From 1634fa444456e0db94cce42656f5687037411a78 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Thu, 30 Jul 2015 19:53:38 +0200 Subject: [PATCH 041/113] Bug 1189058 - Don't spin the Event loop while setting up WebRTC + Java. r=snorp --- widget/android/AndroidJNIWrapper.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/widget/android/AndroidJNIWrapper.cpp b/widget/android/AndroidJNIWrapper.cpp index 35c2be2369eb..15aaa6d93540 100644 --- a/widget/android/AndroidJNIWrapper.cpp +++ b/widget/android/AndroidJNIWrapper.cpp @@ -9,6 +9,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/Assertions.h" +#include "mozilla/SyncRunnable.h" #include "nsThreadUtils.h" #include "AndroidBridge.h" @@ -66,7 +67,8 @@ extern "C" { jclass foundClass; nsCOMPtr runnable_ref(new GetGlobalClassRefRunnable(className, &foundClass)); - mainThread->Dispatch(runnable_ref, NS_DISPATCH_SYNC); + nsRefPtr sr = new mozilla::SyncRunnable(runnable_ref); + sr->DispatchToThread(mainThread); if (!foundClass) return nullptr; From 48d97405813702ad6d2b1d58812699a7fcf800b2 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Sat, 1 Aug 2015 13:58:39 +0800 Subject: [PATCH 042/113] Bug 1189866 - revert code that accidentally got into bug 1187817 patch 5. r=me. --- media/libcubeb/src/cubeb_opensl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/media/libcubeb/src/cubeb_opensl.c b/media/libcubeb/src/cubeb_opensl.c index c65f72603a08..a46489b750f7 100644 --- a/media/libcubeb/src/cubeb_opensl.c +++ b/media/libcubeb/src/cubeb_opensl.c @@ -671,10 +671,8 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name static void opensl_stream_destroy(cubeb_stream * stm) { - if (stm->playerObj) { - (*stm->bufq)->Clear(stm->bufq); + if (stm->playerObj) (*stm->playerObj)->Destroy(stm->playerObj); - } int i; for (i = 0; i < NBUFS; i++) { free(stm->queuebuf[i]); From 3976f99d470b03d99e073f01042e86d146ab4e91 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Sat, 1 Aug 2015 05:53:35 -0400 Subject: [PATCH 043/113] Bug 1190053 - update talos.json to pick up additional cleanup and tps work. r=parkouss --- testing/talos/talos.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/talos/talos.json b/testing/talos/talos.json index 14a66bdccf90..e8c7c56e216d 100644 --- a/testing/talos/talos.json +++ b/testing/talos/talos.json @@ -5,7 +5,7 @@ }, "global": { "talos_repo": "https://hg.mozilla.org/build/talos", - "talos_revision": "24ecc5140689" + "talos_revision": "d44548b8feb9" }, "extra_options": { "android": [ "--apkPath=%(apk_path)s" ] From bbe2316f0572011dbb7a92940b90edbd952078da Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Sat, 1 Aug 2015 14:03:43 +0100 Subject: [PATCH 044/113] Bug 1189698 Potential race condition when shutting down the old player thread r=jimm --- widget/windows/nsSound.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/widget/windows/nsSound.cpp b/widget/windows/nsSound.cpp index e1e51f381b1a..69b8ae5bd93d 100644 --- a/widget/windows/nsSound.cpp +++ b/widget/windows/nsSound.cpp @@ -124,10 +124,9 @@ nsSound::~nsSound() void nsSound::ShutdownOldPlayerThread() { - if (mPlayerThread) { - mPlayerThread->Shutdown(); - mPlayerThread = nullptr; - } + nsCOMPtr playerThread(mPlayerThread.forget()); + if (playerThread) + playerThread->Shutdown(); } void nsSound::PurgeLastSound() From 839a3bc3fb676c5fd8a4adb5f930cc517c60ea3b Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Thu, 30 Jul 2015 12:44:14 -0700 Subject: [PATCH 045/113] Bug 1183954 - Fix Notification.data structured cloning on workers. r=robertbindar,mccr8 Rather than store a non-thread-safe refcounted nsIStructuredCloneContainer, store the base64 representation. Caches a jsval the first time an attempt to access data is made from content script. --HG-- extra : commitid : Ijd82LTJaYo extra : rebase_source : f82e837842037ea02efae3a0fc9b2b35c2a0d7d0 --- dom/base/nsStructuredCloneContainer.cpp | 28 +++++-- .../base/nsIStructuredCloneContainer.idl | 10 ++- dom/notification/Notification.cpp | 84 +++++++++++-------- dom/notification/Notification.h | 9 +- dom/workers/test/notification_worker.js | 6 +- .../serviceworkers/notificationclick.html | 4 +- .../test/serviceworkers/notificationclick.js | 6 +- .../test_notificationclick.html | 4 +- 8 files changed, 93 insertions(+), 58 deletions(-) diff --git a/dom/base/nsStructuredCloneContainer.cpp b/dom/base/nsStructuredCloneContainer.cpp index 72ef1c028d5a..bd789e01179e 100644 --- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -95,6 +95,24 @@ nsStructuredCloneContainer::InitFromBase64(const nsAString &aData, return NS_OK; } +nsresult +nsStructuredCloneContainer::DeserializeToJsval(JSContext* aCx, + JS::MutableHandle aValue) +{ + aValue.setNull(); + JS::Rooted jsStateObj(aCx); + bool hasTransferable = false; + bool success = JS_ReadStructuredClone(aCx, mData, mSize, mVersion, + &jsStateObj, nullptr, nullptr) && + JS_StructuredCloneHasTransferables(mData, mSize, + &hasTransferable); + // We want to be sure that mData doesn't contain transferable objects + MOZ_ASSERT(!hasTransferable); + NS_ENSURE_STATE(success && !hasTransferable); + + aValue.set(jsStateObj); + return NS_OK; +} nsresult nsStructuredCloneContainer::DeserializeToVariant(JSContext *aCx, @@ -106,14 +124,8 @@ nsStructuredCloneContainer::DeserializeToVariant(JSContext *aCx, // Deserialize to a JS::Value. JS::Rooted jsStateObj(aCx); - bool hasTransferable = false; - bool success = JS_ReadStructuredClone(aCx, mData, mSize, mVersion, - &jsStateObj, nullptr, nullptr) && - JS_StructuredCloneHasTransferables(mData, mSize, - &hasTransferable); - // We want to be sure that mData doesn't contain transferable objects - MOZ_ASSERT(!hasTransferable); - NS_ENSURE_STATE(success && !hasTransferable); + nsresult rv = DeserializeToJsval(aCx, &jsStateObj); + NS_ENSURE_SUCCESS(rv, rv); // Now wrap the JS::Value as an nsIVariant. nsCOMPtr varStateObj; diff --git a/dom/interfaces/base/nsIStructuredCloneContainer.idl b/dom/interfaces/base/nsIStructuredCloneContainer.idl index 68813be5d781..84a12ae0e4c0 100644 --- a/dom/interfaces/base/nsIStructuredCloneContainer.idl +++ b/dom/interfaces/base/nsIStructuredCloneContainer.idl @@ -27,7 +27,7 @@ interface nsIDocument; * string containing a copy of the container's serialized data, using * getDataAsBase64. */ -[scriptable, uuid(63eeafec-63f5-42c3-aea9-5c04678784e7)] +[scriptable, uuid(c664aae7-0d67-4155-a2dd-a3861778626f)] interface nsIStructuredCloneContainer : nsISupports { /** @@ -45,9 +45,17 @@ interface nsIStructuredCloneContainer : nsISupports [implicit_jscontext] void initFromBase64(in AString aData,in unsigned long aFormatVersion); + /** + * Deserializes this structured clone container returning it as a jsval. + * Can be called on main and worker threads. + */ + [implicit_jscontext] + jsval deserializeToJsval(); + /** * Deserialize the object this container holds, returning it wrapped as * an nsIVariant. + * Main thread only! */ [implicit_jscontext] nsIVariant deserializeToVariant(); diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 1ade511a9cb6..ac7a9f3c2943 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -696,8 +696,8 @@ Notification::Notification(nsIGlobalObject* aGlobal, const nsAString& aID, : DOMEventTargetHelper(), mWorkerPrivate(nullptr), mObserver(nullptr), mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang), - mTag(aTag), mIconUrl(aIconUrl), mBehavior(aBehavior), mIsClosed(false), - mIsStored(false), mTaskCount(0) + mTag(aTag), mIconUrl(aIconUrl), mBehavior(aBehavior), mData(JS::NullValue()), + mIsClosed(false), mIsStored(false), mTaskCount(0) { if (NS_IsMainThread()) { // We can only call this on the main thread because @@ -827,13 +827,6 @@ Notification::PersistNotification() nsString alertName; GetAlertName(alertName); - nsString dataString; - nsCOMPtr scContainer; - scContainer = GetDataCloneContainer(); - if (scContainer) { - scContainer->GetDataAsBase64(dataString); - } - nsAutoString behavior; if (!mBehavior.ToJSON(behavior)) { return NS_ERROR_FAILURE; @@ -848,7 +841,7 @@ Notification::PersistNotification() mTag, mIconUrl, alertName, - dataString, + mDataAsBase64, behavior, mScope); @@ -913,6 +906,8 @@ Notification::CreateInternal(nsIGlobalObject* aGlobal, Notification::~Notification() { + mData.setUndefined(); + mozilla::DropJSObjects(this); AssertIsOnTargetThread(); MOZ_ASSERT(!mFeature); MOZ_ASSERT(!mTempRef); @@ -920,15 +915,16 @@ Notification::~Notification() NS_IMPL_CYCLE_COLLECTION_CLASS(Notification) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Notification, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mData) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mDataObjectContainer) + tmp->mData.setUndefined(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Notification, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataObjectContainer) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Notification, DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData); +NS_IMPL_CYCLE_COLLECTION_TRACE_END + NS_IMPL_ADDREF_INHERITED(Notification, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(Notification, DOMEventTargetHelper) @@ -1394,13 +1390,6 @@ Notification::ShowInternal() } MOZ_ASSERT(observer); - // mDataObjectContainer might be uninitialized here because the notification - // was constructed with an undefined data property. - nsString dataStr; - if (mDataObjectContainer) { - mDataObjectContainer->GetDataAsBase64(dataStr); - } - #ifdef MOZ_B2G nsCOMPtr appNotifier = do_GetService("@mozilla.org/system-alerts-service;1"); @@ -1428,7 +1417,7 @@ Notification::ShowInternal() ops.mDir = DirectionToString(mDir); ops.mLang = mLang; ops.mTag = mTag; - ops.mData = dataStr; + ops.mData = mDataAsBase64; ops.mMozbehavior = mBehavior; ops.mMozbehavior.mSoundFile = soundUrl; @@ -1471,7 +1460,7 @@ Notification::ShowInternal() alertService->ShowAlertNotification(iconUrl, mTitle, mBody, true, uniqueCookie, observer, alertName, DirectionToString(mDir), mLang, - dataStr, GetPrincipal(), + mDataAsBase64, GetPrincipal(), inPrivateBrowsing); } @@ -1977,52 +1966,73 @@ Notification::GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin) return NS_OK; } -nsIStructuredCloneContainer* Notification::GetDataCloneContainer() -{ - return mDataObjectContainer; -} - void Notification::GetData(JSContext* aCx, JS::MutableHandle aRetval) { - if (!mData && mDataObjectContainer) { + if (mData.isNull() && !mDataAsBase64.IsEmpty()) { nsresult rv; - rv = mDataObjectContainer->DeserializeToVariant(aCx, getter_AddRefs(mData)); + auto container = new nsStructuredCloneContainer(); + rv = container->InitFromBase64(mDataAsBase64, JS_STRUCTURED_CLONE_VERSION, + aCx); if (NS_WARN_IF(NS_FAILED(rv))) { aRetval.setNull(); return; } + + JS::Rooted data(aCx); + rv = container->DeserializeToJsval(aCx, &data); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRetval.setNull(); + return; + } + + if (data.isGCThing()) { + mozilla::HoldJSObjects(this); + } + mData = data; } - if (!mData) { + if (mData.isNull()) { aRetval.setNull(); return; } - VariantToJsval(aCx, mData, aRetval); + + JS::ExposeValueToActiveJS(mData); + aRetval.set(mData); } void Notification::InitFromJSVal(JSContext* aCx, JS::Handle aData, ErrorResult& aRv) { - if (mDataObjectContainer || aData.isNull()) { + if (!mDataAsBase64.IsEmpty() || aData.isNull()) { return; } - mDataObjectContainer = new nsStructuredCloneContainer(); - aRv = mDataObjectContainer->InitFromJSVal(aData, aCx); + auto dataObjectContainer = new nsStructuredCloneContainer(); + aRv = dataObjectContainer->InitFromJSVal(aData, aCx); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + dataObjectContainer->GetDataAsBase64(mDataAsBase64); } void Notification::InitFromBase64(JSContext* aCx, const nsAString& aData, ErrorResult& aRv) { - if (mDataObjectContainer || aData.IsEmpty()) { + if (!mDataAsBase64.IsEmpty() || aData.IsEmpty()) { return; } + // To and fro to ensure it is valid base64. auto container = new nsStructuredCloneContainer(); aRv = container->InitFromBase64(aData, JS_STRUCTURED_CLONE_VERSION, aCx); - mDataObjectContainer = container; + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + container->GetDataAsBase64(mDataAsBase64); } bool diff --git a/dom/notification/Notification.h b/dom/notification/Notification.h index e7a685b71c14..dd2825d349fa 100644 --- a/dom/notification/Notification.h +++ b/dom/notification/Notification.h @@ -17,7 +17,6 @@ #include "nsCycleCollectionParticipant.h" class nsIPrincipal; -class nsIStructuredCloneContainer; class nsIVariant; namespace mozilla { @@ -126,7 +125,7 @@ public: IMPL_EVENT_HANDLER(close) NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Notification, DOMEventTargetHelper) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(Notification, DOMEventTargetHelper) static bool PrefEnabled(JSContext* aCx, JSObject* aObj); // Returns if Notification.get() is allowed for the current global. @@ -205,8 +204,6 @@ public: return mIsStored; } - nsIStructuredCloneContainer* GetDataCloneContainer(); - static bool RequestPermissionEnabledForScope(JSContext* aCx, JSObject* /* unused */); static void RequestPermission(const GlobalObject& aGlobal, @@ -359,11 +356,11 @@ protected: const nsString mLang; const nsString mTag; const nsString mIconUrl; - nsCOMPtr mDataObjectContainer; + nsString mDataAsBase64; const NotificationBehavior mBehavior; // It's null until GetData is first called - nsCOMPtr mData; + JS::Heap mData; nsString mAlertName; nsString mScope; diff --git a/dom/workers/test/notification_worker.js b/dom/workers/test/notification_worker.js index b054a0a6db1c..cb55f8358566 100644 --- a/dom/workers/test/notification_worker.js +++ b/dom/workers/test/notification_worker.js @@ -20,7 +20,9 @@ if (self.Notification) { lang: "", body: "This is a notification body", tag: "sometag", - icon: "icon.png" + icon: "icon.png", + data: ["a complex object that should be", { "structured": "cloned" }], + mozbehavior: { vibrationPattern: [30, 200, 30] }, }; var notification = new Notification("This is a title", options); @@ -36,6 +38,8 @@ if (self.Notification) { is(notification.body, options.body, "body should get set"); is(notification.tag, options.tag, "tag should get set"); is(notification.icon, options.icon, "icon should get set"); + is(notification.data[0], "a complex object that should be", "data item 0 should be a matching string"); + is(notification.data[1]["structured"], "cloned", "data item 1 should be a matching object literal"); // store notification in test context this.notification = notification; diff --git a/dom/workers/test/serviceworkers/notificationclick.html b/dom/workers/test/serviceworkers/notificationclick.html index 80531b85bb95..448764a1cb33 100644 --- a/dom/workers/test/serviceworkers/notificationclick.html +++ b/dom/workers/test/serviceworkers/notificationclick.html @@ -13,11 +13,11 @@ } navigator.serviceWorker.ready.then(function(swr) { - swr.showNotification("Hi there. The ServiceWorker should receive a click event for this."); + swr.showNotification("Hi there. The ServiceWorker should receive a click event for this.", { data: { complex: ["jsval", 5] }}); }); navigator.serviceWorker.onmessage = function(msg) { - testWindow.callback(); + testWindow.callback(msg.data.result); }; diff --git a/dom/workers/test/serviceworkers/notificationclick.js b/dom/workers/test/serviceworkers/notificationclick.js index 63ab71c35bd5..39e7adb8157d 100644 --- a/dom/workers/test/serviceworkers/notificationclick.js +++ b/dom/workers/test/serviceworkers/notificationclick.js @@ -9,7 +9,11 @@ onnotificationclick = function(e) { } clients.forEach(function(client) { - client.postMessage("done"); + client.postMessage({ result: e.notification.data && + e.notification.data['complex'] && + e.notification.data['complex'][0] == "jsval" && + e.notification.data['complex'][1] == 5 }); + }); }); } diff --git a/dom/workers/test/serviceworkers/test_notificationclick.html b/dom/workers/test/serviceworkers/test_notificationclick.html index 258736137f0e..04d63184b01b 100644 --- a/dom/workers/test/serviceworkers/test_notificationclick.html +++ b/dom/workers/test/serviceworkers/test_notificationclick.html @@ -23,11 +23,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=916893 function testFrame(src) { var iframe = document.createElement("iframe"); iframe.src = src; - window.callback = function() { + window.callback = function(result) { window.callback = null; document.body.removeChild(iframe); iframe = null; - ok(true, "Got notificationclick event."); + ok(result, "Got notificationclick event with correct data."); MockServices.unregister(); SimpleTest.finish(); }; From 97ea06c0c0d1d384ddc5b3b0fe9958895dcd56b4 Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Wed, 29 Jul 2015 11:33:48 -0700 Subject: [PATCH 046/113] Bug 1188686 - Clear push subscriptions when forgetting about site. r=kitcambridge --HG-- extra : commitid : 4z3omFAziAN extra : transplant_source : %0A%E7%85%26%EE9%CE%95%0C/%AC%7E%89%ED%08%9A%3C%99mA --- .../push/nsIPushNotificationService.idl | 11 +++- dom/push/PushDB.jsm | 30 +++++++++ dom/push/PushNotificationService.js | 4 ++ dom/push/PushService.jsm | 42 +++++++++++- dom/push/test/xpcshell/head.js | 2 +- toolkit/forgetaboutsite/ForgetAboutSite.jsm | 10 +++ .../test/unit/head_forgetaboutsite.js | 6 +- .../test/unit/test_removeDataFromDomain.js | 64 +++++++++++++++++++ .../forgetaboutsite/test/unit/xpcshell.ini | 2 +- 9 files changed, 162 insertions(+), 9 deletions(-) diff --git a/dom/interfaces/push/nsIPushNotificationService.idl b/dom/interfaces/push/nsIPushNotificationService.idl index 7dd1ac51b0ea..da947aaf1b7f 100644 --- a/dom/interfaces/push/nsIPushNotificationService.idl +++ b/dom/interfaces/push/nsIPushNotificationService.idl @@ -11,7 +11,7 @@ * uses service workers to notify applications. This interface exists to allow * privileged code to receive messages without migrating to service workers. */ -[scriptable, uuid(abde228b-7d14-4cab-b1f9-9f87750ede0f)] +[scriptable, uuid(74586476-d73f-4867-bece-87c1dea35750)] interface nsIPushNotificationService : nsISupports { /** @@ -48,7 +48,12 @@ interface nsIPushNotificationService : nsISupports jsval registration(in string scope, in jsval originAttributes); /** - * Clear all subscriptions + * Clear all subscriptions. */ - jsval clearAll(); + jsval clearAll(); + + /** + * Clear subscriptions for a domain. + */ + jsval clearForDomain(in string domain); }; diff --git a/dom/push/PushDB.jsm b/dom/push/PushDB.jsm index 613d2187802b..60a4f336219e 100644 --- a/dom/push/PushDB.jsm +++ b/dom/push/PushDB.jsm @@ -154,6 +154,36 @@ this.PushDB.prototype = { ); }, + // testFn(record) is called with a database record and should return true if + // that record should be deleted. + clearIf: function(testFn) { + debug("clearIf()"); + return new Promise((resolve, reject) => + this.newTxn( + "readwrite", + this._dbStoreName, + (aTxn, aStore) => { + aTxn.result = undefined; + + aStore.openCursor().onsuccess = event => { + let cursor = event.target.result; + if (cursor) { + if (testFn(this.toPushRecord(cursor.value))) { + let deleteRequest = cursor.delete(); + deleteRequest.onerror = e => { + debug("Failed to delete entry even when test succeeded!"); + } + } + cursor.continue(); + } + } + }, + resolve, + reject + ) + ); + }, + getByPushEndpoint: function(aPushEndpoint) { debug("getByPushEndpoint()"); diff --git a/dom/push/PushNotificationService.js b/dom/push/PushNotificationService.js index 4ce0189017de..9ecc8f671dba 100644 --- a/dom/push/PushNotificationService.js +++ b/dom/push/PushNotificationService.js @@ -58,6 +58,10 @@ PushNotificationService.prototype = { return PushService._clearAll(); }, + clearForDomain: function(domain) { + return PushService._clearForDomain(domain); + }, + observe: function observe(subject, topic, data) { switch (topic) { case "app-startup": diff --git a/dom/push/PushService.jsm b/dom/push/PushService.jsm index 88ba57a7d2fd..471a08fa5e4e 100644 --- a/dom/push/PushService.jsm +++ b/dom/push/PushService.jsm @@ -1027,7 +1027,47 @@ this.PushService = { _clearAll: function _clearAll() { return this._checkActivated() .then(_ => this._db.clearAll()) - .catch(_ => { + .catch(_ => Promise.resolve()); + }, + + _clearForDomain: function(domain) { + /** + * Copied from ForgetAboutSite.jsm. + * + * Returns true if the string passed in is part of the root domain of the + * current string. For example, if this is "www.mozilla.org", and we pass in + * "mozilla.org", this will return true. It would return false the other way + * around. + */ + function hasRootDomain(str, aDomain) + { + let index = str.indexOf(aDomain); + // If aDomain is not found, we know we do not have it as a root domain. + if (index == -1) + return false; + + // If the strings are the same, we obviously have a match. + if (str == aDomain) + return true; + + // Otherwise, we have aDomain as our root domain iff the index of aDomain is + // aDomain.length subtracted from our length and (since we do not have an + // exact match) the character before the index is a dot or slash. + let prevChar = str[index - 1]; + return (index == (str.length - aDomain.length)) && + (prevChar == "." || prevChar == "/"); + } + + let clear = (db, domain) => { + db.clearIf(record => { + return hasRootDomain(record.origin, domain); + }); + } + + return this._checkActivated() + .then(_ => clear(this._db, domain)) + .catch(e => { + debug("Error forgetting about domain! " + e); return Promise.resolve(); }); }, diff --git a/dom/push/test/xpcshell/head.js b/dom/push/test/xpcshell/head.js index 327812ca58b7..aa1ce9b2c753 100644 --- a/dom/push/test/xpcshell/head.js +++ b/dom/push/test/xpcshell/head.js @@ -3,7 +3,7 @@ 'use strict'; -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; +let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); diff --git a/toolkit/forgetaboutsite/ForgetAboutSite.jsm b/toolkit/forgetaboutsite/ForgetAboutSite.jsm index ad9f5a46370a..bbff491c40cc 100644 --- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm +++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm @@ -181,6 +181,16 @@ this.ForgetAboutSite = { let np = Cc["@mozilla.org/network/predictor;1"]. getService(Ci.nsINetworkPredictor); np.reset(); + + // Push notifications. + try { + var push = Cc["@mozilla.org/push/NotificationService;1"] + .getService(Ci.nsIPushNotificationService); + push.clearForDomain(aDomain); + } catch (e) { + dump("Web Push may not be available.\n"); + } + return Promise.all(promises); } }; diff --git a/toolkit/forgetaboutsite/test/unit/head_forgetaboutsite.js b/toolkit/forgetaboutsite/test/unit/head_forgetaboutsite.js index 8e7a9ea4c428..ba72a264f8a6 100644 --- a/toolkit/forgetaboutsite/test/unit/head_forgetaboutsite.js +++ b/toolkit/forgetaboutsite/test/unit/head_forgetaboutsite.js @@ -2,9 +2,9 @@ * 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/. */ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; +let Cc = Components.classes; +let Ci = Components.interfaces; +let Cu = Components.utils; var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); var profileDir = do_get_profile(); diff --git a/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js b/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js index d9a40aa98150..a6793091c70b 100644 --- a/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js +++ b/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js @@ -462,6 +462,67 @@ function test_content_preferences_not_cleared_with_uri_contains_domain() do_check_false(yield preference_exists(TEST_URI)); } +// Push +function test_push_cleared() +{ + let ps; + try { + ps = Cc["@mozilla.org/push/NotificationService;1"]. + getService(Ci.nsIPushNotificationService); + } catch(e) { + // No push service, skip test. + return; + } + + do_get_profile(); + setPrefs(); + const {PushDB, PushService, PushServiceWebSocket} = serviceExports; + const userAgentID = 'bd744428-f125-436a-b6d0-dd0c9845837f'; + const channelID = '0ef2ad4a-6c49-41ad-af6e-95d2425276bf'; + + let db = PushServiceWebSocket.newPushDB(); + do_register_cleanup(() => {return db.drop().then(_ => db.close());}); + + PushService.init({ + serverURI: "wss://push.example.org/", + networkInfo: new MockDesktopNetworkInfo(), + db, + makeWebSocket(uri) { + return new MockWebSocket(uri, { + onHello(request) { + this.serverSendMsg(JSON.stringify({ + messageType: 'hello', + status: 200, + uaid: userAgentID, + })); + }, + }); + } + }); + + function push_registration_exists(aURL, ps) + { + return ps.registration(aURL, ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })) + .then(record => !!record) + .catch(_ => false); + } + + const TEST_URL = "https://www.mozilla.org/scope/"; + do_check_false(yield push_registration_exists(TEST_URL, ps)); + yield db.put({ + channelID, + pushEndpoint: 'https://example.org/update/clear-success', + scope: TEST_URL, + version: 1, + originAttributes: '', + quota: Infinity, + }); + do_check_true(yield push_registration_exists(TEST_URL, ps)); + ForgetAboutSite.removeDataFromDomain("mozilla.org"); + yield waitForPurgeNotification(); + do_check_false(yield push_registration_exists(TEST_URL, ps)); +} + // Cache function test_cache_cleared() { @@ -555,6 +616,9 @@ let tests = [ test_content_preferences_cleared_with_subdomain, test_content_preferences_not_cleared_with_uri_contains_domain, + // Push + test_push_cleared, + // Storage test_storage_cleared, diff --git a/toolkit/forgetaboutsite/test/unit/xpcshell.ini b/toolkit/forgetaboutsite/test/unit/xpcshell.ini index a4e800e2446d..0d1abb25f43a 100644 --- a/toolkit/forgetaboutsite/test/unit/xpcshell.ini +++ b/toolkit/forgetaboutsite/test/unit/xpcshell.ini @@ -1,5 +1,5 @@ [DEFAULT] -head = head_forgetaboutsite.js +head = head_forgetaboutsite.js ../../../../dom/push/test/xpcshell/head.js tail = skip-if = toolkit == 'android' || toolkit == 'gonk' From 7781b0599dc18dcaa49c25b40f13f2e9d4f3495e Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Sat, 1 Aug 2015 11:53:03 -0400 Subject: [PATCH 047/113] Bug 1190081 - Keep the tab muted icon visible and functional even when the audio playback on the tab has been stopped; r=jaws --- browser/base/content/tabbrowser.xml | 12 +++++++++--- .../test/general/browser_audioTabIcon.js | 12 ++++++++++++ browser/themes/shared/tabs.inc.css | 18 +++++++++++++----- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 392d421f4438..43826467537a 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -5885,20 +5885,24 @@ diff --git a/browser/base/content/test/general/browser_audioTabIcon.js b/browser/base/content/test/general/browser_audioTabIcon.js index cd00ccf36f90..aa389e98e305 100644 --- a/browser/base/content/test/general/browser_audioTabIcon.js +++ b/browser/base/content/test/general/browser_audioTabIcon.js @@ -87,11 +87,23 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) { yield test_tooltip(icon, "This tab is playing audio"); + yield test_mute_tab(tab, icon, true); + yield ContentTask.spawn(browser, {}, function* () { let audio = content.document.querySelector("audio"); audio.pause(); }); yield wait_for_tab_playing_event(tab, false); + + ok(tab.hasAttribute("muted") && + !tab.hasAttribute("soundplaying"), "Tab should still be muted but not playing"); + + yield test_tooltip(icon, "This tab has been muted"); + + yield test_mute_tab(tab, icon, false); + + ok(!tab.hasAttribute("muted") && + !tab.hasAttribute("soundplaying"), "Tab should not be be muted or playing"); } function* test_on_browser(browser) { diff --git a/browser/themes/shared/tabs.inc.css b/browser/themes/shared/tabs.inc.css index 498b7ced1336..e78822aee72f 100644 --- a/browser/themes/shared/tabs.inc.css +++ b/browser/themes/shared/tabs.inc.css @@ -101,20 +101,27 @@ list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg"); } -.tab-icon-overlay[soundplaying][pinned] { +.tab-icon-overlay[soundplaying][pinned], +.tab-icon-overlay[muted][pinned] { display: -moz-box; - list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio"); border-radius: 8px; } +.tab-icon-overlay[soundplaying][pinned]:hover, +.tab-icon-overlay[muted][pinned]:hover { + background-color: white; +} + +.tab-icon-overlay[soundplaying][pinned] { + list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio"); +} + .tab-icon-overlay[soundplaying][pinned]:hover { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover"); - background-color: white; } .tab-icon-overlay[soundplaying][pinned]:hover:active { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed"); - background-color: white; } .tab-icon-overlay[muted][pinned] { @@ -179,7 +186,7 @@ padding: 0; } -.tab-icon-sound:not([soundplaying]), +.tab-icon-sound:not([soundplaying]):not([muted]), .tab-icon-sound[pinned] { display: none; } @@ -414,6 +421,7 @@ .tab-background-middle, .tabs-newtab-button, .tab-icon-overlay[soundplaying], +.tab-icon-overlay[muted], .tab-icon-sound, .tab-close-button { pointer-events: auto; From f87c728a4a51a83d26c2ee1ae1ae8b7d09fc6bf8 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Sat, 1 Aug 2015 18:10:29 -0400 Subject: [PATCH 048/113] Bug 1190106 - Preserve the playing and muted icons when swapping browsers; r=jaws --- browser/base/content/tabbrowser.xml | 16 +++++- .../test/general/browser_audioTabIcon.js | 52 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 43826467537a..eb62e9b856fd 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -2450,6 +2450,16 @@ if (!remoteBrowser._beginRemoveTab(aOtherTab, true, true)) return; + let modifiedAttrs = []; + if (aOtherTab.hasAttribute("muted")) { + aOurTab.setAttribute("muted", "true"); + modifiedAttrs.push("muted"); + } + if (aOtherTab.hasAttribute("soundplaying")) { + aOurTab.setAttribute("soundplaying", "true"); + modifiedAttrs.push("soundplaying"); + } + let ourBrowser = this.getBrowserForTab(aOurTab); let otherBrowser = aOtherTab.linkedBrowser; @@ -2469,7 +2479,7 @@ var isBusy = aOtherTab.hasAttribute("busy"); if (isBusy) { aOurTab.setAttribute("busy", "true"); - this._tabAttrModified(aOurTab, ["busy"]); + modifiedAttrs.push("busy"); if (aOurTab.selected) this.mIsBusy = true; } @@ -2499,6 +2509,10 @@ // of replaceTabWithWindow), notify onLocationChange, etc. if (aOurTab.selected) this.updateCurrentBrowser(true); + + if (modifiedAttrs.length) { + this._tabAttrModified(aOurTab, modifiedAttrs); + } ]]> diff --git a/browser/base/content/test/general/browser_audioTabIcon.js b/browser/base/content/test/general/browser_audioTabIcon.js index aa389e98e305..1ad9f7e1eed1 100644 --- a/browser/base/content/test/general/browser_audioTabIcon.js +++ b/browser/base/content/test/general/browser_audioTabIcon.js @@ -106,6 +106,56 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) { !tab.hasAttribute("soundplaying"), "Tab should not be be muted or playing"); } +function* test_swapped_browser(oldTab, newBrowser, isPlaying) { + ok(oldTab.hasAttribute("muted"), "Expected the correct muted attribute on the old tab"); + is(oldTab.hasAttribute("soundplaying"), isPlaying, "Expected the correct soundplaying attribute on the old tab"); + + let newTab = gBrowser.getTabForBrowser(newBrowser); + let attrChangePromise = BrowserTestUtils.waitForEvent(newTab, "TabAttrModified", false, event => { + return (event.detail.changed.indexOf("soundplaying") >= 0 || !isPlaying) && + event.detail.changed.indexOf("muted") >= 0; + }); + + gBrowser.swapBrowsersAndCloseOther(newTab, oldTab); + yield attrChangePromise; + + ok(newTab.hasAttribute("muted"), "Expected the correct muted attribute on the new tab"); + is(newTab.hasAttribute("soundplaying"), isPlaying, "Expected the correct soundplaying attribute on the new tab"); +} + +function* test_browser_swapping(tab, browser) { + // First, test swapping with a playing but muted tab. + yield ContentTask.spawn(browser, {}, function* () { + let audio = content.document.querySelector("audio"); + audio.play(); + }); + yield wait_for_tab_playing_event(tab, true); + + let icon = document.getAnonymousElementByAttribute(tab, "anonid", + "soundplaying-icon"); + yield test_mute_tab(tab, icon, true); + + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank", + }, function*(newBrowser) { + yield test_swapped_browser(tab, newBrowser, true) + + // Now, test swapping with a muted but not playing tab. + // Note that the tab remains muted, so we only need to pause playback. + tab = gBrowser.getTabForBrowser(newBrowser); + yield ContentTask.spawn(newBrowser, {}, function* () { + let audio = content.document.querySelector("audio"); + audio.pause(); + }); + + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "about:blank", + }, newBrowser => test_swapped_browser(tab, newBrowser, false)); + }); +} + function* test_on_browser(browser) { let tab = gBrowser.getTabForBrowser(browser); @@ -125,6 +175,8 @@ function* test_on_browser(browser) { gBrowser, url: "data:text/html,test" }, () => test_on_browser(browser)); + } else { + yield test_browser_swapping(tab, browser); } } From d3364c4ca1d7b9e38edb193cbdfcecd2588e7cb3 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Sun, 2 Aug 2015 10:17:50 +0200 Subject: [PATCH 049/113] Backed out 8fdfa9d502f8 (bug 1190106) for M-e10s(bc2) bustage. r=backout --- browser/base/content/tabbrowser.xml | 16 +----- .../test/general/browser_audioTabIcon.js | 52 ------------------- 2 files changed, 1 insertion(+), 67 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index eb62e9b856fd..43826467537a 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -2450,16 +2450,6 @@ if (!remoteBrowser._beginRemoveTab(aOtherTab, true, true)) return; - let modifiedAttrs = []; - if (aOtherTab.hasAttribute("muted")) { - aOurTab.setAttribute("muted", "true"); - modifiedAttrs.push("muted"); - } - if (aOtherTab.hasAttribute("soundplaying")) { - aOurTab.setAttribute("soundplaying", "true"); - modifiedAttrs.push("soundplaying"); - } - let ourBrowser = this.getBrowserForTab(aOurTab); let otherBrowser = aOtherTab.linkedBrowser; @@ -2479,7 +2469,7 @@ var isBusy = aOtherTab.hasAttribute("busy"); if (isBusy) { aOurTab.setAttribute("busy", "true"); - modifiedAttrs.push("busy"); + this._tabAttrModified(aOurTab, ["busy"]); if (aOurTab.selected) this.mIsBusy = true; } @@ -2509,10 +2499,6 @@ // of replaceTabWithWindow), notify onLocationChange, etc. if (aOurTab.selected) this.updateCurrentBrowser(true); - - if (modifiedAttrs.length) { - this._tabAttrModified(aOurTab, modifiedAttrs); - } ]]> diff --git a/browser/base/content/test/general/browser_audioTabIcon.js b/browser/base/content/test/general/browser_audioTabIcon.js index 1ad9f7e1eed1..aa389e98e305 100644 --- a/browser/base/content/test/general/browser_audioTabIcon.js +++ b/browser/base/content/test/general/browser_audioTabIcon.js @@ -106,56 +106,6 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) { !tab.hasAttribute("soundplaying"), "Tab should not be be muted or playing"); } -function* test_swapped_browser(oldTab, newBrowser, isPlaying) { - ok(oldTab.hasAttribute("muted"), "Expected the correct muted attribute on the old tab"); - is(oldTab.hasAttribute("soundplaying"), isPlaying, "Expected the correct soundplaying attribute on the old tab"); - - let newTab = gBrowser.getTabForBrowser(newBrowser); - let attrChangePromise = BrowserTestUtils.waitForEvent(newTab, "TabAttrModified", false, event => { - return (event.detail.changed.indexOf("soundplaying") >= 0 || !isPlaying) && - event.detail.changed.indexOf("muted") >= 0; - }); - - gBrowser.swapBrowsersAndCloseOther(newTab, oldTab); - yield attrChangePromise; - - ok(newTab.hasAttribute("muted"), "Expected the correct muted attribute on the new tab"); - is(newTab.hasAttribute("soundplaying"), isPlaying, "Expected the correct soundplaying attribute on the new tab"); -} - -function* test_browser_swapping(tab, browser) { - // First, test swapping with a playing but muted tab. - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.play(); - }); - yield wait_for_tab_playing_event(tab, true); - - let icon = document.getAnonymousElementByAttribute(tab, "anonid", - "soundplaying-icon"); - yield test_mute_tab(tab, icon, true); - - yield BrowserTestUtils.withNewTab({ - gBrowser, - url: "about:blank", - }, function*(newBrowser) { - yield test_swapped_browser(tab, newBrowser, true) - - // Now, test swapping with a muted but not playing tab. - // Note that the tab remains muted, so we only need to pause playback. - tab = gBrowser.getTabForBrowser(newBrowser); - yield ContentTask.spawn(newBrowser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.pause(); - }); - - yield BrowserTestUtils.withNewTab({ - gBrowser, - url: "about:blank", - }, newBrowser => test_swapped_browser(tab, newBrowser, false)); - }); -} - function* test_on_browser(browser) { let tab = gBrowser.getTabForBrowser(browser); @@ -175,8 +125,6 @@ function* test_on_browser(browser) { gBrowser, url: "data:text/html,test" }, () => test_on_browser(browser)); - } else { - yield test_browser_swapping(tab, browser); } } From a2c1faa15b66f67a4fe560170a7224f46833ac27 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Sun, 2 Aug 2015 12:34:26 +0300 Subject: [PATCH 050/113] Bug 1183954, Don't leak nsStructuredCloneContainer (use of 'auto' is error prone), r=leak --- dom/notification/Notification.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index ac7a9f3c2943..8b750aa2f81c 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -1972,7 +1972,8 @@ Notification::GetData(JSContext* aCx, { if (mData.isNull() && !mDataAsBase64.IsEmpty()) { nsresult rv; - auto container = new nsStructuredCloneContainer(); + nsRefPtr container = + new nsStructuredCloneContainer(); rv = container->InitFromBase64(mDataAsBase64, JS_STRUCTURED_CLONE_VERSION, aCx); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -2008,7 +2009,8 @@ Notification::InitFromJSVal(JSContext* aCx, JS::Handle aData, if (!mDataAsBase64.IsEmpty() || aData.isNull()) { return; } - auto dataObjectContainer = new nsStructuredCloneContainer(); + nsRefPtr dataObjectContainer = + new nsStructuredCloneContainer(); aRv = dataObjectContainer->InitFromJSVal(aData, aCx); if (NS_WARN_IF(aRv.Failed())) { return; @@ -2025,7 +2027,8 @@ void Notification::InitFromBase64(JSContext* aCx, const nsAString& aData, } // To and fro to ensure it is valid base64. - auto container = new nsStructuredCloneContainer(); + nsRefPtr container = + new nsStructuredCloneContainer(); aRv = container->InitFromBase64(aData, JS_STRUCTURED_CLONE_VERSION, aCx); if (NS_WARN_IF(aRv.Failed())) { From 6d2572150631e4397b85c626d1f1a3c96979a4a9 Mon Sep 17 00:00:00 2001 From: Mike Taylor Date: Sun, 2 Aug 2015 10:27:44 -0700 Subject: [PATCH 051/113] Bug 1189922. Add a preference to enable global whitelisting of the CSSUnprefixingService. r=dholbert This is a temporary preference to allow for testing-in-the-wild by a larger audience, for nightly builds. When 1177263 is fixed this can be removed. --- layout/style/nsCSSParser.cpp | 14 ++++++++++++++ modules/libpref/init/all.js | 6 ++++++ 2 files changed, 20 insertions(+) --- layout/style/nsCSSParser.cpp | 14 ++++++++++++++ modules/libpref/init/all.js | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index f17299297d85..84e3be7f3ca3 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -58,6 +58,9 @@ typedef nsCSSProps::KTableValue KTableValue; // pref-backed bool values (hooked up in nsCSSParser::Startup) static bool sOpentypeSVGEnabled; static bool sUnprefixingServiceEnabled; +#ifdef NIGHTLY_BUILD +static bool sUnprefixingServiceGloballyWhitelisted; +#endif static bool sMozGradientsEnabled; const uint32_t @@ -6666,6 +6669,13 @@ CSSParserImpl::ShouldUseUnprefixingService() return false; } +#ifdef NIGHTLY_BUILD + if (sUnprefixingServiceGloballyWhitelisted) { + // Unprefixing is globally whitelisted, + // so no need to check mSheetPrincipal. + return true; + } +#endif // Unprefixing enabled; see if our principal is whitelisted for unprefixing. return mSheetPrincipal && mSheetPrincipal->IsOnCSSUnprefixingWhitelist(); } @@ -15677,6 +15687,10 @@ nsCSSParser::Startup() "gfx.font_rendering.opentype_svg.enabled"); Preferences::AddBoolVarCache(&sUnprefixingServiceEnabled, "layout.css.unprefixing-service.enabled"); +#ifdef NIGHTLY_BUILD + Preferences::AddBoolVarCache(&sUnprefixingServiceGloballyWhitelisted, + "layout.css.unprefixing-service.globally-whitelisted"); +#endif Preferences::AddBoolVarCache(&sMozGradientsEnabled, "layout.css.prefixes.gradients"); } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index fabf83aec7b4..23c742e38094 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2286,6 +2286,12 @@ pref("layout.css.prefixes.gradients", true); // Is the CSS Unprefixing Service enabled? (This service emulates support // for certain vendor-prefixed properties & values, for sites on a "fixlist".) pref("layout.css.unprefixing-service.enabled", true); +#ifdef NIGHTLY_BUILD +// Is the CSS Unprefixing Service whitelisted for all domains? +// (This pref is only honored in Nightly builds and can be removed when +// Bug 1177263 is fixed.) +pref("layout.css.unprefixing-service.globally-whitelisted", false); +#endif // Is support for the :scope selector enabled? pref("layout.css.scope-pseudo.enabled", true); From 5d6e8c751fdf6488028e2251fba4ab4a02e25ddb Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Mon, 20 Jul 2015 13:59:19 -0700 Subject: [PATCH 052/113] Bug 1152574 - Do not report aborted XHR requests in web console (r=sicking) --- dom/locales/en-US/chrome/security/security.properties | 1 - dom/security/nsCORSListenerProxy.cpp | 10 ++-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/dom/locales/en-US/chrome/security/security.properties b/dom/locales/en-US/chrome/security/security.properties index 2a2b4d8def56..46a68c116c4c 100644 --- a/dom/locales/en-US/chrome/security/security.properties +++ b/dom/locales/en-US/chrome/security/security.properties @@ -6,7 +6,6 @@ BlockMixedActiveContent = Blocked loading mixed active content "%1$S" # CORS # LOCALIZATION NOTE: Do not translate "Access-Control-Allow-Origin", Access-Control-Allow-Credentials, Access-Control-Allow-Methods, Access-Control-Allow-Headers CORSDisabled=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS disabled). -CORSRequestFailed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request failed). CORSRequestNotHttp=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request not http). CORSMissingAllowOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header 'Access-Control-Allow-Origin' missing). CORSAllowOriginNotMatchingOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header 'Access-Control-Allow-Origin' does not match '%2$S'). diff --git a/dom/security/nsCORSListenerProxy.cpp b/dom/security/nsCORSListenerProxy.cpp index 5e167cc9c621..613f283bd4c5 100644 --- a/dom/security/nsCORSListenerProxy.cpp +++ b/dom/security/nsCORSListenerProxy.cpp @@ -538,14 +538,8 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest) // Check if the request failed nsresult status; nsresult rv = aRequest->GetStatus(&status); - if (NS_FAILED(rv)) { - LogBlockedRequest(aRequest, "CORSRequestFailed", nullptr); - return rv; - } - if (NS_FAILED(status)) { - LogBlockedRequest(aRequest, "CORSRequestFailed", nullptr); - return status; - } + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(status, status); // Test that things worked on a HTTP level nsCOMPtr http = do_QueryInterface(aRequest); From ccd1961c443a677dae8b8b4518877ec4b035166c Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Sun, 2 Aug 2015 10:42:22 -0700 Subject: [PATCH 053/113] Bug 1127534 - Remove assertion before creating a channel (r=sicking) --- image/imgLoader.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/image/imgLoader.cpp b/image/imgLoader.cpp index 21f55ca64ee0..416c003f67a9 100644 --- a/image/imgLoader.cpp +++ b/image/imgLoader.cpp @@ -789,7 +789,9 @@ NewImageChannel(nsIChannel** aResult, // we should always have a requestingNode, or we are loading something // outside a document, in which case the triggeringPrincipal // should always be the systemPrincipal. - MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(triggeringPrincipal)); + // However, there are two exceptions: one is Notifications and the + // other one is Favicons which create a channel in the parent prcoess + // in which case we can't get a requestingNode. rv = NS_NewChannel(aResult, aURI, triggeringPrincipal, From ac8c6304a9c6474c8158fea292ca360d4737c5d2 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sun, 2 Aug 2015 13:59:33 -0700 Subject: [PATCH 054/113] Use the same graphics device parameters across processes. (bug 1183910 part 7, r=mattwoodrow) --- dom/cache/PCacheStorage.ipdl | 1 + dom/ipc/ContentChild.cpp | 8 ++ dom/ipc/ContentChild.h | 1 + dom/ipc/ContentParent.cpp | 7 ++ dom/ipc/ContentParent.h | 1 + dom/ipc/ContentProcess.cpp | 5 +- dom/ipc/PContent.ipdl | 7 ++ gfx/ipc/GraphicsMessages.ipdlh | 25 ++++++ gfx/ipc/moz.build | 4 + gfx/thebes/gfxPlatform.cpp | 22 ++++++ gfx/thebes/gfxPlatform.h | 19 ++++- gfx/thebes/gfxWindowsPlatform.cpp | 126 ++++++++++++++++++++++++++---- gfx/thebes/gfxWindowsPlatform.h | 5 ++ 13 files changed, 214 insertions(+), 17 deletions(-) create mode 100644 gfx/ipc/GraphicsMessages.ipdlh diff --git a/dom/cache/PCacheStorage.ipdl b/dom/cache/PCacheStorage.ipdl index ef06005b4125..fa11de48fbc7 100644 --- a/dom/cache/PCacheStorage.ipdl +++ b/dom/cache/PCacheStorage.ipdl @@ -6,6 +6,7 @@ include protocol PBackground; include protocol PBlob; // FIXME: bug 792908 include protocol PCache; include protocol PCacheOp; +include protocol PCachePushStream; include protocol PCacheStreamControl; include protocol PFileDescriptorSet; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index ebf99617495b..21a3cd023126 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -763,6 +763,14 @@ ContentChild::AppendProcessId(nsACString& aName) aName.Append(nsPrintfCString("(pid %u)", pid)); } +void +ContentChild::InitGraphicsDeviceData() +{ + // Initialize the graphics platform. This may contact the parent process + // to read device preferences. + gfxPlatform::GetPlatform(); +} + void ContentChild::InitXPCOM() { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 5fe6698b8f45..afd38b29c1eb 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -76,6 +76,7 @@ public: IPC::Channel* aChannel); void InitProcessAttributes(); void InitXPCOM(); + void InitGraphicsDeviceData(); static ContentChild* GetSingleton() { return sSingleton; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 559835a2d8b9..ff2aa963ed5c 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5148,6 +5148,13 @@ ContentParent::RecvProfile(const nsCString& aProfile) return true; } +bool +ContentParent::RecvGetGraphicsDeviceInitData(DeviceInitData* aOut) +{ + gfxPlatform::GetPlatform()->GetDeviceInitData(aOut); + return true; +} + } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index fe9af7072c3f..f55d6131d9a5 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -862,6 +862,7 @@ private: virtual bool RecvGamepadListenerAdded() override; virtual bool RecvGamepadListenerRemoved() override; virtual bool RecvProfile(const nsCString& aProfile) override; + virtual bool RecvGetGraphicsDeviceInitData(DeviceInitData* aOut) override; // If you add strong pointers to cycle collected objects here, be sure to // release these objects in ShutDownProcess. See the comment there for more diff --git a/dom/ipc/ContentProcess.cpp b/dom/ipc/ContentProcess.cpp index 41c9cf71041f..230f8fd29589 100644 --- a/dom/ipc/ContentProcess.cpp +++ b/dom/ipc/ContentProcess.cpp @@ -76,10 +76,11 @@ bool ContentProcess::Init() { mContent.Init(IOThreadChild::message_loop(), - ParentPid(), - IOThreadChild::channel()); + ParentPid(), + IOThreadChild::channel()); mXREEmbed.Start(); mContent.InitXPCOM(); + mContent.InitGraphicsDeviceData(); #if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) SetUpSandboxEnvironment(); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 791908915cd5..4e12c18afb8c 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -60,6 +60,7 @@ include PluginTypes; include ProtocolTypes; include PContentPermission; include BrowserConfiguration; +include GraphicsMessages; // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp // are put into different UnifiedProtocolsXX.cpp files. @@ -1053,6 +1054,12 @@ parent: async Profile(nsCString aProfile); + /** + * Request graphics initialization information from the parent. + */ + sync GetGraphicsDeviceInitData() + returns (DeviceInitData aData); + both: AsyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows, Principal aPrincipal); diff --git a/gfx/ipc/GraphicsMessages.ipdlh b/gfx/ipc/GraphicsMessages.ipdlh new file mode 100644 index 000000000000..eef1fe22eb48 --- /dev/null +++ b/gfx/ipc/GraphicsMessages.ipdlh @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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/. */ + +namespace mozilla { +namespace gfx { + +struct DeviceInitData +{ + bool useAcceleration; + + // Windows only. + bool useD3D11; + bool useD3D11WARP; + bool useD3D11ImageBridge; + bool d3d11TextureSharingWorks; + bool useD2D; + bool useD2D1; +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/moz.build b/gfx/ipc/moz.build index db509c25033e..06742a360987 100644 --- a/gfx/ipc/moz.build +++ b/gfx/ipc/moz.build @@ -26,6 +26,10 @@ SOURCES += [ 'SharedDIB.cpp', ] +IPDL_SOURCES = [ + 'GraphicsMessages.ipdlh', +] + include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index a995c32cb1a9..f746df0d83af 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -112,6 +112,7 @@ class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted { #include "VsyncSource.h" #include "SoftwareVsyncSource.h" #include "nscore.h" // for NS_FREE_PERMANENT_DATA +#include "mozilla/dom/ContentChild.h" namespace mozilla { namespace layers { @@ -2488,3 +2489,24 @@ gfxPlatform::NotifyCompositorCreated(LayersBackend aBackend) obsvc->NotifyObservers(nullptr, "compositor:created", nullptr); } } + +void +gfxPlatform::GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + aOut->useAcceleration() = ShouldUseLayersAcceleration(); +} + +void +gfxPlatform::UpdateDeviceInitData() +{ + if (XRE_IsParentProcess()) { + // The parent process figures out device initialization on its own. + return; + } + + mozilla::gfx::DeviceInitData data; + mozilla::dom::ContentChild::GetSingleton()->SendGetGraphicsDeviceInitData(&data); + + SetDeviceInitData(data); +} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 1fd51509ffc6..7f835a516a5b 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -49,6 +49,7 @@ class DataSourceSurface; class ScaledFont; class DrawEventRecorder; class VsyncSource; +class DeviceInitData; inline uint32_t BackendTypeBit(BackendType b) @@ -485,7 +486,8 @@ public: virtual bool CanUseHardwareVideoDecoding(); static bool CanUseDirect3D11ANGLE(); - // Returns whether or not layers acceleration should be used. + // Returns whether or not layers acceleration should be used. This should + // only be called on the parent process. bool ShouldUseLayersAcceleration(); // Returns a prioritized list of all available compositor backends. @@ -646,6 +648,10 @@ public: virtual void TestDeviceReset(DeviceResetReason aReason) {} + // Return information on how child processes should initialize graphics + // devices. Currently this is only used on Windows. + virtual void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut); + protected: gfxPlatform(); virtual ~gfxPlatform(); @@ -678,6 +684,17 @@ protected: void InitBackendPrefs(uint32_t aCanvasBitmask, mozilla::gfx::BackendType aCanvasDefault, uint32_t aContentBitmask, mozilla::gfx::BackendType aContentDefault); + /** + * If in a child process, triggers a refresh of device preferences. + */ + void UpdateDeviceInitData(); + + /** + * Called when new device preferences are available. + */ + virtual void SetDeviceInitData(mozilla::gfx::DeviceInitData& aData) + {} + /** * returns the first backend named in the pref gfx.canvas.azure.backends * which is a component of aBackendBitmask, a bitmask of backend types diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 246223a0ed66..59b9ef7da07b 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -373,6 +373,16 @@ public: NS_IMPL_ISUPPORTS(D3D9SharedTextureReporter, nsIMemoryReporter) +// Device init data should only be used on child processes, so we protect it +// behind a getter here. +static DeviceInitData sDeviceInitDataDoNotUseDirectly; +static inline DeviceInitData& +GetParentDevicePrefs() +{ + MOZ_ASSERT(XRE_IsContentProcess()); + return sDeviceInitDataDoNotUseDirectly; +} + gfxWindowsPlatform::gfxWindowsPlatform() : mRenderMode(RENDER_GDI) , mIsWARP(false) @@ -403,6 +413,7 @@ gfxWindowsPlatform::gfxWindowsPlatform() mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0); mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3); + UpdateDeviceInitData(); InitializeDevices(); UpdateRenderMode(); @@ -518,6 +529,9 @@ gfxWindowsPlatform::HandleDeviceReset() imgLoader::Singleton()->ClearCache(false); gfxAlphaBoxBlur::ShutdownBlurCache(); + // Since we got a device reset, we must ask the parent process for an updated + // list of which devices to create. + UpdateDeviceInitData(); InitializeDevices(); return true; } @@ -1900,6 +1914,12 @@ CanUseWARP() return true; } + // The child process can only use WARP if the parent process is also using + // WARP. + if (XRE_IsContentProcess()) { + return GetParentDevicePrefs().useD3D11WARP(); + } + // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703. if (!IsWin8OrLater() || gfxPrefs::LayersD3D11DisableWARP() || @@ -1918,6 +1938,14 @@ gfxWindowsPlatform::CheckD3D11Support(bool* aCanUseHardware) return mD3D11Status; } + if (XRE_IsContentProcess()) { + if (!GetParentDevicePrefs().useD3D11()) { + return FeatureStatus::Blocked; + } + *aCanUseHardware = !GetParentDevicePrefs().useD3D11WARP(); + return FeatureStatus::Available; + } + if (gfxPrefs::LayersD3D11ForceWARP()) { *aCanUseHardware = false; return FeatureStatus::Available; @@ -1982,6 +2010,12 @@ gfxWindowsPlatform::AttemptD3D11DeviceCreation() // Only test this when not using WARP since it can fail and cause // GetDeviceRemovedReason to return weird values. mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device); + + // Assert that the child and parent process both computed texture sharing + // properly. + MOZ_ASSERT_IF(XRE_IsContentProcess(), + mDoesD3D11TextureSharingWork == GetParentDevicePrefs().d3d11TextureSharingWorks()); + mD3D11Device->SetExceptionMode(0); mIsWARP = false; } @@ -2078,31 +2112,31 @@ gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation() } } +void +gfxWindowsPlatform::SetDeviceInitData(mozilla::gfx::DeviceInitData& aData) +{ + MOZ_ASSERT(XRE_IsContentProcess()); + sDeviceInitDataDoNotUseDirectly = aData; +} + void gfxWindowsPlatform::InitializeDevices() { - // Don't retry acceleration if it failed earlier. + // If acceleration is disabled, we refuse to initialize anything. + mAcceleration = CheckAccelerationSupport(); if (IsFeatureStatusFailure(mAcceleration)) { return; } - // If we previously crashed initializing devices, or if we're in safe mode, - // bail out now. + // If we previously crashed initializing devices, bail out now. This is + // effectively a parent-process only check, since the content process + // cannot create a lock file. DriverInitCrashDetection detectCrashes; - if (detectCrashes.DisableAcceleration() || InSafeMode()) { + if (detectCrashes.DisableAcceleration()) { mAcceleration = FeatureStatus::Blocked; return; } - // If acceleration is disabled, we refuse to initialize anything. - if (!ShouldUseLayersAcceleration()) { - mAcceleration = FeatureStatus::Disabled; - return; - } - - // At this point, as far as we know, we can probably accelerate. - mAcceleration = FeatureStatus::Available; - // If we're going to prefer D3D9, stop here. The rest of this function // attempts to use D3D11 features. if (gfxPrefs::LayersPreferD3D9()) { @@ -2117,6 +2151,38 @@ gfxWindowsPlatform::InitializeDevices() } } +FeatureStatus +gfxWindowsPlatform::CheckAccelerationSupport() +{ + // Don't retry acceleration if it failed earlier. + if (IsFeatureStatusFailure(mAcceleration)) { + return mAcceleration; + } + if (XRE_IsContentProcess()) { + return GetParentDevicePrefs().useAcceleration() + ? FeatureStatus::Available + : FeatureStatus::Blocked; + } + if (InSafeMode()) { + return FeatureStatus::Blocked; + } + if (!ShouldUseLayersAcceleration()) { + return FeatureStatus::Disabled; + } + return FeatureStatus::Available; +} + +bool +gfxWindowsPlatform::CanUseD3D11ImageBridge() +{ + if (XRE_IsContentProcess()) { + if (!GetParentDevicePrefs().useD3D11ImageBridge()) { + return false; + } + } + return !mIsWARP; +} + void gfxWindowsPlatform::InitializeD3D11() { @@ -2170,7 +2236,7 @@ gfxWindowsPlatform::InitializeD3D11() mD3D11Status = FeatureStatus::Available; MOZ_ASSERT(mD3D11Device); - if (!mIsWARP) { + if (CanUseD3D11ImageBridge()) { AttemptD3D11ImageBridgeDeviceCreation(); } @@ -2206,6 +2272,12 @@ gfxWindowsPlatform::CheckD2DSupport() return mD2DStatus; } + if (XRE_IsContentProcess()) { + return GetParentDevicePrefs().useD2D() + ? FeatureStatus::Available + : FeatureStatus::Blocked; + } + if (!gfxPrefs::Direct2DForceEnabled() && IsD2DBlacklisted()) { return FeatureStatus::Blacklisted; } @@ -2266,6 +2338,11 @@ gfxWindowsPlatform::CheckD2D1Support() if (!Factory::SupportsD2D1()) { return FeatureStatus::Unavailable; } + if (XRE_IsContentProcess()) { + return GetParentDevicePrefs().useD2D1() + ? FeatureStatus::Available + : FeatureStatus::Blocked; + } if (!gfxPrefs::Direct2DUse1_1()) { return FeatureStatus::Disabled; } @@ -2609,3 +2686,24 @@ gfxWindowsPlatform::GetD3D11Version() } return device->GetFeatureLevel(); } + +void +gfxWindowsPlatform::GetDeviceInitData(DeviceInitData* aOut) +{ + // Check for device resets before giving back new graphics information. + UpdateRenderMode(); + + gfxPlatform::GetDeviceInitData(aOut); + + // IPDL initializes each field to false for us so we can early return. + if (GetD3D11Status() != FeatureStatus::Available) { + return; + } + + aOut->useD3D11() = true; + aOut->useD3D11ImageBridge() = !!mD3D11ImageBridgeDevice; + aOut->d3d11TextureSharingWorks() = mDoesD3D11TextureSharingWork; + aOut->useD3D11WARP() = mIsWARP; + aOut->useD2D() = (GetD2DStatus() == FeatureStatus::Available); + aOut->useD2D1() = (GetD2D1Status() == FeatureStatus::Available); +} diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 16f12413550f..52b5c0e7182b 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -283,12 +283,15 @@ public: static mozilla::Atomic sD3D9SurfaceImageUsed; static mozilla::Atomic sD3D9SharedTextureUsed; + void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut) override; + protected: bool AccelerateLayersByDefault() override { return true; } void GetAcceleratedCompositorBackends(nsTArray& aBackends); virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size); + void SetDeviceInitData(mozilla::gfx::DeviceInitData& aData) override; protected: RenderMode mRenderMode; @@ -307,6 +310,7 @@ private: void DisableD2D(); + mozilla::gfx::FeatureStatus CheckAccelerationSupport(); mozilla::gfx::FeatureStatus CheckD3D11Support(bool* aCanUseHardware); mozilla::gfx::FeatureStatus CheckD2DSupport(); mozilla::gfx::FeatureStatus CheckD2D1Support(); @@ -314,6 +318,7 @@ private: void AttemptWARPDeviceCreation(); void AttemptD3D11ImageBridgeDeviceCreation(); bool AttemptD3D11ContentDeviceCreation(); + bool CanUseD3D11ImageBridge(); IDXGIAdapter1 *GetDXGIAdapter(); bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason); From 0ebb28aa85a0a6542ef2d480333f59213915ea72 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Mon, 27 Jul 2015 22:41:31 +1200 Subject: [PATCH 055/113] bug 1188704 redefine halfSize as fftSize / 2 r=rillian There are no behavior changes here, just the change in meaning of the variable. --HG-- extra : rebase_source : 9bae9f29a27430e9074dc0337773313140af9ba2 --- dom/media/webaudio/blink/PeriodicWave.cpp | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/dom/media/webaudio/blink/PeriodicWave.cpp b/dom/media/webaudio/blink/PeriodicWave.cpp index 023365cb2fcf..ca65657b5bc8 100644 --- a/dom/media/webaudio/blink/PeriodicWave.cpp +++ b/dom/media/webaudio/blink/PeriodicWave.cpp @@ -178,18 +178,18 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i float normalizationScale = 1; unsigned fftSize = m_periodicWaveSize; - unsigned halfSize = fftSize / 2 + 1; + unsigned halfSize = fftSize / 2; unsigned i; - numberOfComponents = std::min(numberOfComponents, halfSize); + numberOfComponents = std::min(numberOfComponents, halfSize + 1); m_bandLimitedTables.SetCapacity(m_numberOfRanges); for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) { // This FFTBlock is used to cull partials (represented by frequency bins). FFTBlock frame(fftSize); - nsAutoArrayPtr realP(new float[halfSize]); - nsAutoArrayPtr imagP(new float[halfSize]); + nsAutoArrayPtr realP(new float[halfSize + 1]); + nsAutoArrayPtr imagP(new float[halfSize + 1]); // Copy from loaded frequency data and scale. float scale = fftSize; @@ -198,7 +198,7 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i // If fewer components were provided than 1/2 FFT size, // then clear the remaining bins. - for (i = numberOfComponents; i < halfSize; ++i) { + for (i = numberOfComponents; i < halfSize + 1; ++i) { realP[i] = 0; imagP[i] = 0; } @@ -206,7 +206,7 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i // Generate complex conjugate because of the way the // inverse FFT is defined. float minusOne = -1; - AudioBufferInPlaceScale(imagP, minusOne, halfSize); + AudioBufferInPlaceScale(imagP, minusOne, halfSize + 1); // Find the starting bin where we should start culling. // We need to clear out the highest frequencies to band-limit @@ -214,20 +214,20 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); // Cull the aliasing partials for this pitch range. - for (i = numberOfPartials + 1; i < halfSize; ++i) { + for (i = numberOfPartials + 1; i < halfSize + 1; ++i) { realP[i] = 0; imagP[i] = 0; } // Clear nyquist if necessary. - if (numberOfPartials < halfSize) - realP[halfSize-1] = 0; + if (numberOfPartials < halfSize + 1) + realP[halfSize] = 0; // Clear any DC-offset. realP[0] = 0; // Clear values which have no effect. imagP[0] = 0; - imagP[halfSize-1] = 0; + imagP[halfSize] = 0; // Create the band-limited table. AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize); @@ -256,20 +256,20 @@ void PeriodicWave::generateBasicWaveform(OscillatorType shape) { const float piFloat = M_PI; unsigned fftSize = periodicWaveSize(); - unsigned halfSize = fftSize / 2 + 1; + unsigned halfSize = fftSize / 2; - AudioFloatArray real(halfSize); - AudioFloatArray imag(halfSize); + AudioFloatArray real(halfSize + 1); + AudioFloatArray imag(halfSize + 1); float* realP = real.Elements(); float* imagP = imag.Elements(); // Clear DC and Nyquist. realP[0] = 0; imagP[0] = 0; - realP[halfSize-1] = 0; - imagP[halfSize-1] = 0; + realP[halfSize] = 0; + imagP[halfSize] = 0; - for (unsigned n = 1; n < halfSize; ++n) { + for (unsigned n = 1; n < halfSize + 1; ++n) { float omega = 2 * piFloat * n; float invOmega = 1 / omega; @@ -319,7 +319,7 @@ void PeriodicWave::generateBasicWaveform(OscillatorType shape) imagP[n] = b; } - createBandLimitedTables(realP, imagP, halfSize); + createBandLimitedTables(realP, imagP, halfSize + 1); } } // namespace WebCore From 0ffb72d8ebd690b80d5fecef309f25d3d626f7ab Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Mon, 27 Jul 2015 23:55:06 +1200 Subject: [PATCH 056/113] bug 1188704 trim unnecessary extra basic waveform coeffient r=rillian The built-in waveforms are all odd and so realP[halfSize] was zero, and it would have been ignored in createBandLimitedTables even if it was non-zero. imagP[halfSize] was ignored as it was not involved in the inverse FFT. This returns the code to that prior to https://hg.mozilla.org/mozilla-central/diff/5377bce3b478/content/media/webaudio/blink/PeriodicWave.cpp#l1.276 --HG-- extra : rebase_source : 1bfa9eefc7dd269a64a98cdfdd4cbf76fa207dc4 --- dom/media/webaudio/blink/PeriodicWave.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/dom/media/webaudio/blink/PeriodicWave.cpp b/dom/media/webaudio/blink/PeriodicWave.cpp index ca65657b5bc8..17c936786ec4 100644 --- a/dom/media/webaudio/blink/PeriodicWave.cpp +++ b/dom/media/webaudio/blink/PeriodicWave.cpp @@ -258,18 +258,16 @@ void PeriodicWave::generateBasicWaveform(OscillatorType shape) unsigned fftSize = periodicWaveSize(); unsigned halfSize = fftSize / 2; - AudioFloatArray real(halfSize + 1); - AudioFloatArray imag(halfSize + 1); + AudioFloatArray real(halfSize); + AudioFloatArray imag(halfSize); float* realP = real.Elements(); float* imagP = imag.Elements(); - // Clear DC and Nyquist. + // Clear DC and imag value which is ignored. realP[0] = 0; imagP[0] = 0; - realP[halfSize] = 0; - imagP[halfSize] = 0; - for (unsigned n = 1; n < halfSize + 1; ++n) { + for (unsigned n = 1; n < halfSize; ++n) { float omega = 2 * piFloat * n; float invOmega = 1 / omega; @@ -319,7 +317,7 @@ void PeriodicWave::generateBasicWaveform(OscillatorType shape) imagP[n] = b; } - createBandLimitedTables(realP, imagP, halfSize + 1); + createBandLimitedTables(realP, imagP, halfSize); } } // namespace WebCore From a4daa97373aa3300ad38624ff93fcc1e5879cad3 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Thu, 30 Jul 2015 10:08:15 +1200 Subject: [PATCH 057/113] bug 1188704 limit number of Fourier coefficients used to halfSize earlier r=rillian realP[halfSize] was always set to zero because numberOfPartials < halfSize + 1 was always true. --HG-- extra : rebase_source : 819b04a56032d810231a16fa0dd139cf82ef8ddf --- dom/media/webaudio/blink/PeriodicWave.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dom/media/webaudio/blink/PeriodicWave.cpp b/dom/media/webaudio/blink/PeriodicWave.cpp index 17c936786ec4..64b4619a7ffd 100644 --- a/dom/media/webaudio/blink/PeriodicWave.cpp +++ b/dom/media/webaudio/blink/PeriodicWave.cpp @@ -181,7 +181,9 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i unsigned halfSize = fftSize / 2; unsigned i; - numberOfComponents = std::min(numberOfComponents, halfSize + 1); + // Limit the number of components used to those for frequencies below the + // Nyquist of the fixed length inverse FFT. + numberOfComponents = std::min(numberOfComponents, halfSize); m_bandLimitedTables.SetCapacity(m_numberOfRanges); @@ -218,16 +220,12 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i realP[i] = 0; imagP[i] = 0; } - // Clear nyquist if necessary. - if (numberOfPartials < halfSize + 1) - realP[halfSize] = 0; // Clear any DC-offset. realP[0] = 0; - // Clear values which have no effect. + // Clear value which has no effect. imagP[0] = 0; - imagP[halfSize] = 0; // Create the band-limited table. AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize); From 893dc9cca8601c6c585f6c8d82297d946177a339 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Tue, 28 Jul 2015 08:37:21 +1200 Subject: [PATCH 058/113] bug 1188704 simplify culling of partials r=rillian avoiding copying and scaling components that will be zeroed. --HG-- extra : rebase_source : a649e78254296945d89f5e03dc75e6af753e56b4 --- dom/media/webaudio/blink/PeriodicWave.cpp | 27 +++++++++-------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/dom/media/webaudio/blink/PeriodicWave.cpp b/dom/media/webaudio/blink/PeriodicWave.cpp index 64b4619a7ffd..862a0d35326b 100644 --- a/dom/media/webaudio/blink/PeriodicWave.cpp +++ b/dom/media/webaudio/blink/PeriodicWave.cpp @@ -193,29 +193,24 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i nsAutoArrayPtr realP(new float[halfSize + 1]); nsAutoArrayPtr imagP(new float[halfSize + 1]); + // Find the starting bin where we should start culling the aliasing + // partials for this pitch range. We need to clear out the highest + // frequencies to band-limit the waveform. + unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); + // Also clear bins where components are not provided. + numberOfPartials = std::min(numberOfPartials, numberOfComponents - 1); + // Copy from loaded frequency data and scale. float scale = fftSize; - AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents); - AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents); - - // If fewer components were provided than 1/2 FFT size, - // then clear the remaining bins. - for (i = numberOfComponents; i < halfSize + 1; ++i) { - realP[i] = 0; - imagP[i] = 0; - } + AudioBufferCopyWithScale(realData, scale, realP, numberOfPartials + 1); + AudioBufferCopyWithScale(imagData, scale, imagP, numberOfPartials + 1); // Generate complex conjugate because of the way the // inverse FFT is defined. float minusOne = -1; - AudioBufferInPlaceScale(imagP, minusOne, halfSize + 1); + AudioBufferInPlaceScale(imagP, minusOne, numberOfPartials + 1); - // Find the starting bin where we should start culling. - // We need to clear out the highest frequencies to band-limit - // the waveform. - unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); - - // Cull the aliasing partials for this pitch range. + // Clear the remaining bins. for (i = numberOfPartials + 1; i < halfSize + 1; ++i) { realP[i] = 0; imagP[i] = 0; From 6ca6918802ebbae7b683b41634358ab5ba7aafbf Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Tue, 28 Jul 2015 15:58:06 +1200 Subject: [PATCH 059/113] bug 1188704 combine scaling with copying r=rillian --HG-- extra : rebase_source : f30aed44b66cca842a752a207ab9536e3cb395b2 --- dom/media/webaudio/blink/PeriodicWave.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dom/media/webaudio/blink/PeriodicWave.cpp b/dom/media/webaudio/blink/PeriodicWave.cpp index 862a0d35326b..1cde637c0c0d 100644 --- a/dom/media/webaudio/blink/PeriodicWave.cpp +++ b/dom/media/webaudio/blink/PeriodicWave.cpp @@ -203,12 +203,9 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i // Copy from loaded frequency data and scale. float scale = fftSize; AudioBufferCopyWithScale(realData, scale, realP, numberOfPartials + 1); - AudioBufferCopyWithScale(imagData, scale, imagP, numberOfPartials + 1); - // Generate complex conjugate because of the way the // inverse FFT is defined. - float minusOne = -1; - AudioBufferInPlaceScale(imagP, minusOne, numberOfPartials + 1); + AudioBufferCopyWithScale(imagData, -scale, imagP, numberOfPartials + 1); // Clear the remaining bins. for (i = numberOfPartials + 1; i < halfSize + 1; ++i) { From e8e61ee28e5b1f95f0ce13dc8c9dbd82e355b3d0 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Thu, 30 Jul 2015 12:33:04 +1200 Subject: [PATCH 060/113] bug 1188704 add accessor functions for setting frequency components for inverse FFT r=rillian --HG-- extra : rebase_source : cfd4b300d5437793dc60f4e0630fa393cc234b9d --- dom/media/webaudio/FFTBlock.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dom/media/webaudio/FFTBlock.h b/dom/media/webaudio/FFTBlock.h index 44f1e7a57c81..cefa92e3636b 100644 --- a/dom/media/webaudio/FFTBlock.h +++ b/dom/media/webaudio/FFTBlock.h @@ -217,10 +217,18 @@ public: { return mOutputBuffer[aIndex].r; } + float& RealData(uint32_t aIndex) + { + return mOutputBuffer[aIndex].r; + } float ImagData(uint32_t aIndex) const { return mOutputBuffer[aIndex].i; } + float& ImagData(uint32_t aIndex) + { + return mOutputBuffer[aIndex].i; + } size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { From a4757ceda31db5142ad07f0f8ccf87796a0bab4a Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Mon, 3 Aug 2015 09:28:32 +1200 Subject: [PATCH 061/113] bug 1188704 use existing FFTBlock arrays instead of allocating and copying and scaling r=rillian It is now no longer necessary to clear zero bins because these are zeroed in the FFTBlock constructor. nsTArray bounds assertions now apply. --HG-- extra : rebase_source : 18c07ecb51f9cb3c199536fe59110093db2295d4 --- dom/media/webaudio/blink/PeriodicWave.cpp | 29 +++++++++-------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/dom/media/webaudio/blink/PeriodicWave.cpp b/dom/media/webaudio/blink/PeriodicWave.cpp index 1cde637c0c0d..f6a5bd13a06f 100644 --- a/dom/media/webaudio/blink/PeriodicWave.cpp +++ b/dom/media/webaudio/blink/PeriodicWave.cpp @@ -190,34 +190,27 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) { // This FFTBlock is used to cull partials (represented by frequency bins). FFTBlock frame(fftSize); - nsAutoArrayPtr realP(new float[halfSize + 1]); - nsAutoArrayPtr imagP(new float[halfSize + 1]); // Find the starting bin where we should start culling the aliasing // partials for this pitch range. We need to clear out the highest // frequencies to band-limit the waveform. unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex); - // Also clear bins where components are not provided. + // Also limit to the number of components that are provided. numberOfPartials = std::min(numberOfPartials, numberOfComponents - 1); - // Copy from loaded frequency data and scale. - float scale = fftSize; - AudioBufferCopyWithScale(realData, scale, realP, numberOfPartials + 1); - // Generate complex conjugate because of the way the - // inverse FFT is defined. - AudioBufferCopyWithScale(imagData, -scale, imagP, numberOfPartials + 1); - - // Clear the remaining bins. - for (i = numberOfPartials + 1; i < halfSize + 1; ++i) { - realP[i] = 0; - imagP[i] = 0; + // Copy from loaded frequency data and generate complex conjugate + // because of the way the inverse FFT is defined. + // The coefficients of higher partials remain zero, as initialized in + // the FFTBlock constructor. + for (i = 0; i < numberOfPartials + 1; ++i) { + frame.RealData(i) = realData[i]; + frame.ImagData(i) = -imagData[i]; } // Clear any DC-offset. - realP[0] = 0; - + frame.RealData(0) = 0; // Clear value which has no effect. - imagP[0] = 0; + frame.ImagData(0) = 0; // Create the band-limited table. AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize); @@ -225,7 +218,7 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i // Apply an inverse FFT to generate the time-domain table data. float* data = m_bandLimitedTables[rangeIndex]->Elements(); - frame.PerformInverseFFT(realP, imagP, data); + frame.GetInverseWithoutScaling(data); // For the first range (which has the highest power), calculate // its peak value then compute normalization scale. From 66f210c5b07995c3c0335df10d36ec0899d4ac50 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Thu, 30 Jul 2015 12:51:48 +1200 Subject: [PATCH 062/113] bug 1188704 remove now unused PerformInverseFFT() variation r=rillian --HG-- extra : rebase_source : 13b3cfce3054fce463d0e2407bd48e343952830e --- dom/media/webaudio/FFTBlock.h | 41 ----------------------------------- 1 file changed, 41 deletions(-) diff --git a/dom/media/webaudio/FFTBlock.h b/dom/media/webaudio/FFTBlock.h index cefa92e3636b..0d6d9f1a495c 100644 --- a/dom/media/webaudio/FFTBlock.h +++ b/dom/media/webaudio/FFTBlock.h @@ -133,47 +133,6 @@ public: } #endif } - // Inverse-transform the FFTSize()/2+1 points of data in each - // of aRealDataIn and aImagDataIn and store the resulting - // FFTSize() points in aRealDataOut. - void PerformInverseFFT(float* aRealDataIn, - float *aImagDataIn, - float *aRealDataOut) - { - EnsureIFFT(); - const uint32_t inputSize = mFFTSize / 2 + 1; -#if defined(MOZ_LIBAV_FFT) - AlignedTArray inputBuffer(inputSize * 2); - for (uint32_t i = 0; i < inputSize; ++i) { - inputBuffer[2*i] = aRealDataIn[i]; - inputBuffer[(2*i)+1] = aImagDataIn[i]; - } - av_rdft_calc(mAvIRDFT, inputBuffer.Elements()); - PodCopy(aRealDataOut, inputBuffer.Elements(), FFTSize()); - // TODO: Once bug 877662 lands, change this to use SSE. - for (uint32_t i = 0; i < mFFTSize; ++i) { - aRealDataOut[i] /= mFFTSize; - } -#else - AlignedTArray inputBuffer(inputSize); - for (uint32_t i = 0; i < inputSize; ++i) { - inputBuffer[i].r = aRealDataIn[i]; - inputBuffer[i].i = aImagDataIn[i]; - } -#if defined(BUILD_ARM_NEON) - if (mozilla::supports_neon()) { - omxSP_FFTInv_CCSToR_F32_Sfs(inputBuffer.Elements()->f, - aRealDataOut, mOmxIFFT); - } else -#endif - { - kiss_fftri(mKissIFFT, &(inputBuffer.Elements()->c), aRealDataOut); - for (uint32_t i = 0; i < mFFTSize; ++i) { - aRealDataOut[i] /= mFFTSize; - } - } -#endif - } void Multiply(const FFTBlock& aFrame) { From 0cac5ed7702649897e765860830aad772c140251 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Fri, 31 Jul 2015 17:24:46 -0300 Subject: [PATCH 063/113] Bug 1175934 part 1: Copy blobfree zip to public upload dir. If we ./build.sh blobfree, move the zip file to public upload dir. Also, we don't need a blobfree specific config file anymore. --HG-- extra : commitid : Gp8QmmLVcju --- .../configs/b2g/taskcluster-phone-blobfree.py | 31 ------------------- testing/mozharness/scripts/b2g_build.py | 12 +++++++ 2 files changed, 12 insertions(+), 31 deletions(-) delete mode 100644 testing/mozharness/configs/b2g/taskcluster-phone-blobfree.py diff --git a/testing/mozharness/configs/b2g/taskcluster-phone-blobfree.py b/testing/mozharness/configs/b2g/taskcluster-phone-blobfree.py deleted file mode 100644 index f5cd1c117178..000000000000 --- a/testing/mozharness/configs/b2g/taskcluster-phone-blobfree.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python -config = { - "default_vcs": "tc-vcs", - "default_actions": [ - 'checkout-sources', - 'build', - 'build-symbols', - 'prep-upload' - ], - "upload": { - "default": { - "upload_dep_target_exclusions": [] - } - }, - "env": { - "GAIA_OPTIMIZE": "1", - "WGET_OPTS": "-c -q" - }, - "build_targets": ["blobfree"], - "is_automation": True, - "repo_remote_mappings": { - 'https://android.googlesource.com/': 'https://git.mozilla.org/external/aosp', - 'git://codeaurora.org/': 'https://git.mozilla.org/external/caf', - 'https://git.mozilla.org/b2g': 'https://git.mozilla.org/b2g', - 'git://github.com/mozilla-b2g/': 'https://git.mozilla.org/b2g', - 'git://github.com/mozilla/': 'https://git.mozilla.org/b2g', - 'https://git.mozilla.org/releases': 'https://git.mozilla.org/releases', - 'http://android.git.linaro.org/git-ro/': 'https://git.mozilla.org/external/linaro', - 'git://github.com/apitrace/': 'https://git.mozilla.org/external/apitrace', - }, -} diff --git a/testing/mozharness/scripts/b2g_build.py b/testing/mozharness/scripts/b2g_build.py index 39efafc649f3..d2f85f232f50 100755 --- a/testing/mozharness/scripts/b2g_build.py +++ b/testing/mozharness/scripts/b2g_build.py @@ -711,6 +711,18 @@ class B2GBuild(LocalesMixin, PurgeMixin, if base_pattern in public_upload_patterns: public_files.append(f) + device_name = self.config['target'].split('-')[0] + blobfree_zip = os.path.join( + dirs['work_dir'], + 'out', + 'target', + 'product', + device_name, + device_name + '.blobfree-dist.zip') + + if os.path.exists(blobfree_zip): + public_files.append(blobfree_zip) + for base_f in files + public_files: f = base_f if f.endswith(".img"): From b1e44f1c8cc464864190651d1ff76822b35f5ef4 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Fri, 31 Jul 2015 17:24:46 -0300 Subject: [PATCH 064/113] Bug 1175934 part 2: Support blobfree images. When we ./build.sh blobfree, we have a new zip file called .blobfree-dist.zip which contains a blobfree image. We copy this file to public artifact. We also refactor the build scripts a little bit to deduplicate the post build code. --HG-- extra : commitid : JU2n5YDGBfa --- b2g/config/aries/config.json | 2 +- b2g/config/flame-kk/config.json | 2 +- testing/mozharness/scripts/b2g_build.py | 3 +- .../scripts/phone-builder/build-phone-ota.sh | 20 +---------- .../scripts/phone-builder/build-phone.sh | 14 +------- .../scripts/phone-builder/post-build.sh | 33 +++++++++++++++++++ 6 files changed, 39 insertions(+), 35 deletions(-) create mode 100755 testing/taskcluster/scripts/phone-builder/post-build.sh diff --git a/b2g/config/aries/config.json b/b2g/config/aries/config.json index a9ba472c0c83..93683c532cfe 100644 --- a/b2g/config/aries/config.json +++ b/b2g/config/aries/config.json @@ -7,7 +7,7 @@ ["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"], ["/builds/crash-stats-api.token", "/builds/crash-stats-api.token"] ], - "build_targets": [], + "build_targets": ["", "blobfree"], "upload_files": [ "{objdir}/dist/b2g-*.crashreporter-symbols.zip", "{objdir}/dist/b2g-*.tar.gz", diff --git a/b2g/config/flame-kk/config.json b/b2g/config/flame-kk/config.json index f34229db93c4..15cfa1a584db 100644 --- a/b2g/config/flame-kk/config.json +++ b/b2g/config/flame-kk/config.json @@ -7,7 +7,7 @@ ["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"], ["/builds/crash-stats-api.token", "/builds/crash-stats-api.token"] ], - "build_targets": [], + "build_targets": ["", "blobfree"], "upload_files": [ "{objdir}/dist/b2g-*.crashreporter-symbols.zip", "{objdir}/dist/b2g-*.tar.gz", diff --git a/testing/mozharness/scripts/b2g_build.py b/testing/mozharness/scripts/b2g_build.py index d2f85f232f50..0a13923feed0 100755 --- a/testing/mozharness/scripts/b2g_build.py +++ b/testing/mozharness/scripts/b2g_build.py @@ -524,7 +524,8 @@ class B2GBuild(LocalesMixin, PurgeMixin, cmd = ['./build.sh'] if target is not None: # Workaround bug 984061 - if target == 'package-tests': + # wcosta: blobfree builds also should run with -j1 + if target in ('package-tests', 'blobfree'): cmd.append('-j1') else: # Ensure we always utilize the correct number of cores diff --git a/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh b/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh index 1978b5ef784d..98becf787396 100755 --- a/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh +++ b/testing/taskcluster/scripts/phone-builder/build-phone-ota.sh @@ -42,22 +42,4 @@ $WORKSPACE/gecko/testing/mozharness/scripts/b2g_build.py \ --platform $PLATFORM \ --complete-mar-url https://queue.taskcluster.net/v1/task/$TASK_ID/runs/$RUN_ID/artifacts/public/build/$mar_file -# Don't cache backups -rm -rf $WORKSPACE/B2G/backup-* -rm -f balrog_credentials - -mkdir -p $HOME/artifacts -mkdir -p $HOME/artifacts-public - -mv $WORKSPACE/B2G/upload-public/$mar_file $HOME/artifacts-public/ -mv $WORKSPACE/B2G/upload/sources.xml $HOME/artifacts/sources.xml -mv $WORKSPACE/B2G/upload/b2g-*.android-arm.tar.gz $HOME/artifacts/b2g-android-arm.tar.gz -mv $WORKSPACE/B2G/upload/${TARGET}.zip $HOME/artifacts/${TARGET}.zip -mv $WORKSPACE/B2G/upload/gaia.zip $HOME/artifacts/gaia.zip - -if [ -f $WORKSPACE/B2G/upload/b2g-*.crashreporter-symbols.zip ]; then - mv $WORKSPACE/B2G/upload/b2g-*.crashreporter-symbols.zip $HOME/artifacts/b2g-crashreporter-symbols.zip -fi - -ccache -s - +. post-build.sh diff --git a/testing/taskcluster/scripts/phone-builder/build-phone.sh b/testing/taskcluster/scripts/phone-builder/build-phone.sh index 0dfead3fdc84..4c0e2fdedba9 100755 --- a/testing/taskcluster/scripts/phone-builder/build-phone.sh +++ b/testing/taskcluster/scripts/phone-builder/build-phone.sh @@ -25,16 +25,4 @@ $WORKSPACE/gecko/testing/mozharness/scripts/b2g_build.py \ --checkout-revision=$GECKO_HEAD_REV \ --repo=$WORKSPACE/gecko -# Don't cache backups -rm -rf $WORKSPACE/B2G/backup-* - -# Move files into artifact locations! -mkdir -p $HOME/artifacts - -mv $WORKSPACE/B2G/upload/sources.xml $HOME/artifacts/sources.xml -mv $WORKSPACE/B2G/upload/b2g-*.crashreporter-symbols.zip $HOME/artifacts/b2g-crashreporter-symbols.zip -mv $WORKSPACE/B2G/upload/b2g-*.android-arm.tar.gz $HOME/artifacts/b2g-android-arm.tar.gz -mv $WORKSPACE/B2G/upload/${TARGET}.zip $HOME/artifacts/${TARGET}.zip -mv $WORKSPACE/B2G/upload/gaia.zip $HOME/artifacts/gaia.zip - -ccache -s +. post-build.sh diff --git a/testing/taskcluster/scripts/phone-builder/post-build.sh b/testing/taskcluster/scripts/phone-builder/post-build.sh new file mode 100755 index 000000000000..a5a5a895342b --- /dev/null +++ b/testing/taskcluster/scripts/phone-builder/post-build.sh @@ -0,0 +1,33 @@ +#! /bin/bash -vex + +# Don't cache backups +rm -rf $WORKSPACE/B2G/backup-* + +if [ -f balrog_credentials ]; then + rm -f balrog_credentials +fi + +mkdir -p $HOME/artifacts +mkdir -p $HOME/artifacts-public + +DEVICE=${TARGET%%-*} + +mv $WORKSPACE/B2G/upload/sources.xml $HOME/artifacts/sources.xml +mv $WORKSPACE/B2G/upload/b2g-*.android-arm.tar.gz $HOME/artifacts/b2g-android-arm.tar.gz +mv $WORKSPACE/B2G/upload/${TARGET}.zip $HOME/artifacts/${TARGET}.zip +mv $WORKSPACE/B2G/upload/gaia.zip $HOME/artifacts/gaia.zip + +if [ -f $WORKSPACE/B2G/upload/b2g-*.crashreporter-symbols.zip ]; then + mv $WORKSPACE/B2G/upload/b2g-*.crashreporter-symbols.zip $HOME/artifacts/b2g-crashreporter-symbols.zip +fi + +if [ -f $WORKSPACE/B2G/upload-public/${DEVICE}.blobfree-dist.zip ]; then + mv $WORKSPACE/B2G/upload-public/${DEVICE}.blobfree-dist.zip $HOME/artifacts-public +fi + +if [ -f $WORKSPACE/B2G/upload-public/$mar_file ]; then + mv $WORKSPACE/B2G/upload-public/$mar_file $HOME/artifacts-public/ +fi + +ccache -s + From a8994dac92312fec7d40c891315a8e1bb714b6f7 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Fri, 31 Jul 2015 17:24:46 -0300 Subject: [PATCH 065/113] Bug 1175934 part 3: Add more build flags to phone builds. All phone builds must have B2G_UPDATER, MOZILLA_OFFICIAL and ENABLE_DEFAULT_BOOTANIMATION build flags. --HG-- extra : commitid : 67VAUTCC6mM --- testing/taskcluster/tasks/builds/b2g_phone_base.yml | 3 +++ testing/taskcluster/tasks/builds/b2g_phone_eng_base.yml | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/testing/taskcluster/tasks/builds/b2g_phone_base.yml b/testing/taskcluster/tasks/builds/b2g_phone_base.yml index 13915ab52d19..1ab2648fe3db 100644 --- a/testing/taskcluster/tasks/builds/b2g_phone_base.yml +++ b/testing/taskcluster/tasks/builds/b2g_phone_base.yml @@ -6,6 +6,9 @@ task: Android phones + b2g environment used in full stack testing. payload: env: + MOZILLA_OFFICIAL: '1' + ENABLE_DEFAULT_BOOTANIMATION: 'true' + B2G_UPDATER: '1' REPO_TRACE: 1 VARIANT: user diff --git a/testing/taskcluster/tasks/builds/b2g_phone_eng_base.yml b/testing/taskcluster/tasks/builds/b2g_phone_eng_base.yml index ec1f1a360ca4..20268158e66d 100644 --- a/testing/taskcluster/tasks/builds/b2g_phone_eng_base.yml +++ b/testing/taskcluster/tasks/builds/b2g_phone_eng_base.yml @@ -9,11 +9,9 @@ task: VARIANT: eng GAIA_OPTIMIZE: '1' B2G_SYSTEM_APPS: '1' - MOZILLA_OFFICIAL: '1' MOZ_TELEMETRY_REPORTING: '1' MOZ_CRASHREPORTER_NO_REPORT: '1' GAIA_KEYBOARD_LAYOUTS: 'en,pt-BR,es,de,fr,pl,zh-Hans-Pinyin,zh-Hant-Zhuyin,en-Dvorak' - B2G_UPDATER: '1' B2G_UPDATE_CHANNEL: 'default' command: - > From bd1e8845b232e46b66b27b2fee7a84cfff582838 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 13:40:41 -0700 Subject: [PATCH 066/113] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/08236e042e0a Author: Gabriele Svelto Desc: Merge pull request #30157 from gabrielesvelto/bug-1104667-callscreen-l10n Bug 1104667 - Remove the LazyL10n code and refactored the callscreen to use modern L10n primitives r=drs,gandalf ======== https://hg.mozilla.org/integration/gaia-central/rev/ee480f1d6b8c Author: Gabriele Svelto Desc: Bug 1104667 - Remove the LazyL10n code and refactor the callscreen to use modern L10n primitives r=drs,gandalf --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index cfe5d4e3fae5..68825066b8c6 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "2ca27bbdd84526c6a3b198d9cf10f2caff1dadde", + "git_revision": "9002fdc0abc2e758c69e1ac48bea6395947a0afb", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "b5e9a95e8f13e775ae2e3828a6e389e5231bef32", + "revision": "08236e042e0ab438c27b9fc48a2246ec08378551", "repo_path": "integration/gaia-central" } From 65e1734c74e86e0ac3995b9b2240dde92c00fec5 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 13:43:05 -0700 Subject: [PATCH 067/113] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index cf44ace64554..47f27c6a677c 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 2a1b283df169..a0fff980846c 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 09cf974ae14b..eb3f7cefbe5e 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index a4f153bb31f5..898f33479ed3 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index a398c5f087f0..ab785a37dfb3 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index 06c3525c3c50..c9462636c216 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 09cf974ae14b..eb3f7cefbe5e 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 6cf0f5b6fe25..8210ebb579df 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 920e5e0f69e4..2bfddcec8aaf 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 3fbc1af2d377..b1d9a8b57a41 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From 8f54b77fbea27637ca80f13bc69a8beeac2daf2d Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 15:41:40 -0700 Subject: [PATCH 068/113] Bumping gaia.json for 1 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/b38cfc1abe2a Author: Wes Kocher Desc: Revert "Merge pull request #30157 from gabrielesvelto/bug-1104667-callscreen-l10n" for Gu test failures This reverts commit 9002fdc0abc2e758c69e1ac48bea6395947a0afb, reversing changes made to 2ca27bbdd84526c6a3b198d9cf10f2caff1dadde. --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 68825066b8c6..43b84f76a951 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "9002fdc0abc2e758c69e1ac48bea6395947a0afb", + "git_revision": "a83366dc9f73182bb43d06b22337670aa5d71cb6", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "08236e042e0ab438c27b9fc48a2246ec08378551", + "revision": "b38cfc1abe2ad9372d276b5feaf533fac27155c6", "repo_path": "integration/gaia-central" } From 85ce85f8d5c7b9a666b46c630371b508a0ccf2ef Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 15:44:03 -0700 Subject: [PATCH 069/113] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index 47f27c6a677c..1fd328a01f2f 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index a0fff980846c..a370d17d6830 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index eb3f7cefbe5e..9fd169f0e1fa 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 898f33479ed3..355ff5fee98a 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index ab785a37dfb3..2908a284d041 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index c9462636c216..cb2be16ab53a 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index eb3f7cefbe5e..9fd169f0e1fa 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 8210ebb579df..1ba6ae70a84d 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 2bfddcec8aaf..f11c615408e7 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index b1d9a8b57a41..88d63fb52785 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From cfe0b1b13e11818f1bb32c2c667fd8c9a66177a8 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 16:30:33 -0700 Subject: [PATCH 070/113] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/19710fcc3a62 Author: Zibi Braniecki Desc: Merge pull request #31199 from zbraniecki/bug-1104667-callscreen-l10n Bug 1104667 - Remove the LazyL10n code and refactor the callscreen to use modern L10n primitives r=drs,gandalf ======== https://hg.mozilla.org/integration/gaia-central/rev/ccf582f7b0d9 Author: Gabriele Svelto Desc: Bug 1104667 - Remove the LazyL10n code and refactor the callscreen to use modern L10n primitives r=drs,gandalf --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 43b84f76a951..def3b34f68e3 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "a83366dc9f73182bb43d06b22337670aa5d71cb6", + "git_revision": "1a814a4e7fa57132cdc0512a6c363b6b9471431d", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "b38cfc1abe2ad9372d276b5feaf533fac27155c6", + "revision": "19710fcc3a62d9c8d1fce0c095016d0013957876", "repo_path": "integration/gaia-central" } From 456a5552727d14b5ccf04dcb07474631868da720 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 16:32:32 -0700 Subject: [PATCH 071/113] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index 1fd328a01f2f..59d2e0776a28 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index a370d17d6830..e4d610b7f925 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 9fd169f0e1fa..1ece863bc904 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 355ff5fee98a..41d5c7c66588 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 2908a284d041..6fa1403344d6 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index cb2be16ab53a..3a42edbea6b3 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 9fd169f0e1fa..1ece863bc904 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 1ba6ae70a84d..2f19f4c6b7b5 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index f11c615408e7..2db16c7cc6aa 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 88d63fb52785..db31c773b541 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From 3b8e4f842cb35360c464485ba334810677046f3b Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 16:58:04 -0700 Subject: [PATCH 072/113] Bumping manifests a=b2g-bump --- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 2f19f4c6b7b5..6e280f241a99 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -126,7 +126,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index db31c773b541..0c36031b1436 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -142,7 +142,7 @@ - + From b5125dad76558bf5f600cef27469495631fa921e Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 17:23:08 -0700 Subject: [PATCH 073/113] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index e4d610b7f925..25f0a1b52c81 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -38,7 +38,7 @@ - + From 709bf51b676cce5a7cac81575d641b6b3b4ae1a3 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 17:45:31 -0700 Subject: [PATCH 074/113] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/7cea548c571a Author: Kevin Grandon Desc: Merge pull request #31031 from KevinGrandon/bug_1185269_settings_radio_component Bug 1185269 - [Settings] Convert radio controls to use gaia-radio ======== https://hg.mozilla.org/integration/gaia-central/rev/dbaa8a888f5e Author: Kevin Grandon Desc: Bug 1185269 - [Settings] Convert radio controls to use gaia-radio r=gasolin --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index def3b34f68e3..190b7c26d9cb 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "1a814a4e7fa57132cdc0512a6c363b6b9471431d", + "git_revision": "9a45076c37ad2139f25194613916af5efb55bbc7", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "19710fcc3a62d9c8d1fce0c095016d0013957876", + "revision": "7cea548c571af8df6c601254b587ba743cbf0613", "repo_path": "integration/gaia-central" } From 8b521c424ba21aac913d01dccbee2f003d1afc05 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 17:47:30 -0700 Subject: [PATCH 075/113] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index 59d2e0776a28..ef80e861c3ce 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 25f0a1b52c81..41798d86b18c 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 1ece863bc904..13fbd514384c 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 41d5c7c66588..fff1b741ece4 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 6fa1403344d6..aeda5ce7c735 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index 3a42edbea6b3..dce8052e4013 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 1ece863bc904..13fbd514384c 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 6e280f241a99..2314e6fe7c68 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 2db16c7cc6aa..bcfd4d627c72 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 0c36031b1436..82520d0b7778 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From d2c9698be84e7fa8963a3f3289f1b8bb909a8873 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 31 Jul 2015 20:28:08 -0700 Subject: [PATCH 076/113] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 41798d86b18c..7e73d2b2914e 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -105,7 +105,7 @@ - + From ae7af3ea3c53be2ce9ae25ca81b942d4f7a8d8f6 Mon Sep 17 00:00:00 2001 From: ffxbld Date: Sat, 1 Aug 2015 03:34:17 -0700 Subject: [PATCH 077/113] No bug, Automated HSTS preload list update from host bld-linux64-spot-317 - a=hsts-update --- security/manager/ssl/nsSTSPreloadList.errors | 22 +++++++++++++------- security/manager/ssl/nsSTSPreloadList.inc | 11 +++------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/security/manager/ssl/nsSTSPreloadList.errors b/security/manager/ssl/nsSTSPreloadList.errors index 23d315ab7320..d7e51c209777 100644 --- a/security/manager/ssl/nsSTSPreloadList.errors +++ b/security/manager/ssl/nsSTSPreloadList.errors @@ -9,12 +9,15 @@ agrimap.com: did not receive HSTS header agrios.de: did not receive HSTS header airbnb.com: did not receive HSTS header aiticon.de: did not receive HSTS header +akclinics.org: did not receive HSTS header akselimedia.fi: did not receive HSTS header +al-shami.net: did not receive HSTS header alecvannoten.be: did not receive HSTS header alpha.irccloud.com: could not connect to host alphabit-secure.com: could not connect to host altmv.com: max-age too low: 7776000 amigogeek.net: could not connect to host +animesharp.com: could not connect to host ankakaak.com: could not connect to host api.mega.co.nz: could not connect to host api.recurly.com: did not receive HSTS header @@ -126,7 +129,7 @@ de.search.yahoo.com: did not receive HSTS header decibelios.li: did not receive HSTS header dedeo.tk: max-age too low: 0 destinationbijoux.fr: max-age too low: 2678400 -diedrich.co: did not receive HSTS header +diedrich.co: max-age too low: 7776000 digitaldaddy.net: could not connect to host discovery.lookout.com: did not receive HSTS header dk.search.yahoo.com: did not receive HSTS header @@ -171,6 +174,7 @@ festember.com: did not receive HSTS header fi.search.yahoo.com: did not receive HSTS header fiftyshadesofluca.ml: could not connect to host firebaseio.com: could not connect to host +firemail.io: could not connect to host fixingdns.com: did not receive HSTS header fj.search.yahoo.com: did not receive HSTS header floweslawncare.com: did not receive HSTS header @@ -186,7 +190,6 @@ g2g.com: did not receive HSTS header gamesdepartment.co.uk: did not receive HSTS header gaptek.id: did not receive HSTS header geekandi.com: max-age too low: 7776000 -genuxation.com: could not connect to host getlantern.org: did not receive HSTS header getssl.uz: could not connect to host gl.search.yahoo.com: did not receive HSTS header @@ -204,6 +207,7 @@ goto.google.com: did not receive HSTS header (error ignored - included regardles gotowned.org: did not receive HSTS header gparent.org: did not receive HSTS header gr.search.yahoo.com: did not receive HSTS header +gracedays.org: could not connect to host grandmascookieblog.com: did not receive HSTS header greensolid.biz: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no] greplin.com: could not connect to host @@ -233,6 +237,7 @@ hstspreload.appspot.com: did not receive HSTS header hu.search.yahoo.com: did not receive HSTS header ian.sh: did not receive HSTS header iban.is: could not connect to host +id-co.in: could not connect to host id.fedoraproject.org: did not receive HSTS header id.search.yahoo.com: did not receive HSTS header identitylabs.uk: did not receive HSTS header @@ -326,13 +331,14 @@ miasarafina.de: did not receive HSTS header micropple.net: could not connect to host miketabor.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no] minikneet.nl: could not connect to host -minora.io: did not receive HSTS header mirindadomo.ru: did not receive HSTS header mirrorx.com: did not receive HSTS header mnemotiv.com: could not connect to host mobilethreat.net: could not connect to host mobilethreatnetwork.net: could not connect to host +moriz.net: did not receive HSTS header mqas.net: could not connect to host +msc-seereisen.net: did not receive HSTS header mt.search.yahoo.com: did not receive HSTS header mu.search.yahoo.com: did not receive HSTS header mustika.cf: max-age too low: 0 @@ -357,6 +363,7 @@ nexth.us: could not connect to host ng-security.com: could not connect to host ni.search.yahoo.com: did not receive HSTS header nl.search.yahoo.com: did not receive HSTS header +nmctest.net: could not connect to host no.search.yahoo.com: did not receive HSTS header noexpect.org: could not connect to host noobs-r-us.co.uk: did not receive HSTS header @@ -395,7 +402,6 @@ prodpad.com: did not receive HSTS header projektzentrisch.de: could not connect to host promecon-gmbh.de: did not receive HSTS header prontolight.com: did not receive HSTS header -proximato.com: could not connect to host pult.co: could not connect to host py.search.yahoo.com: did not receive HSTS header qc.search.yahoo.com: did not receive HSTS header @@ -413,7 +419,6 @@ ro.search.yahoo.com: did not receive HSTS header roan24.pl: did not receive HSTS header roddis.net: did not receive HSTS header roeper.party: could not connect to host -roman-pavlik.cz: could not connect to host ru.search.yahoo.com: did not receive HSTS header rugstorene.co.uk: did not receive HSTS header rw.search.yahoo.com: did not receive HSTS header @@ -433,7 +438,6 @@ seomobo.com: did not receive HSTS header seowarp.net: max-age too low: 1576800 serverdensity.io: did not receive HSTS header sg.search.yahoo.com: did not receive HSTS header -shamka.ru: could not connect to host shiinko.com: could not connect to host shoprose.ru: did not receive HSTS header shops.neonisi.com: could not connect to host @@ -445,7 +449,9 @@ sistemy48.ru: did not receive HSTS header sites.google.com: did not receive HSTS header (error ignored - included regardless) smartlend.se: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no] snailing.org: could not connect to host +sneberger.cz: could not connect to host soccergif.com: could not connect to host +sockeye.cc: did not receive HSTS header sol.io: could not connect to host souyar.de: could not connect to host souyar.net: could not connect to host @@ -477,6 +483,7 @@ tandarts-haarlem.nl: did not receive HSTS header tanzhijun.com: did not receive HSTS header tapka.cz: did not receive HSTS header tc-bonito.de: did not receive HSTS header +tegelsensanitaironline.nl: did not receive HSTS header tektoria.de: could not connect to host temehu.com: did not receive HSTS header terrax.berlin: could not connect to host @@ -506,7 +513,6 @@ tv.search.yahoo.com: could not connect to host tw.search.yahoo.com: did not receive HSTS header tzappa.net: did not receive HSTS header ua.search.yahoo.com: did not receive HSTS header -uber.com: max-age too low: 0 ubicv.com: did not receive HSTS header uega.net: did not receive HSTS header uk.search.yahoo.com: did not receive HSTS header @@ -529,6 +535,8 @@ vzk.io: could not connect to host webeau.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no] webmail.mayfirst.org: did not receive HSTS header webmaniabr.com: did not receive HSTS header +wf-training-master.appspot.com: could not connect to host +wf-training-master.appspot.com: could not connect to host (error ignored - included regardless) when-release.com: did not receive HSTS header wikidsystems.com: did not receive HSTS header wilf1rst.com: could not connect to host diff --git a/security/manager/ssl/nsSTSPreloadList.inc b/security/manager/ssl/nsSTSPreloadList.inc index 42493408f9d7..634dd6032415 100644 --- a/security/manager/ssl/nsSTSPreloadList.inc +++ b/security/manager/ssl/nsSTSPreloadList.inc @@ -8,7 +8,7 @@ /*****************************************************************************/ #include -const PRTime gPreloadListExpirationTime = INT64_C(1449175283973000); +const PRTime gPreloadListExpirationTime = INT64_C(1449311122438000); class nsSTSPreload { @@ -88,12 +88,10 @@ static const nsSTSPreload kSTSPreloadList[] = { { "aiticon.com", true }, { "ajouin.com", true }, { "akachanikuji.com", true }, - { "akclinics.org", true }, { "akselinurmio.fi", true }, { "aktiv-naturheilmittel.at", true }, { "aktiv-naturheilmittel.ch", true }, { "aktiv-naturheilmittel.de", true }, - { "al-shami.net", true }, { "aladdinschools.appspot.com", true }, { "alainwolf.net", true }, { "alaninkenya.org", true }, @@ -462,7 +460,7 @@ static const nsSTSPreload kSTSPreloadList[] = { { "cotonea.de", true }, { "courtlistener.com", true }, { "covenantoftheriver.org", true }, - { "covoiturage.fr", true }, + { "covoiturage.fr", false }, { "cpvmatch.eu", true }, { "cracker.in.th", true }, { "cradlepointecm.com", true }, @@ -1390,6 +1388,7 @@ static const nsSTSPreload kSTSPreloadList[] = { { "minikneet.com", true }, { "minkondom.nu", true }, { "minnesotadata.com", true }, + { "minora.io", true }, { "mironet.cz", true }, { "miskatonic.org", true }, { "miss-inventory.co.uk", true }, @@ -1414,7 +1413,6 @@ static const nsSTSPreload kSTSPreloadList[] = { { "moniquedekermadec.com", true }, { "morethanadream.lv", true }, { "moriz.de", true }, - { "moriz.net", true }, { "mothereff.in", true }, { "moula.com.au", true }, { "mountainmusicpromotions.com", true }, @@ -1426,7 +1424,6 @@ static const nsSTSPreload kSTSPreloadList[] = { { "mqas.net", true }, { "mr-hosting.com", true }, { "msa-aesch.ch", true }, - { "msc-seereisen.net", true }, { "msebera.cz", true }, { "mtau.com", true }, { "mthode.org", true }, @@ -1949,7 +1946,6 @@ static const nsSTSPreload kSTSPreloadList[] = { { "social-media-strategies.it", true }, { "socialrank.com", false }, { "socialspirit.com.br", false }, - { "sockeye.cc", true }, { "soia.ca", true }, { "solihullcarnival.co.uk", true }, { "solihulllionsclub.org.uk", true }, @@ -2068,7 +2064,6 @@ static const nsSTSPreload kSTSPreloadList[] = { { "techloaner.com", true }, { "technotonic.com.au", false }, { "techvalue.gr", true }, - { "tegelsensanitaironline.nl", true }, { "tehrabbitt.com", true }, { "tekshrek.com", true }, { "temp.pm", true }, From abb4d538ee8d3a978147609aa2b826ab81b49982 Mon Sep 17 00:00:00 2001 From: ffxbld Date: Sat, 1 Aug 2015 03:34:19 -0700 Subject: [PATCH 078/113] No bug, Automated HPKP preload list update from host bld-linux64-spot-317 - a=hpkp-update --- security/manager/ssl/StaticHPKPins.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/manager/ssl/StaticHPKPins.h b/security/manager/ssl/StaticHPKPins.h index 35a84fafda7f..da616eff3a30 100644 --- a/security/manager/ssl/StaticHPKPins.h +++ b/security/manager/ssl/StaticHPKPins.h @@ -753,6 +753,7 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = { { "aus4.mozilla.org", true, true, true, 3, &kPinset_mozilla }, { "blog.torproject.org", true, false, false, -1, &kPinset_tor }, { "blogger.com", true, false, false, -1, &kPinset_google_root_pems }, + { "blogspot.com", true, false, false, -1, &kPinset_google_root_pems }, { "business.facebook.com", true, false, false, -1, &kPinset_facebook }, { "business.twitter.com", true, false, false, -1, &kPinset_twitterCom }, { "cdn.mozilla.net", true, false, true, -1, &kPinset_mozilla }, @@ -783,6 +784,7 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = { { "exclude-subdomains.pinning.example.com", false, false, false, 0, &kPinset_mozilla_test }, { "facebook.com", false, false, false, -1, &kPinset_facebook }, { "g.co", true, false, false, -1, &kPinset_google_root_pems }, + { "ggpht.com", true, false, false, -1, &kPinset_google_root_pems }, { "glass.google.com", true, false, false, -1, &kPinset_google_root_pems }, { "gmail.com", false, false, false, -1, &kPinset_google_root_pems }, { "goo.gl", true, false, false, -1, &kPinset_google_root_pems }, @@ -1093,8 +1095,8 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = { { "ytimg.com", true, false, false, -1, &kPinset_google_root_pems }, }; -// Pinning Preload List Length = 354; +// Pinning Preload List Length = 356; static const int32_t kUnknownId = -1; -static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1446756090891000); +static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1446891927723000); From 2cb27f0fefa7db4c0d56d335afec5f7dd9956c70 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Sat, 1 Aug 2015 12:06:06 -0700 Subject: [PATCH 079/113] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/24c0fdcc5d2f Author: Kevin Grandon Desc: Merge pull request #31170 from KevinGrandon/bug_1189176_autolander_tasks Bug 1189176 - [Autolander] Update tasks for recent changes ======== https://hg.mozilla.org/integration/gaia-central/rev/d70a309ec1e2 Author: Kevin Grandon Desc: Bug 1189176 - [Autolander] Update tasks for recent changes r=kgrandon --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 190b7c26d9cb..96c42eb966d0 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "9a45076c37ad2139f25194613916af5efb55bbc7", + "git_revision": "9b10722c5299c45ca62b341e9619f640c5dc39be", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "7cea548c571af8df6c601254b587ba743cbf0613", + "revision": "24c0fdcc5d2f9e48cf84dd78dab0e231ac3973cc", "repo_path": "integration/gaia-central" } From 33a59f79d8c9060790e755f0cd4ebb7f0df840ad Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Sat, 1 Aug 2015 12:08:05 -0700 Subject: [PATCH 080/113] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index ef80e861c3ce..be8972d26a64 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 7e73d2b2914e..97f7594a2718 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 13fbd514384c..c31f3195df26 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index fff1b741ece4..68e51d30168b 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index aeda5ce7c735..0c326e044430 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index dce8052e4013..368b1afbc680 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 13fbd514384c..c31f3195df26 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 2314e6fe7c68..9d0cbacbe3ca 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index bcfd4d627c72..ae9a0c138fbe 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 82520d0b7778..e2822354719e 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From 7d28adf54fe3775afe136b2fd78f94825b7ccb9e Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Sun, 2 Aug 2015 02:46:13 -0700 Subject: [PATCH 081/113] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/cc37327ec619 Author: Julien Wajsberg Desc: Merge pull request #30703 from julienw/1177165-run-tests-gdb Bug 1177165 - Provide a way to run the test runner in gdb r=ricky ======== https://hg.mozilla.org/integration/gaia-central/rev/9e4eff6d6079 Author: Julien Wajsberg Desc: Bug 1177165 - Provide a way to run the test runner in gdb r=ricky --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 96c42eb966d0..c722311b351b 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "9b10722c5299c45ca62b341e9619f640c5dc39be", + "git_revision": "1019c52bd5a29f7d9a23a61a15cedf7773bcfd2a", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "24c0fdcc5d2f9e48cf84dd78dab0e231ac3973cc", + "revision": "cc37327ec6190138a6351836a15c26175cd3dafe", "repo_path": "integration/gaia-central" } From 05bc403b79789084a3bb3479ce49a3c901192546 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Sun, 2 Aug 2015 02:48:12 -0700 Subject: [PATCH 082/113] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index be8972d26a64..9e153bfc1c86 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 97f7594a2718..ad6591050a50 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index c31f3195df26..b30a914cc991 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 68e51d30168b..8f5d8ac95002 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 0c326e044430..87e629aaf13e 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index 368b1afbc680..48970985d3b5 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index c31f3195df26..b30a914cc991 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 9d0cbacbe3ca..7ee6412a8920 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index ae9a0c138fbe..b292a93372e5 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index e2822354719e..0c896b4cc1f7 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From be377c43901a54a3e2dd0f7a23e3c9cccfd01d28 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Sun, 2 Aug 2015 12:35:37 -0700 Subject: [PATCH 083/113] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ======== https://hg.mozilla.org/integration/gaia-central/rev/4a0216037684 Author: Julien Wajsberg Desc: Merge pull request #31205 from julienw/1190151-run-make-node-modules Bug 1190151 - [bin/gaia-test] Run "make node_modules" before running … ======== https://hg.mozilla.org/integration/gaia-central/rev/ad5dae6bab74 Author: Julien Wajsberg Desc: Bug 1190151 - [bin/gaia-test] Run "make node_modules" before running the server r=ricky --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index c722311b351b..a915981c7f0f 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "1019c52bd5a29f7d9a23a61a15cedf7773bcfd2a", + "git_revision": "8dba2077f5e7137253fbb3faf10cd0b5f7da25c2", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "cc37327ec6190138a6351836a15c26175cd3dafe", + "revision": "4a0216037684d9f9299e8ca7470ca05c2ead4ab5", "repo_path": "integration/gaia-central" } From bb1294bc8cdf1c77cf242bb6ffb1f7f95d1bb2bc Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Sun, 2 Aug 2015 12:37:36 -0700 Subject: [PATCH 084/113] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index 9e153bfc1c86..96dda2f7c299 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index ad6591050a50..441773981de5 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index b30a914cc991..a8e8c25748e7 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 8f5d8ac95002..ffb54a9ec3ea 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 87e629aaf13e..c4440e6b1a02 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index 48970985d3b5..d9395fa0bc19 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index b30a914cc991..a8e8c25748e7 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 7ee6412a8920..d82c6aba6ec5 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index b292a93372e5..9fad08f6384c 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 0c896b4cc1f7..94051693b005 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From c04b0be1f125a14e008ea3d110833546ac17a8ce Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Sun, 2 Aug 2015 15:11:39 -0700 Subject: [PATCH 085/113] Bug 1182730 - Followup: only mark the JitcodeGlobalMap when profiling is on. (r=djvj) --- js/src/jit/Ion.cpp | 6 ++++-- js/src/jit/JitcodeMap.cpp | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index ea2580664e92..fc4578f62bf1 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -624,7 +624,8 @@ JitRuntime::Mark(JSTracer* trc) /* static */ void JitRuntime::MarkJitcodeGlobalTableUnconditionally(JSTracer* trc) { - if (trc->runtime()->hasJitRuntime() && + if (trc->runtime()->spsProfiler.enabled() && + trc->runtime()->hasJitRuntime() && trc->runtime()->jitRuntime()->hasJitcodeGlobalTable()) { trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->markUnconditionally(trc); @@ -634,7 +635,8 @@ JitRuntime::MarkJitcodeGlobalTableUnconditionally(JSTracer* trc) /* static */ bool JitRuntime::MarkJitcodeGlobalTableIteratively(JSTracer* trc) { - if (trc->runtime()->hasJitRuntime() && + if (trc->runtime()->spsProfiler.enabled() && + trc->runtime()->hasJitRuntime() && trc->runtime()->jitRuntime()->hasJitcodeGlobalTable()) { return trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->markIteratively(trc); diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index a386423df509..ad222839fe74 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -763,6 +763,8 @@ JitcodeGlobalTable::markUnconditionally(JSTracer* trc) // Mark all entries unconditionally. This is done during minor collection // to account for tenuring. + MOZ_ASSERT(trc->runtime()->spsProfiler.enabled()); + AutoSuppressProfilerSampling suppressSampling(trc->runtime()); for (Range r(*this); !r.empty(); r.popFront()) r.front()->mark(trc); @@ -806,14 +808,12 @@ JitcodeGlobalTable::markIteratively(JSTracer* trc) // above is checked in JitcodeGlobalTable::lookupForSampler. MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting()); + MOZ_ASSERT(trc->runtime()->spsProfiler.enabled()); AutoSuppressProfilerSampling suppressSampling(trc->runtime()); uint32_t gen = trc->runtime()->profilerSampleBufferGen(); uint32_t lapCount = trc->runtime()->profilerSampleBufferLapCount(); - if (!trc->runtime()->spsProfiler.enabled()) - gen = UINT32_MAX; - bool markedAny = false; for (Range r(*this); !r.empty(); r.popFront()) { JitcodeGlobalEntry* entry = r.front(); From ae6469ddd55eae5368105ae398c303480965ffcb Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Sun, 2 Aug 2015 15:11:39 -0700 Subject: [PATCH 086/113] Bug 1179063 - Cleanup: remove useless pn_index. (r=me) --- js/src/frontend/BytecodeEmitter.cpp | 1 - js/src/frontend/ParseNode.h | 1 - 2 files changed, 2 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 209ed7500828..9f86495e02af 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5861,7 +5861,6 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) bi->kind() == Binding::ARGUMENT); MOZ_ASSERT(bi.argOrLocalIndex() < JS_BIT(20)); #endif - pn->pn_index = index; if (!emitIndexOp(JSOP_LAMBDA, index)) return false; MOZ_ASSERT(pn->getOp() == JSOP_GETLOCAL || pn->getOp() == JSOP_GETARG); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 9fa3593fa6ee..96cc3defcf36 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -655,7 +655,6 @@ class ParseNode #define pn_cookie pn_u.name.cookie #define pn_dflags pn_u.name.dflags #define pn_blockid pn_u.name.blockid -#define pn_index pn_u.name.blockid /* reuse as object table index */ #define pn_head pn_u.list.head #define pn_tail pn_u.list.tail #define pn_count pn_u.list.count From 19b86227d65536c9df80ae375289decdd1d28597 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Thu, 30 Jul 2015 15:59:49 -0700 Subject: [PATCH 087/113] Bug 1189550 - In parent process, cpmm.initialProcessData should be same as ppmm.initialProcessData (r=smaug) --- dom/base/nsFrameMessageManager.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 9d33b8d3ffeb..dd673f8ffd64 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -1432,6 +1432,14 @@ nsFrameMessageManager::GetInitialProcessData(JSContext* aCx, JS::MutableHandleVa init.setObject(*obj); } + if (!mChrome && XRE_IsParentProcess()) { + // This is the cpmm in the parent process. We should use the same object as the ppmm. + nsCOMPtr ppmm = + do_GetService("@mozilla.org/parentprocessmessagemanager;1"); + ppmm->GetInitialProcessData(aCx, &init); + mInitialProcessData = init; + } + if (!JS_WrapValue(aCx, &init)) { return NS_ERROR_OUT_OF_MEMORY; } @@ -2164,7 +2172,6 @@ NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult) NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED); global.forget(aResult); return NS_OK; - } static PLDHashOperator From 14e7d8ca0ab6a3b1be00557f86cb0cd5687819f4 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 3 Jun 2015 15:34:44 -0700 Subject: [PATCH 088/113] Bug 1175770 - New extension API (r=Mossop) --- browser/base/content/tab-content.js | 6 + browser/components/extensions/bootstrap.js | 20 + .../extensions/ext-browserAction.js | 326 ++++++++++ .../components/extensions/ext-contextMenus.js | 8 + browser/components/extensions/ext-tabs.js | 486 +++++++++++++++ browser/components/extensions/ext-utils.js | 324 ++++++++++ browser/components/extensions/ext-windows.js | 151 +++++ browser/components/extensions/extension.svg | 19 + browser/components/extensions/jar.mn | 11 + browser/components/extensions/moz.build | 7 + browser/components/extensions/prepare.py | 83 +++ browser/components/moz.build | 1 + browser/components/nsBrowserGlue.js | 9 + browser/modules/E10SUtils.jsm | 9 + browser/themes/linux/browser.css | 4 + browser/themes/osx/browser.css | 4 + browser/themes/windows/browser.css | 4 + toolkit/components/extensions/Extension.jsm | 578 ++++++++++++++++++ .../extensions/ExtensionContent.jsm | 521 ++++++++++++++++ .../extensions/ExtensionManagement.jsm | 201 ++++++ .../extensions/ExtensionStorage.jsm | 149 +++++ .../components/extensions/ExtensionUtils.jsm | 540 ++++++++++++++++ toolkit/components/extensions/ext-alarms.js | 168 +++++ .../extensions/ext-backgroundPage.js | 92 +++ .../components/extensions/ext-extension.js | 10 + toolkit/components/extensions/ext-i18n.js | 9 + toolkit/components/extensions/ext-idle.js | 9 + .../extensions/ext-notifications.js | 140 +++++ toolkit/components/extensions/ext-runtime.js | 47 ++ toolkit/components/extensions/ext-storage.js | 48 ++ .../extensions/ext-webNavigation.js | 70 +++ .../components/extensions/ext-webRequest.js | 104 ++++ toolkit/components/extensions/jar.mn | 16 + toolkit/components/extensions/moz.build | 15 + toolkit/components/moz.build | 1 + toolkit/components/utils/simpleServices.js | 6 +- toolkit/modules/Locale.jsm | 93 +++ toolkit/modules/addons/MatchPattern.jsm | 13 +- toolkit/modules/moz.build | 1 + .../extensions/internal/XPIProvider.jsm | 84 +-- 40 files changed, 4303 insertions(+), 84 deletions(-) create mode 100644 browser/components/extensions/bootstrap.js create mode 100644 browser/components/extensions/ext-browserAction.js create mode 100644 browser/components/extensions/ext-contextMenus.js create mode 100644 browser/components/extensions/ext-tabs.js create mode 100644 browser/components/extensions/ext-utils.js create mode 100644 browser/components/extensions/ext-windows.js create mode 100644 browser/components/extensions/extension.svg create mode 100644 browser/components/extensions/jar.mn create mode 100644 browser/components/extensions/moz.build create mode 100644 browser/components/extensions/prepare.py create mode 100644 toolkit/components/extensions/Extension.jsm create mode 100644 toolkit/components/extensions/ExtensionContent.jsm create mode 100644 toolkit/components/extensions/ExtensionManagement.jsm create mode 100644 toolkit/components/extensions/ExtensionStorage.jsm create mode 100644 toolkit/components/extensions/ExtensionUtils.jsm create mode 100644 toolkit/components/extensions/ext-alarms.js create mode 100644 toolkit/components/extensions/ext-backgroundPage.js create mode 100644 toolkit/components/extensions/ext-extension.js create mode 100644 toolkit/components/extensions/ext-i18n.js create mode 100644 toolkit/components/extensions/ext-idle.js create mode 100644 toolkit/components/extensions/ext-notifications.js create mode 100644 toolkit/components/extensions/ext-runtime.js create mode 100644 toolkit/components/extensions/ext-storage.js create mode 100644 toolkit/components/extensions/ext-webNavigation.js create mode 100644 toolkit/components/extensions/ext-webRequest.js create mode 100644 toolkit/components/extensions/jar.mn create mode 100644 toolkit/components/extensions/moz.build create mode 100644 toolkit/modules/Locale.jsm diff --git a/browser/base/content/tab-content.js b/browser/base/content/tab-content.js index ea7179586b41..75ede343ec1a 100644 --- a/browser/base/content/tab-content.js +++ b/browser/base/content/tab-content.js @@ -9,6 +9,7 @@ let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/ExtensionContent.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils", "resource:///modules/E10SUtils.jsm"); @@ -656,3 +657,8 @@ let DOMFullscreenHandler = { } }; DOMFullscreenHandler.init(); + +ExtensionContent.init(this); +addEventListener("unload", () => { + ExtensionContent.uninit(this); +}); diff --git a/browser/components/extensions/bootstrap.js b/browser/components/extensions/bootstrap.js new file mode 100644 index 000000000000..19a648bb7274 --- /dev/null +++ b/browser/components/extensions/bootstrap.js @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +Components.utils.import("resource://gre/modules/Extension.jsm"); + +let extension; + +function startup(data, reason) +{ + extension = new Extension(data); + extension.startup(); +} + +function shutdown(data, reason) +{ + extension.shutdown(); +} diff --git a/browser/components/extensions/ext-browserAction.js b/browser/components/extensions/ext-browserAction.js new file mode 100644 index 000000000000..f29cd2038a47 --- /dev/null +++ b/browser/components/extensions/ext-browserAction.js @@ -0,0 +1,326 @@ +XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI", + "resource:///modules/CustomizableUI.jsm"); + +Cu.import("resource://gre/modules/devtools/event-emitter.js"); + +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + EventManager, + DefaultWeakMap, + ignoreEvent, + runSafe, +} = ExtensionUtils; + +// WeakMap[Extension -> BrowserAction] +let browserActionMap = new WeakMap(); + +function browserActionOf(extension) +{ + return browserActionMap.get(extension); +} + +function makeWidgetId(id) +{ + id = id.toLowerCase(); + return id.replace(/[^a-z0-9_-]/g, "_"); +} + +let nextActionId = 0; + +// Responsible for the browser_action section of the manifest as well +// as the associated popup. +function BrowserAction(options, extension) +{ + this.extension = extension; + this.id = makeWidgetId(extension.id) + "-browser-action"; + this.widget = null; + + this.title = new DefaultWeakMap(extension.localize(options.default_title)); + this.badgeText = new DefaultWeakMap(); + this.badgeBackgroundColor = new DefaultWeakMap(); + this.icon = new DefaultWeakMap(options.default_icon); + this.popup = new DefaultWeakMap(options.default_popup); + + // Make the default something that won't compare equal to anything. + this.prevPopups = new DefaultWeakMap({}); + + this.context = null; +} + +BrowserAction.prototype = { + build() { + let widget = CustomizableUI.createWidget({ + id: this.id, + type: "custom", + removable: true, + defaultArea: CustomizableUI.AREA_NAVBAR, + onBuild: document => { + let node = document.createElement("toolbarbutton"); + node.id = this.id; + node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional badged-button"); + node.setAttribute("constrain-size", "true"); + + this.updateTab(null, node); + + let tabbrowser = document.defaultView.gBrowser; + tabbrowser.ownerDocument.addEventListener("TabSelect", () => { + this.updateTab(tabbrowser.selectedTab, node); + }); + + node.addEventListener("command", event => { + if (node.getAttribute("type") != "panel") { + this.emit("click"); + } + }); + + return node; + }, + }); + this.widget = widget; + }, + + // Initialize the toolbar icon and popup given that |tab| is the + // current tab and |node| is the CustomizableUI node. Note: |tab| + // will be null if we don't know the current tab yet (during + // initialization). + updateTab(tab, node) { + let window = node.ownerDocument.defaultView; + + let title = this.getProperty(tab, "title"); + if (title) { + node.setAttribute("tooltiptext", title); + node.setAttribute("label", title); + } else { + node.removeAttribute("tooltiptext"); + node.removeAttribute("label"); + } + + let badgeText = this.badgeText.get(tab); + if (badgeText) { + node.setAttribute("badge", badgeText); + } else { + node.removeAttribute("badge"); + } + + function toHex(n) { + return Math.floor(n / 16).toString(16) + (n % 16).toString(16); + } + + let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node, + 'class', 'toolbarbutton-badge'); + if (badgeNode) { + let color = this.badgeBackgroundColor.get(tab); + if (Array.isArray(color)) { + color = `rgb(${color[0]}, ${color[1]}, ${color[2]})`; + } + badgeNode.style.backgroundColor = color; + } + + let iconURL = this.getIcon(tab, node); + node.setAttribute("image", iconURL); + + let popup = this.getProperty(tab, "popup"); + + if (popup != this.prevPopups.get(window)) { + this.prevPopups.set(window, popup); + + let panel = node.querySelector("panel"); + if (panel) { + panel.remove(); + } + + if (popup) { + let popupURL = this.extension.baseURI.resolve(popup); + node.setAttribute("type", "panel"); + + let document = node.ownerDocument; + let panel = document.createElement("panel"); + panel.setAttribute("class", "browser-action-panel"); + panel.setAttribute("type", "arrow"); + panel.setAttribute("flip", "slide"); + node.appendChild(panel); + + let browser = document.createElementNS(XUL_NS, "browser"); + browser.setAttribute("type", "content"); + browser.setAttribute("disableglobalhistory", "true"); + browser.setAttribute("width", "500"); + browser.setAttribute("height", "500"); + panel.appendChild(browser); + + let loadListener = () => { + panel.removeEventListener("load", loadListener); + + if (this.context) { + this.context.unload(); + } + + this.context = new ExtensionPage(this.extension, { + type: "popup", + contentWindow: browser.contentWindow, + uri: Services.io.newURI(popupURL, null, null), + docShell: browser.docShell, + }); + GlobalManager.injectInDocShell(browser.docShell, this.extension, this.context); + browser.setAttribute("src", popupURL); + }; + panel.addEventListener("load", loadListener); + } else { + node.removeAttribute("type"); + } + } + }, + + // Note: tab is allowed to be null here. + getIcon(tab, node) { + let icon = this.icon.get(tab); + + let url; + if (typeof(icon) != "object") { + url = icon; + } else { + let window = node.ownerDocument.defaultView; + let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowUtils); + let res = {value: 1} + utils.getResolution(res); + + let size = res.value == 1 ? 19 : 38; + url = icon[size]; + } + + if (url) { + return this.extension.baseURI.resolve(url); + } else { + return "chrome://browser/content/extension.svg"; + } + }, + + // Update the toolbar button for a given window. + updateWindow(window) { + let tab = window.gBrowser ? window.gBrowser.selectedTab : null; + let node = CustomizableUI.getWidget(this.id).forWindow(window).node; + this.updateTab(tab, node); + }, + + // Update the toolbar button when the extension changes the icon, + // title, badge, etc. If it only changes a parameter for a single + // tab, |tab| will be that tab. Otherwise it will be null. + updateOnChange(tab) { + if (tab) { + if (tab.selected) { + this.updateWindow(tab.ownerDocument.defaultView); + } + } else { + let e = Services.wm.getEnumerator("navigator:browser"); + while (e.hasMoreElements()) { + let window = e.getNext(); + if (window.gBrowser) { + this.updateWindow(window); + } + } + } + }, + + // tab is allowed to be null. + // prop should be one of "icon", "title", "badgeText", "popup", or "badgeBackgroundColor". + setProperty(tab, prop, value) { + this[prop].set(tab, value); + this.updateOnChange(tab); + }, + + // tab is allowed to be null. + // prop should be one of "title", "badgeText", "popup", or "badgeBackgroundColor". + getProperty(tab, prop) { + return this[prop].get(tab); + }, + + shutdown() { + CustomizableUI.destroyWidget(this.id); + }, +}; + +EventEmitter.decorate(BrowserAction.prototype); + +extensions.on("manifest_browser_action", (type, directive, extension, manifest) => { + let browserAction = new BrowserAction(manifest.browser_action, extension); + browserAction.build(); + browserActionMap.set(extension, browserAction); +}); + +extensions.on("shutdown", (type, extension) => { + if (browserActionMap.has(extension)) { + browserActionMap.get(extension).shutdown(); + browserActionMap.delete(extension); + } +}); + +extensions.registerAPI((extension, context) => { + return { + browserAction: { + onClicked: new EventManager(context, "browserAction.onClicked", fire => { + let listener = () => { + let tab = TabManager.activeTab; + fire(TabManager.convert(extension, tab)); + }; + browserActionOf(extension).on("click", listener); + return () => { + browserActionOf(extension).off("click", listener); + }; + }).api(), + + setTitle: function(details) { + let tab = details.tabId ? TabManager.getTab(details.tabId) : null; + browserActionOf(extension).setProperty(tab, "title", details.title); + }, + + getTitle: function(details, callback) { + let tab = details.tabId ? TabManager.getTab(details.tabId) : null; + let title = browserActionOf(extension).getProperty(tab, "title"); + runSafe(context, callback, title); + }, + + setIcon: function(details, callback) { + let tab = details.tabId ? TabManager.getTab(details.tabId) : null; + if (details.imageData) { + // FIXME: Support the imageData attribute. + return; + } + browserActionOf(extension).setProperty(tab, "icon", details.path); + }, + + setBadgeText: function(details) { + let tab = details.tabId ? TabManager.getTab(details.tabId) : null; + browserActionOf(extension).setProperty(tab, "badgeText", details.text); + }, + + getBadgeText: function(details, callback) { + let tab = details.tabId ? TabManager.getTab(details.tabId) : null; + let text = browserActionOf(extension).getProperty(tab, "badgeText"); + runSafe(context, callback, text); + }, + + setPopup: function(details) { + let tab = details.tabId ? TabManager.getTab(details.tabId) : null; + browserActionOf(extension).setProperty(tab, "popup", details.popup); + }, + + getPopup: function(details, callback) { + let tab = details.tabId ? TabManager.getTab(details.tabId) : null; + let popup = browserActionOf(extension).getProperty(tab, "popup"); + runSafe(context, callback, popup); + }, + + setBadgeBackgroundColor: function(details) { + let color = details.color; + let tab = details.tabId ? TabManager.getTab(details.tabId) : null; + browserActionOf(extension).setProperty(tab, "badgeBackgroundColor", details.color); + }, + + getBadgeBackgroundColor: function(details, callback) { + let tab = details.tabId ? TabManager.getTab(details.tabId) : null; + let color = browserActionOf(extension).getProperty(tab, "badgeBackgroundColor"); + runSafe(context, callback, color); + }, + } + }; +}); diff --git a/browser/components/extensions/ext-contextMenus.js b/browser/components/extensions/ext-contextMenus.js new file mode 100644 index 000000000000..671318ab322e --- /dev/null +++ b/browser/components/extensions/ext-contextMenus.js @@ -0,0 +1,8 @@ +extensions.registerPrivilegedAPI("contextMenus", (extension, context) => { + return { + contextMenus: { + create() {}, + removeAll() {}, + }, + }; +}); diff --git a/browser/components/extensions/ext-tabs.js b/browser/components/extensions/ext-tabs.js new file mode 100644 index 000000000000..4050cd67d2ff --- /dev/null +++ b/browser/components/extensions/ext-tabs.js @@ -0,0 +1,486 @@ +XPCOMUtils.defineLazyModuleGetter(this, "NewTabURL", + "resource:///modules/NewTabURL.jsm"); + +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + EventManager, + ignoreEvent, + runSafe, +} = ExtensionUtils; + +// This function is pretty tightly tied to Extension.jsm. +// Its job is to fill in the |tab| property of the sender. +function getSender(context, target, sender) +{ + // The message was sent from a content script to a element. + // We can just get the |tab| from |target|. + if (target instanceof Ci.nsIDOMXULElement) { + // The message came from a content script. + let tabbrowser = target.ownerDocument.defaultView.gBrowser; + if (!tabbrowser) { + return; + } + let tab = tabbrowser.getTabForBrowser(target); + + sender.tab = TabManager.convert(context.extension, tab); + } else { + // The message came from an ExtensionPage. In that case, it should + // include a tabId property (which is filled in by the page-open + // listener below). + if ("tabId" in sender) { + sender.tab = TabManager.convert(context.extension, TabManager.getTab(sender.tabId)); + delete sender.tabId; + } + } +} + +// WeakMap[ExtensionPage -> {tab, parentWindow}] +let pageDataMap = new WeakMap(); + +// This listener fires whenever an extension page opens in a tab +// (either initiated by the extension or the user). Its job is to fill +// in some tab-specific details and keep data around about the +// ExtensionPage. +extensions.on("page-load", (type, page, params, sender, delegate) => { + if (params.type == "tab") { + let browser = params.docShell.chromeEventHandler; + let parentWindow = browser.ownerDocument.defaultView; + let tab = parentWindow.gBrowser.getTabForBrowser(browser); + sender.tabId = TabManager.getId(tab); + + pageDataMap.set(page, {tab, parentWindow}); + } + + delegate.getSender = getSender; +}); + +extensions.on("page-unload", (type, page) => { + pageDataMap.delete(page); +}); + +extensions.on("page-shutdown", (type, page) => { + if (pageDataMap.has(page)) { + let {tab, parentWindow} = pageDataMap.get(page); + pageDataMap.delete(page); + + parentWindow.gBrowser.removeTab(tab); + } +}); + +extensions.on("fill-browser-data", (type, browser, data, result) => { + let tabId = TabManager.getBrowserId(browser); + if (tabId == -1) { + result.cancel = true; + return; + } + + data.tabId = tabId; +}); + +// TODO: activeTab permission + +extensions.registerAPI((extension, context) => { + let self = { + tabs: { + onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => { + let tab = event.originalTarget; + let tabId = TabManager.getId(tab); + let windowId = WindowManager.getId(tab.ownerDocument.defaultView); + fire({tabId, windowId}); + }).api(), + + onCreated: new EventManager(context, "tabs.onCreated", fire => { + let listener = event => { + let tab = event.originalTarget; + fire({tab: TabManager.convert(extension, tab)}); + }; + + let windowListener = window => { + for (let tab of window.gBrowser.tabs) { + fire({tab: TabManager.convert(extension, tab)}); + } + }; + + WindowListManager.addOpenListener(windowListener, false); + AllWindowEvents.addListener("TabOpen", listener); + return () => { + WindowListManager.removeOpenListener(windowListener); + AllWindowEvents.removeListener("TabOpen", listener); + }; + }).api(), + + onUpdated: new EventManager(context, "tabs.onUpdated", fire => { + function sanitize(extension, changeInfo) { + let result = {}; + let nonempty = false; + for (let prop in changeInfo) { + if ((prop != "favIconUrl" && prop != "url") || extension.hasPermission("tabs")) { + nonempty = true; + result[prop] = changeInfo[prop]; + } + } + return [nonempty, result]; + } + + let listener = event => { + let tab = event.originalTarget; + let window = tab.ownerDocument.defaultView; + let tabId = TabManager.getId(tab); + + let changeInfo = {}; + let needed = false; + if (event.type == "TabAttrModified") { + if (event.detail.changed.indexOf("image") != -1) { + changeInfo.favIconUrl = window.gBrowser.getIcon(tab); + needed = true; + } + } else if (event.type == "TabPinned") { + changeInfo.pinned = true; + needed = true; + } else if (event.type == "TabUnpinned") { + changeInfo.pinned = false; + needed = true; + } + + [needed, changeInfo] = sanitize(extension, changeInfo); + if (needed) { + fire(tabId, changeInfo, TabManager.convert(extension, tab)); + } + }; + let progressListener = { + onStateChange(browser, webProgress, request, stateFlags, statusCode) { + if (!webProgress.isTopLevel) { + return; + } + + let status; + if (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) { + if (stateFlags & Ci.nsIWebProgressListener.STATE_START) { + status = "loading"; + } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) { + status = "complete"; + } + } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP && + statusCode == Cr.NS_BINDING_ABORTED) { + status = "complete"; + } + + let gBrowser = browser.ownerDocument.defaultView.gBrowser; + let tab = gBrowser.getTabForBrowser(browser); + let tabId = TabManager.getId(tab); + let [needed, changeInfo] = sanitize(extension, {status}); + fire(tabId, changeInfo, TabManager.convert(extension, tab)); + }, + + onLocationChange(browser, webProgress, request, locationURI, flags) { + let gBrowser = browser.ownerDocument.defaultView.gBrowser; + let tab = gBrowser.getTabForBrowser(browser); + let tabId = TabManager.getId(tab); + let [needed, changeInfo] = sanitize(extension, {url: locationURI.spec}); + if (needed) { + fire(tabId, changeInfo, TabManager.convert(extension, tab)); + } + }, + }; + + AllWindowEvents.addListener("progress", progressListener); + AllWindowEvents.addListener("TabAttrModified", listener); + AllWindowEvents.addListener("TabPinned", listener); + AllWindowEvents.addListener("TabUnpinned", listener); + return () => { + AllWindowEvents.removeListener("progress", progressListener); + AllWindowEvents.addListener("TabAttrModified", listener); + AllWindowEvents.addListener("TabPinned", listener); + AllWindowEvents.addListener("TabUnpinned", listener); + }; + }).api(), + + onReplaced: ignoreEvent(), + + onRemoved: new EventManager(context, "tabs.onRemoved", fire => { + let tabListener = event => { + let tab = event.originalTarget; + let tabId = TabManager.getId(tab); + let windowId = WindowManager.getId(tab.ownerDocument.defaultView); + let removeInfo = {windowId, isWindowClosing: false}; + fire(tabId, removeInfo); + }; + + let windowListener = window => { + for (let tab of window.gBrowser.tabs) { + let tabId = TabManager.getId(tab); + let windowId = WindowManager.getId(window); + let removeInfo = {windowId, isWindowClosing: true}; + fire(tabId, removeInfo); + } + }; + + WindowListManager.addCloseListener(windowListener); + AllWindowEvents.addListener("TabClose", tabListener); + return () => { + WindowListManager.removeCloseListener(windowListener); + AllWindowEvents.removeListener("TabClose", tabListener); + }; + }).api(), + + create: function(createProperties, callback) { + if (!createProperties) { + createProperties = {}; + } + + let url = createProperties.url || NewTabURL.get(); + url = extension.baseURI.resolve(url); + + function createInWindow(window) { + let tab = window.gBrowser.addTab(url); + + let active = true; + if ("active" in createProperties) { + active = createProperties.active; + } else if ("selected" in createProperties) { + active = createProperties.selected; + } + if (active) { + window.gBrowser.selectedTab = tab; + } + + if ("index" in createProperties) { + window.gBrowser.moveTabTo(tab, createProperties.index); + } + + if (createProperties.pinned) { + window.gBrowser.pinTab(tab); + } + + if (callback) { + runSafe(context, callback, TabManager.convert(extension, tab)); + } + } + + let window = createProperties.windowId ? + WindowManager.getWindow(createProperties.windowId) : + WindowManager.topWindow; + if (!window.gBrowser) { + let obs = (finishedWindow, topic, data) => { + if (finishedWindow != window) { + return; + } + Services.obs.removeObserver(obs, "browser-delayed-startup-finished"); + createInWindow(window); + }; + Services.obs.addObserver(obs, "browser-delayed-startup-finished", false); + } else { + createInWindow(window); + } + }, + + remove: function(tabs, callback) { + if (!Array.isArray(tabs)) { + tabs = [tabs]; + } + + for (let tabId of tabs) { + let tab = TabManager.getTab(tabId); + tab.ownerDocument.defaultView.gBrowser.removeTab(tab); + } + + if (callback) { + runSafe(context, callback); + } + }, + + update: function(...args) { + let tabId, updateProperties, callback; + if (args.length == 1) { + updateProperties = args[0]; + } else { + [tabId, updateProperties, callback] = args; + } + + let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab; + let tabbrowser = tab.ownerDocument.gBrowser; + if ("url" in updateProperties) { + tab.linkedBrowser.loadURI(updateProperties.url); + } + if ("active" in updateProperties) { + if (updateProperties.active) { + tabbrowser.selectedTab = tab; + } else { + // Not sure what to do here? Which tab should we select? + } + } + if ("pinned" in updateProperties) { + if (updateProperties.pinned) { + tabbrowser.pinTab(tab); + } else { + tabbrowser.unpinTab(tab); + } + } + // FIXME: highlighted/selected, openerTabId + + if (callback) { + runSafe(context, callback, TabManager.convert(extension, tab)); + } + }, + + reload: function(tabId, reloadProperties, callback) { + let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab; + let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; + if (reloadProperties && reloadProperties.bypassCache) { + flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; + } + tab.linkedBrowser.reloadWithFlags(flags); + + if (callback) { + runSafe(context, callback); + } + }, + + get: function(tabId, callback) { + let tab = TabManager.getTab(tabId); + runSafe(context, callback, TabManager.convert(extension, tab)); + }, + + getAllInWindow: function(...args) { + let window, callback; + if (args.length == 1) { + callbacks = args[0]; + } else { + window = WindowManager.getWindow(args[0]); + callback = args[1]; + } + + if (!window) { + window = WindowManager.topWindow; + } + + return self.tabs.query({windowId: WindowManager.getId(window)}, callback); + }, + + query: function(queryInfo, callback) { + if (!queryInfo) { + queryInfo = {}; + } + + function matches(window, tab) { + let props = ["active", "pinned", "highlighted", "status", "title", "url", "index"]; + for (let prop of props) { + if (prop in queryInfo && queryInfo[prop] != tab[prop]) { + return false; + } + } + + let lastFocused = window == WindowManager.topWindow; + if ("lastFocusedWindow" in queryInfo && queryInfo.lastFocusedWindow != lastFocused) { + return false; + } + + let windowType = WindowManager.windowType(window); + if ("windowType" in queryInfo && queryInfo.windowType != windowType) { + return false; + } + + if ("windowId" in queryInfo) { + if (queryInfo.windowId == WindowManager.WINDOW_ID_CURRENT) { + if (context.contentWindow != window) { + return false; + } + } else { + if (queryInfo.windowId != tab.windowId) { + return false; + } + } + } + + if ("currentWindow" in queryInfo) { + let eq = window == context.contentWindow; + if (queryInfo.currentWindow != eq) { + return false; + } + } + + return true; + } + + let result = []; + let e = Services.wm.getEnumerator("navigator:browser"); + while (e.hasMoreElements()) { + let window = e.getNext(); + let tabs = TabManager.getTabs(extension, window); + for (let tab of tabs) { + if (matches(window, tab)) { + result.push(tab); + } + } + } + runSafe(context, callback, result); + }, + + _execute: function(tabId, details, kind, callback) { + let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab; + let mm = tab.linkedBrowser.messageManager; + + let options = {js: [], css: []}; + if (details.code) { + options[kind + 'Code'] = details.code; + } + if (details.file) { + options[kind].push(extension.baseURI.resolve(details.file)); + } + if (details.allFrames) { + options.all_frames = details.allFrames; + } + if (details.matchAboutBlank) { + options.match_about_blank = details.matchAboutBlank; + } + if (details.runAt) { + options.run_at = details.runAt; + } + mm.sendAsyncMessage("Extension:Execute", + {extensionId: extension.id, options}); + + // TODO: Call the callback with the result (which is what???). + }, + + executeScript: function(...args) { + if (args.length == 1) { + self.tabs._execute(undefined, args[0], 'js', undefined); + } else { + self.tabs._execute(args[0], args[1], 'js', args[2]); + } + }, + + insertCss: function(tabId, details, callback) { + if (args.length == 1) { + self.tabs._execute(undefined, args[0], 'css', undefined); + } else { + self.tabs._execute(args[0], args[1], 'css', args[2]); + } + }, + + connect: function(tabId, connectInfo) { + let tab = TabManager.getTab(tabId); + let mm = tab.linkedBrowser.messageManager; + + let name = connectInfo.name || ""; + let recipient = {extensionId: extension.id}; + if ("frameId" in connectInfo) { + recipient.frameId = connectInfo.frameId; + } + return context.messenger.connect(mm, name, recipient); + }, + + sendMessage: function(tabId, message, options, responseCallback) { + let tab = TabManager.getTab(tabId); + let mm = tab.linkedBrowser.messageManager; + + let recipient = {extensionId: extension.id}; + if (options && "frameId" in options) { + recipient.frameId = options.frameId; + } + return context.messenger.sendMessage(mm, message, recipient, responseCallback); + }, + }, + }; + return self; +}); diff --git a/browser/components/extensions/ext-utils.js b/browser/components/extensions/ext-utils.js new file mode 100644 index 000000000000..83d58123bc4a --- /dev/null +++ b/browser/components/extensions/ext-utils.js @@ -0,0 +1,324 @@ +XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", + "resource://gre/modules/PrivateBrowsingUtils.jsm"); + +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + EventManager, +} = ExtensionUtils; + +// This file provides some useful code for the |tabs| and |windows| +// modules. All of the code is installed on |global|, which is a scope +// shared among the different ext-*.js scripts. + +// Manages mapping between XUL tabs and extension tab IDs. +global.TabManager = { + _tabs: new WeakMap(), + _nextId: 1, + + getId(tab) { + if (this._tabs.has(tab)) { + return this._tabs.get(tab); + } + let id = this._nextId++; + this._tabs.set(tab, id); + return id; + }, + + getBrowserId(browser) { + let gBrowser = browser.ownerDocument.defaultView.gBrowser; + // Some non-browser windows have gBrowser but not + // getTabForBrowser! + if (gBrowser && gBrowser.getTabForBrowser) { + let tab = gBrowser.getTabForBrowser(browser); + if (tab) { + return this.getId(tab); + } + } + return -1; + }, + + getTab(tabId) { + // FIXME: Speed this up without leaking memory somehow. + let e = Services.wm.getEnumerator("navigator:browser"); + while (e.hasMoreElements()) { + let window = e.getNext(); + if (!window.gBrowser) { + continue; + } + for (let tab of window.gBrowser.tabs) { + if (this.getId(tab) == tabId) { + return tab; + } + } + } + return null; + }, + + get activeTab() { + let window = WindowManager.topWindow; + if (window && window.gBrowser) { + return window.gBrowser.selectedTab; + } + return null; + }, + + getStatus(tab) { + return tab.getAttribute("busy") == "true" ? "loading" : "complete"; + }, + + convert(extension, tab) { + let window = tab.ownerDocument.defaultView; + let windowActive = window == WindowManager.topWindow; + let result = { + id: this.getId(tab), + index: tab._tPos, + windowId: WindowManager.getId(window), + selected: tab.selected, + highlighted: tab.selected, + active: tab.selected, + pinned: tab.pinned, + status: this.getStatus(tab), + incognito: PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser), + width: tab.linkedBrowser.clientWidth, + height: tab.linkedBrowser.clientHeight, + }; + + if (extension.hasPermission("tabs")) { + result.url = tab.linkedBrowser.currentURI.spec; + if (tab.linkedBrowser.contentTitle) { + result.title = tab.linkedBrowser.contentTitle; + } + let icon = window.gBrowser.getIcon(tab); + if (icon) { + result.favIconUrl = icon; + } + } + + return result; + }, + + getTabs(extension, window) { + if (!window.gBrowser) { + return []; + } + return [ for (tab of window.gBrowser.tabs) this.convert(extension, tab) ]; + }, +}; + +// Manages mapping between XUL windows and extension window IDs. +global.WindowManager = { + _windows: new WeakMap(), + _nextId: 0, + + WINDOW_ID_NONE: -1, + WINDOW_ID_CURRENT: -2, + + get topWindow() { + return Services.wm.getMostRecentWindow("navigator:browser"); + }, + + windowType(window) { + // TODO: Make this work. + return "normal"; + }, + + getId(window) { + if (this._windows.has(window)) { + return this._windows.get(window); + } + let id = this._nextId++; + this._windows.set(window, id); + return id; + }, + + getWindow(id) { + let e = Services.wm.getEnumerator("navigator:browser"); + while (e.hasMoreElements()) { + let window = e.getNext(); + if (this.getId(window) == id) { + return window; + } + } + return null; + }, + + convert(extension, window, getInfo) { + let result = { + id: this.getId(window), + focused: window == WindowManager.topWindow, + top: window.screenY, + left: window.screenX, + width: window.outerWidth, + height: window.outerHeight, + incognito: PrivateBrowsingUtils.isWindowPrivate(window), + + // We fudge on these next two. + type: this.windowType(window), + state: window.fullScreen ? "fullscreen" : "normal", + }; + + if (getInfo && getInfo.populate) { + results.tabs = TabManager.getTabs(extension, window); + } + + return result; + }, +}; + +// Manages listeners for window opening and closing. A window is +// considered open when the "load" event fires on it. A window is +// closed when a "domwindowclosed" notification fires for it. +global.WindowListManager = { + _openListeners: new Set(), + _closeListeners: new Set(), + + addOpenListener(listener, fireOnExisting = true) { + if (this._openListeners.length == 0 && this._closeListeners.length == 0) { + Services.ww.registerNotification(this); + } + this._openListeners.add(listener); + + let e = Services.wm.getEnumerator("navigator:browser"); + while (e.hasMoreElements()) { + let window = e.getNext(); + if (window.document.readyState != "complete") { + window.addEventListener("load", this); + } else if (fireOnExisting) { + listener(window); + } + } + }, + + removeOpenListener(listener) { + this._openListeners.delete(listener); + if (this._openListeners.length == 0 && this._closeListeners.length == 0) { + Services.ww.unregisterNotification(this); + } + }, + + addCloseListener(listener) { + if (this._openListeners.length == 0 && this._closeListeners.length == 0) { + Services.ww.registerNotification(this); + } + this._closeListeners.add(listener); + }, + + removeCloseListener(listener) { + this._closeListeners.delete(listener); + if (this._openListeners.length == 0 && this._closeListeners.length == 0) { + Services.ww.unregisterNotification(this); + } + }, + + handleEvent(event) { + let window = event.target.defaultView; + window.removeEventListener("load", this.loadListener); + if (window.document.documentElement.getAttribute("windowtype") != "navigator:browser") { + return; + } + + for (let listener of this._openListeners) { + listener(window); + } + }, + + queryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), + + observe(window, topic, data) { + if (topic == "domwindowclosed") { + if (window.document.documentElement.getAttribute("windowtype") != "navigator:browser") { + return; + } + + window.removeEventListener("load", this); + for (let listener of this._closeListeners) { + listener(window); + } + } else { + window.addEventListener("load", this); + } + }, +}; + +// Provides a facility to listen for DOM events across all XUL windows. +global.AllWindowEvents = { + _listeners: new Map(), + + // If |type| is a normal event type, invoke |listener| each time + // that event fires in any open window. If |type| is "progress", add + // a web progress listener that covers all open windows. + addListener(type, listener) { + if (type == "domwindowopened") { + return WindowListManager.addOpenListener(listener); + } else if (type == "domwindowclosed") { + return WindowListManager.addCloseListener(listener); + } + + let needOpenListener = this._listeners.size == 0; + + if (!this._listeners.has(type)) { + this._listeners.set(type, new Set()); + } + let list = this._listeners.get(type); + list.add(listener); + + if (needOpenListener) { + WindowListManager.addOpenListener(this.openListener); + } + }, + + removeListener(type, listener) { + if (type == "domwindowopened") { + return WindowListManager.removeOpenListener(listener); + } else if (type == "domwindowclosed") { + return WindowListManager.removeCloseListener(listener); + } + + let listeners = this._listeners.get(type); + listeners.delete(listener); + if (listeners.length == 0) { + this._listeners.delete(type); + if (this._listeners.size == 0) { + WindowListManager.removeOpenListener(this.openListener); + } + } + + let e = Services.wm.getEnumerator("navigator:browser"); + while (e.hasMoreElements()) { + let window = e.getNext(); + if (type == "progress") { + window.gBrowser.removeTabsProgressListener(listener); + } else { + window.removeEventListener(type, listener); + } + } + }, + + // Runs whenever the "load" event fires for a new window. + openListener(window) { + for (let [eventType, listeners] of AllWindowEvents._listeners) { + for (let listener of listeners) { + if (eventType == "progress") { + window.gBrowser.addTabsProgressListener(listener); + } else { + window.addEventListener(eventType, listener); + } + } + } + }, +}; + +// Subclass of EventManager where we just need to call +// add/removeEventListener on each XUL window. +global.WindowEventManager = function(context, name, event, listener) +{ + EventManager.call(this, context, name, fire => { + let listener2 = (...args) => listener(fire, ...args); + AllWindowEvents.addListener(event, listener2); + return () => { + AllWindowEvents.removeListener(event, listener2); + } + }); +} + +WindowEventManager.prototype = Object.create(EventManager.prototype); diff --git a/browser/components/extensions/ext-windows.js b/browser/components/extensions/ext-windows.js new file mode 100644 index 000000000000..f83bc4b81949 --- /dev/null +++ b/browser/components/extensions/ext-windows.js @@ -0,0 +1,151 @@ +XPCOMUtils.defineLazyModuleGetter(this, "NewTabURL", + "resource:///modules/NewTabURL.jsm"); + +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + EventManager, + ignoreEvent, + runSafe, +} = ExtensionUtils; + +extensions.registerAPI((extension, context) => { + return { + windows: { + WINDOW_ID_CURRENT: WindowManager.WINDOW_ID_CURRENT, + WINDOW_ID_NONE: WindowManager.WINDOW_ID_NONE, + + onCreated: + new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => { + fire(WindowManager.convert(extension, window)); + }).api(), + + onRemoved: + new WindowEventManager(context, "windows.onRemoved", "domwindowclosed", (fire, window) => { + fire(WindowManager.getId(window)); + }).api(), + + onFocusChanged: new EventManager(context, "windows.onFocusChanged", fire => { + // FIXME: This will send multiple messages for a single focus change. + let listener = event => { + let window = WindowManager.topWindow; + let windowId = window ? WindowManager.getId(window) : WindowManager.WINDOW_ID_NONE; + fire(windowId); + }; + AllWindowEvents.addListener("focus", listener); + AllWindowEvents.addListener("blur", listener); + return () => { + AllWindowEvents.removeListener("focus", listener); + AllWindowEvents.removeListener("blur", listener); + }; + }).api(), + + get: function(windowId, getInfo, callback) { + let window = WindowManager.getWindow(windowId); + runSafe(context, callback, WindowManager.convert(extension, window, getInfo)); + }, + + getCurrent: function(getInfo, callback) { + let window = context.contentWindow; + runSafe(context, callback, WindowManager.convert(extension, window, getInfo)); + }, + + getLastFocused: function(...args) { + let getInfo, callback; + if (args.length == 1) { + callback = args[0]; + } else { + [getInfo, callback] = args; + } + let window = WindowManager.topWindow; + runSafe(context, callback, WindowManager.convert(extension, window, getInfo)); + }, + + getAll: function(getAll, callback) { + let e = Services.wm.getEnumerator("navigator:browser"); + let windows = []; + while (e.hasMoreElements()) { + let window = e.getNext(); + windows.push(WindowManager.convert(extension, window, getInfo)); + } + runSafe(context, callback, windows); + }, + + create: function(createData, callback) { + function mkstr(s) { + let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); + result.data = s; + return result; + } + + let args = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray); + if ("url" in createData) { + if (Array.isArray(createData.url)) { + let array = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray); + for (let url of createData.url) { + array.AppendElement(mkstr(url)); + } + args.AppendElement(array); + } else { + args.AppendElement(mkstr(createData.url)); + } + } else { + args.AppendElement(mkstr(NewTabURL.get())); + } + + let extraFeatures = ""; + if ("incognito" in createData) { + if (createData.incognito) { + extraFeatures += ",private"; + } else { + extraFeatures += ",non-private"; + } + } + + let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank", + "chrome,dialog=no,all" + extraFeatures, args); + + if ("left" in createData || "top" in createData) { + let left = "left" in createData ? createData.left : window.screenX; + let top = "top" in createData ? createData.top : window.screenY; + window.moveTo(left, top); + } + if ("width" in createData || "height" in createData) { + let width = "width" in createData ? createData.width : window.outerWidth; + let height = "height" in createData ? createData.height : window.outerHeight; + window.resizeTo(width, height); + } + + // TODO: focused, type, state + + window.addEventListener("load", function listener() { + window.removeEventListener("load", listener); + if (callback) { + runSafe(context, callback, WindowManager.convert(extension, window)); + } + }); + }, + + update: function(windowId, updateInfo, callback) { + let window = WindowManager.getWindow(windowId); + if (updateInfo.focused) { + Services.focus.activeWindow = window; + } + // TODO: All the other properties... + runSafe(context, callback, WindowManager.convert(extension, window)); + }, + + remove: function(windowId, callback) { + let window = WindowManager.getWindow(windowId); + window.close(); + + let listener = () => { + AllWindowEvents.removeListener("domwindowclosed", listener); + if (callback) { + runSafe(context, callback); + } + }; + AllWindowEvents.addListener("domwindowclosed", listener); + }, + }, + }; +}); diff --git a/browser/components/extensions/extension.svg b/browser/components/extensions/extension.svg new file mode 100644 index 000000000000..a16455253834 --- /dev/null +++ b/browser/components/extensions/extension.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/browser/components/extensions/jar.mn b/browser/components/extensions/jar.mn new file mode 100644 index 000000000000..ea0b27625cf0 --- /dev/null +++ b/browser/components/extensions/jar.mn @@ -0,0 +1,11 @@ +# 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/. + +browser.jar: + content/browser/extension.svg (extension.svg) + content/browser/ext-utils.js (ext-utils.js) + content/browser/ext-contextMenus.js (ext-contextMenus.js) + content/browser/ext-browserAction.js (ext-browserAction.js) + content/browser/ext-tabs.js (ext-tabs.js) + content/browser/ext-windows.js (ext-windows.js) diff --git a/browser/components/extensions/moz.build b/browser/components/extensions/moz.build new file mode 100644 index 000000000000..3bbe6729759c --- /dev/null +++ b/browser/components/extensions/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +JAR_MANIFESTS += ['jar.mn'] diff --git a/browser/components/extensions/prepare.py b/browser/components/extensions/prepare.py new file mode 100644 index 000000000000..179adae45171 --- /dev/null +++ b/browser/components/extensions/prepare.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import argparse +import json +import uuid +import sys +import os.path + +parser = argparse.ArgumentParser(description='Create install.rdf from manifest.json') +parser.add_argument('--locale') +parser.add_argument('--profile') +parser.add_argument('--uuid') +parser.add_argument('dir') +args = parser.parse_args() + +manifestFile = os.path.join(args.dir, 'manifest.json') +manifest = json.load(open(manifestFile)) + +locale = args.locale +if not locale: + locale = manifest.get('default_locale', 'en-US') + +def process_locale(s): + if s.startswith('__MSG_') and s.endswith('__'): + tag = s[6:-2] + path = os.path.join(args.dir, '_locales', locale, 'messages.json') + data = json.load(open(path)) + return data[tag]['message'] + else: + return s + +id = args.uuid +if not id: + id = '{' + str(uuid.uuid4()) + '}' + +name = process_locale(manifest['name']) +desc = process_locale(manifest['description']) +version = manifest['version'] + +installFile = open(os.path.join(args.dir, 'install.rdf'), 'w') +print >>installFile, '' +print >>installFile, '>installFile, ' xmlns:em="http://www.mozilla.org/2004/em-rdf#">' +print >>installFile +print >>installFile, ' ' +print >>installFile, ' {}'.format(id) +print >>installFile, ' 2' +print >>installFile, ' {}'.format(name) +print >>installFile, ' {}'.format(desc) +print >>installFile, ' {}'.format(version) +print >>installFile, ' true' + +print >>installFile, ' ' +print >>installFile, ' ' +print >>installFile, ' {ec8030f7-c20a-464f-9b0e-13a3a9e97384}' +print >>installFile, ' 4.0' +print >>installFile, ' 50.0' +print >>installFile, ' ' +print >>installFile, ' ' + +print >>installFile, ' ' +print >>installFile, '' +installFile.close() + +bootstrapPath = os.path.join(os.path.dirname(sys.argv[0]), 'bootstrap.js') +data = open(bootstrapPath).read() +boot = open(os.path.join(args.dir, 'bootstrap.js'), 'w') +boot.write(data) +boot.close() + +if args.profile: + os.system('mkdir -p {}/extensions'.format(args.profile)) + output = open(args.profile + '/extensions/' + id, 'w') + print >>output, os.path.realpath(args.dir) + output.close() +else: + dir = os.path.realpath(args.dir) + if dir[-1] == os.sep: + dir = dir[:-1] + os.system('cd "{}"; zip ../"{}".xpi -r *'.format(args.dir, os.path.basename(dir))) diff --git a/browser/components/moz.build b/browser/components/moz.build index 8bcf35587a1e..673692144b9b 100644 --- a/browser/components/moz.build +++ b/browser/components/moz.build @@ -9,6 +9,7 @@ DIRS += [ 'customizableui', 'dirprovider', 'downloads', + 'extensions', 'feeds', 'loop', 'migration', diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index aad0240f2252..9f7e0d5d9280 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -169,6 +169,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher", XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement", + "resource://gre/modules/ExtensionManagement.jsm"); + const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; const PREF_PLUGINS_UPDATEURL = "plugins.update.url"; @@ -601,6 +604,12 @@ BrowserGlue.prototype = { os.addObserver(this, "xpi-signature-changed", false); os.addObserver(this, "autocomplete-did-enter-text", false); + ExtensionManagement.registerScript("chrome://browser/content/ext-utils.js"); + ExtensionManagement.registerScript("chrome://browser/content/ext-browserAction.js"); + ExtensionManagement.registerScript("chrome://browser/content/ext-contextMenus.js"); + ExtensionManagement.registerScript("chrome://browser/content/ext-tabs.js"); + ExtensionManagement.registerScript("chrome://browser/content/ext-windows.js"); + this._flashHangCount = 0; }, diff --git a/browser/modules/E10SUtils.jsm b/browser/modules/E10SUtils.jsm index a27c46ff8453..1798082069ca 100644 --- a/browser/modules/E10SUtils.jsm +++ b/browser/modules/E10SUtils.jsm @@ -59,6 +59,15 @@ this.E10SUtils = { mustLoadRemote = chromeReg.mustLoadURLRemotely(url); } + if (aURL.startsWith("moz-extension:")) { + canLoadRemote = false; + mustLoadRemote = false; + } + + if (aURL.startsWith("view-source:")) { + return this.canLoadURIInProcess(aURL.substr("view-source:".length), aProcess); + } + if (mustLoadRemote) return processIsRemote; diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 856610f65477..d25dd0f72ca0 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -1951,3 +1951,7 @@ chatbox { -moz-padding-end: 0 !important; -moz-margin-end: 0 !important; } + +.browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent { + padding: 0; +} diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index 0a23cfd5aae6..e7286466392d 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -3692,3 +3692,7 @@ window > chatbox { padding-left: 0; padding-right: 0; } + +.browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent { + padding: 0; +} diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 043c707f7137..f6be1ab2a65d 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -2912,3 +2912,7 @@ chatbox { @media not all and (-moz-os-version: windows-xp) { %include browser-aero.css } + +.browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent { + padding: 0; +} diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm new file mode 100644 index 000000000000..c73b3eefd1cb --- /dev/null +++ b/toolkit/components/extensions/Extension.jsm @@ -0,0 +1,578 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["Extension"]; + +/* + * This file is the main entry point for extensions. When an extension + * loads, its bootstrap.js file creates a Extension instance + * and calls .startup() on it. It calls .shutdown() when the extension + * unloads. Extension manages any extension-specific state in + * the chrome process. + */ + +const Ci = Components.interfaces; +const Cc = Components.classes; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); + +XPCOMUtils.defineLazyModuleGetter(this, "Locale", + "resource://gre/modules/Locale.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern", + "resource://gre/modules/MatchPattern.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", + "resource://gre/modules/NetUtil.jsm"); + +Cu.import("resource://gre/modules/ExtensionManagement.jsm"); + +// Register built-in parts of the API. Other parts may be registered +// in browser/, mobile/, or b2g/. +ExtensionManagement.registerScript("chrome://extensions/content/ext-alarms.js"); +ExtensionManagement.registerScript("chrome://extensions/content/ext-backgroundPage.js"); +ExtensionManagement.registerScript("chrome://extensions/content/ext-notifications.js"); +ExtensionManagement.registerScript("chrome://extensions/content/ext-i18n.js"); +ExtensionManagement.registerScript("chrome://extensions/content/ext-idle.js"); +ExtensionManagement.registerScript("chrome://extensions/content/ext-runtime.js"); +ExtensionManagement.registerScript("chrome://extensions/content/ext-extension.js"); +ExtensionManagement.registerScript("chrome://extensions/content/ext-webNavigation.js"); +ExtensionManagement.registerScript("chrome://extensions/content/ext-webRequest.js"); +ExtensionManagement.registerScript("chrome://extensions/content/ext-storage.js"); + +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + MessageBroker, + Messenger, + injectAPI, +} = ExtensionUtils; + +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +let scriptScope = this; + +// This object loads the ext-*.js scripts that define the extension API. +let Management = { + initialized: false, + scopes: [], + apis: [], + emitter: new EventEmitter(), + + // Loads all the ext-*.js scripts currently registered. + lazyInit() { + if (this.initialized) { + return; + } + this.initialized = true; + + for (let script of ExtensionManagement.getScripts()) { + let scope = {extensions: this, global: scriptScope}; + Services.scriptloader.loadSubScript(script, scope, "UTF-8"); + + // Save the scope to avoid it being garbage collected. + this.scopes.push(scope); + } + }, + + // Called by an ext-*.js script to register an API. The |api| + // parameter should be an object of the form: + // { + // tabs: { + // create: ..., + // onCreated: ... + // } + // } + // This registers tabs.create and tabs.onCreated as part of the API. + registerAPI(api) { + this.apis.push({api}); + }, + + // Same as above, but only register the API is the add-on has the + // given permission. + registerPrivilegedAPI(permission, api) { + this.apis.push({api, permission}); + }, + + // Mash together into a single object all the APIs registered by the + // functions above. Return the merged object. + generateAPIs(extension, context) { + let obj = {}; + + // Recursively copy properties from source to dest. + function copy(dest, source) { + for (let prop in source) { + if (typeof(source[prop]) == "object") { + if (!(prop in dest)) { + dest[prop] = {}; + } + copy(dest[prop], source[prop]); + } else { + dest[prop] = source[prop]; + } + } + } + + for (let api of this.apis) { + if (api.permission) { + if (!extension.hasPermission(api.permission)) { + continue; + } + } + + api = api.api(extension, context); + copy(obj, api); + } + + return obj; + }, + + // The ext-*.js scripts can ask to be notified for certain hooks. + on(hook, callback) { + this.emitter.on(hook, callback); + }, + + // Ask to run all the callbacks that are registered for a given hook. + emit(hook, ...args) { + this.lazyInit(); + this.emitter.emit(hook, ...args); + }, +}; + +// A MessageBroker that's used to send and receive messages for +// extension pages (which run in the chrome process). +let globalBroker = new MessageBroker([Services.mm, Services.ppmm]); + +// An extension page is an execution context for any extension content +// that runs in the chrome process. It's used for background pages +// (type="background"), popups (type="popup"), and any extension +// content loaded into browser tabs (type="tab"). +// +// |params| is an object with the following properties: +// |type| is one of "background", "popup", or "tab". +// |contentWindow| is the DOM window the content runs in. +// |uri| is the URI of the content (optional). +// |docShell| is the docshell the content runs in (optional). +function ExtensionPage(extension, params) +{ + let {type, contentWindow, uri, docShell} = params; + this.extension = extension; + this.type = type; + this.contentWindow = contentWindow || null; + this.uri = uri || extension.baseURI; + this.onClose = new Set(); + + // This is the sender property passed to the Messenger for this + // page. It can be augmented by the "page-open" hook. + let sender = {id: extension.id}; + if (uri) { + sender.url = uri.spec; + } + let delegate = {}; + Management.emit("page-load", this, params, sender, delegate); + + let filter = {id: extension.id}; + this.messenger = new Messenger(this, globalBroker, sender, filter, delegate); + + this.extension.views.add(this); +} + +ExtensionPage.prototype = { + get cloneScope() { + return this.contentWindow; + }, + + callOnClose(obj) { + this.onClose.add(obj); + }, + + forgetOnClose(obj) { + this.onClose.delete(obj); + }, + + // Called when the extension shuts down. + shutdown() { + Management.emit("page-shutdown", this); + this.unload(); + }, + + // This method is called when an extension page navigates away or + // its tab is closed. + unload() { + Management.emit("page-unload", this); + + this.extension.views.delete(this); + + for (let obj of this.onClose) { + obj.close(); + } + }, +}; + +// Responsible for loading extension APIs into the right globals. +let GlobalManager = { + // Number of extensions currently enabled. + count: 0, + + // Map[docShell -> {extension, context}] where context is an ExtensionPage. + docShells: new Map(), + + // Map[extension ID -> Extension]. Determines which extension is + // responsible for content under a particular extension ID. + extensionMap: new Map(), + + init(extension) { + if (this.count == 0) { + Services.obs.addObserver(this, "content-document-global-created", false); + } + this.count++; + + this.extensionMap.set(extension.id, extension); + }, + + uninit(extension) { + this.count--; + if (this.count == 0) { + Services.obs.removeObserver(this, "content-document-global-created"); + } + + for (let [docShell, data] of this.docShells) { + if (extension == data.extension) { + this.docShells.delete(docShell); + } + } + + this.extensionMap.delete(extension.id); + }, + + injectInDocShell(docShell, extension, context) { + this.docShells.set(docShell, {extension, context}); + }, + + observe(contentWindow, topic, data) { + function inject(extension, context) { + let chromeObj = Cu.createObjectIn(contentWindow, {defineAs: "chrome"}); + let api = Management.generateAPIs(extension, context); + injectAPI(api, chromeObj); + } + + let docShell = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .sameTypeRootTreeItem + .QueryInterface(Ci.nsIDocShell); + + if (this.docShells.has(docShell)) { + let {extension, context} = this.docShells.get(docShell); + inject(extension, context); + return; + } + + // We don't inject into sub-frames of a UI page. + if (contentWindow != contentWindow.top) { + return; + } + + // Find the add-on associated with this document via the + // principal's originAttributes. This value is computed by + // extensionURIToAddonID, which ensures that we don't inject our + // API into webAccessibleResources. + let principal = contentWindow.document.nodePrincipal; + let id = principal.originAttributes.addonId; + if (!this.extensionMap.has(id)) { + return; + } + let extension = this.extensionMap.get(id); + let uri = contentWindow.document.documentURIObject; + let context = new ExtensionPage(extension, {type: "tab", contentWindow, uri, docShell}); + inject(extension, context); + + let eventHandler = docShell.chromeEventHandler; + let listener = event => { + eventHandler.removeEventListener("unload", listener); + context.unload(); + }; + eventHandler.addEventListener("unload", listener, true); + }, +}; + +// We create one instance of this class per extension. |addonData| +// comes directly from bootstrap.js when initializing. +function Extension(addonData) +{ + let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); + let uuid = uuidGenerator.generateUUID().number; + uuid = uuid.substring(1, uuid.length - 1); // Strip of { and } off the UUID. + this.uuid = uuid; + + this.addonData = addonData; + this.id = addonData.id; + this.baseURI = Services.io.newURI("moz-extension://" + uuid, null, null); + this.manifest = null; + this.localeMessages = null; + + this.views = new Set(); + + this.onStartup = null; + + this.hasShutdown = false; + this.onShutdown = new Set(); + + this.permissions = new Set(); + this.whiteListedHosts = null; + this.webAccessibleResources = new Set(); + + ExtensionManagement.startupExtension(this.uuid, this.addonData.resourceURI, this); +} + +Extension.prototype = { + // Representation of the extension to send to content + // processes. This should include anything the content process might + // need. + serialize() { + return { + id: this.id, + uuid: this.uuid, + manifest: this.manifest, + resourceURL: this.addonData.resourceURI.spec, + baseURL: this.baseURI.spec, + content_scripts: this.manifest.content_scripts || [], + webAccessibleResources: this.webAccessibleResources, + whiteListedHosts: this.whiteListedHosts.serialize(), + }; + }, + + // https://developer.chrome.com/extensions/i18n + localizeMessage(message, substitutions) { + if (message in this.localeMessages) { + let str = this.localeMessages[message].message; + + if (!substitutions) { + substitutions = []; + } + if (!Array.isArray(substitutions)) { + substitutions = [substitutions]; + } + + // https://developer.chrome.com/extensions/i18n-messages + // |str| may contain substrings of the form $1 or $PLACEHOLDER$. + // In the former case, we replace $n with substitutions[n - 1]. + // In the latter case, we consult the placeholders array. + // The placeholder may itself use $n to refer to substitutions. + let replacer = (matched, name) => { + if (name.length == 1 && name[0] >= '1' && name[0] <= '9') { + return substitutions[parseInt(name) - 1]; + } else { + let content = this.localeMessages[message].placeholders[name].content; + if (content[0] == '$') { + return replacer(matched, content[1]); + } else { + return content; + } + } + }; + return str.replace(/\$([A-Za-z_@]+)\$/, replacer) + .replace(/\$([0-9]+)/, replacer) + .replace(/\$\$/, "$"); + } + + // Check for certain pre-defined messages. + if (message == "@@extension_id") { + return this.id; + } else if (message == "@@ui_locale") { + return Locale.getLocale(); + } else if (message == "@@bidi_dir") { + return "ltr"; // FIXME + } + + Cu.reportError(`Unknown localization message ${message}`); + return "??"; + }, + + localize(str) { + if (!str) { + return str; + } + + if (str.startsWith("__MSG_") && str.endsWith("__")) { + let message = str.substring("__MSG_".length, str.length - "__".length); + return this.localizeMessage(message); + } + + return str; + }, + + readJSON(uri) { + return new Promise((resolve, reject) => { + NetUtil.asyncFetch({uri, loadUsingSystemPrincipal: true}, (inputStream, status) => { + if (!Components.isSuccessCode(status)) { + reject(status); + return; + } + let text = NetUtil.readInputStreamToString(inputStream, inputStream.available()); + try { + resolve(JSON.parse(text)); + } catch (e) { + reject(e); + } + }); + }); + }, + + readManifest() { + let manifestURI = Services.io.newURI("manifest.json", null, this.baseURI); + return this.readJSON(manifestURI); + }, + + readLocaleFile(locale) { + let dir = locale.replace("-", "_"); + let url = `_locales/${dir}/messages.json`; + let uri = Services.io.newURI(url, null, this.baseURI); + return this.readJSON(uri); + }, + + readLocaleMessages() { + let locales = []; + + // We need to base this off of this.addonData.resourceURI rather + // than baseURI since baseURI is a moz-extension URI, which always + // QIs to nsIFileURL. + let uri = Services.io.newURI("_locales", null, this.addonData.resourceURI); + if (uri instanceof Ci.nsIFileURL) { + let file = uri.file; + let enumerator; + try { + enumerator = file.directoryEntries; + } catch (e) { + return {}; + } + while (enumerator.hasMoreElements()) { + let file = enumerator.getNext().QueryInterface(Ci.nsIFile); + locales.push({ + name: file.leafName, + locales: [file.leafName.replace("_", "-")] + }); + } + } + + if (uri instanceof Ci.nsIJARURI && uri.JARFile instanceof Ci.nsIFileURL) { + let file = uri.JARFile.file; + let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader); + try { + zipReader.open(file); + let enumerator = zipReader.findEntries("_locales/*"); + while (enumerator.hasMore()) { + let name = enumerator.getNext(); + let match = name.match(new RegExp("_locales\/([^/]*)")); + if (match && match[1]) { + locales.push({ + name: match[1], + locales: [match[1].replace("_", "-")] + }); + } + } + } finally { + zipReader.close(); + } + } + + let locale = Locale.findClosestLocale(locales); + if (locale) { + return this.readLocaleFile(locale.name).catch(() => {}); + } + return {}; + }, + + runManifest(manifest) { + let permissions = manifest.permissions || []; + let webAccessibleResources = manifest.web_accessible_resources || []; + + let whitelist = []; + for (let perm of permissions) { + if (perm.match(/:\/\//)) { + whitelist.push(perm); + } else { + this.permissions.add(perm); + } + } + this.whiteListedHosts = new MatchPattern(whitelist); + + let resources = new Set(); + for (let url of webAccessibleResources) { + resources.add(url); + } + this.webAccessibleResources = resources; + + for (let directive in manifest) { + Management.emit("manifest_" + directive, directive, this, manifest); + } + + let data = Services.ppmm.initialProcessData; + if (!data["Extension:Extensions"]) { + data["Extension:Extensions"] = []; + } + let serial = this.serialize(); + data["Extension:Extensions"].push(serial); + Services.ppmm.broadcastAsyncMessage("Extension:Startup", serial); + }, + + callOnClose(obj) { + this.onShutdown.add(obj); + }, + + forgetOnClose(obj) { + this.onShutdown.delete(obj); + }, + + startup() { + GlobalManager.init(this); + + return Promise.all([this.readManifest(), this.readLocaleMessages()]).then(([manifest, messages]) => { + if (this.hasShutdown) { + return; + } + + this.manifest = manifest; + this.localeMessages = messages; + + Management.emit("startup", this); + + this.runManifest(manifest); + }).catch(e => { + dump(`Extension error: ${e} ${e.fileName}:${e.lineNumber}\n`); + Cu.reportError(e); + }); + }, + + shutdown() { + this.hasShutdown = true; + if (!this.manifest) { + return; + } + + GlobalManager.uninit(this); + + for (let view of this.views) { + view.shutdown(); + } + + for (let obj of this.onShutdown) { + obj.close(); + } + + Management.emit("shutdown", this); + + Services.ppmm.broadcastAsyncMessage("Extension:Shutdown", {id: this.id}); + + ExtensionManagement.shutdownExtension(this.uuid); + }, + + hasPermission(perm) { + return this.permissions.has(perm); + }, +}; + diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm new file mode 100644 index 000000000000..81ea03e92128 --- /dev/null +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -0,0 +1,521 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["ExtensionContent"]; + +/* + * This file handles the content process side of extensions. It mainly + * takes care of content script injection, content script APIs, and + * messaging. + */ + +const Ci = Components.interfaces; +const Cc = Components.classes; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement", + "resource://gre/modules/ExtensionManagement.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern", + "resource://gre/modules/MatchPattern.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", + "resource://gre/modules/PrivateBrowsingUtils.jsm"); + +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + runSafeWithoutClone, + MessageBroker, + Messenger, + ignoreEvent, + injectAPI, +} = ExtensionUtils; + +function isWhenBeforeOrSame(when1, when2) +{ + let table = {"document_start": 0, + "document_end": 1, + "document_idle": 2}; + return table[when1] <= table[when2]; +} + +// This is the fairly simple API that we inject into content +// scripts. +let api = context => { return { + runtime: { + connect: function(extensionId, connectInfo) { + let name = connectInfo && connectInfo.name || ""; + let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId}; + return context.messenger.connect(context.messageManager, name, recipient); + }, + + getManifest: function(context) { + return context.extension.getManifest(); + }, + + getURL: function(path) { + return context.extension.baseURI.resolve(url); + }, + + onConnect: context.messenger.onConnect("runtime.onConnect"), + + onMessage: context.messenger.onMessage("runtime.onMessage"), + + sendMessage: function(...args) { + let extensionId, message, options, responseCallback; + if (args.length == 1) { + message = args[0]; + } else if (args.length == 2) { + [message, responseCallback] = args; + } else { + [extensionId, message, options, responseCallback] = args; + } + + let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId}; + context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback); + }, + }, + + extension: { + getURL: function(path) { + return context.extension.baseURI.resolve(url); + }, + + inIncognitoContext: PrivateBrowsingUtils.isContentWindowPrivate(context.contentWindow), + }, +}}; + +// Represents a content script. +function Script(options) +{ + this.options = options; + this.run_at = this.options.run_at; + this.js = this.options.js || []; + this.css = this.options.css || []; + + this.matches_ = new MatchPattern(this.options.matches); + this.exclude_matches_ = new MatchPattern(this.options.exclude_matches || null); + + // TODO: Support glob patterns. +} + +Script.prototype = { + matches(window) { + let uri = window.document.documentURIObject; + if (!this.matches_.matches(uri)) { + return false; + } + + if (this.exclude_matches_.matches(uri)) { + return false; + } + + if (!this.options.all_frames && window.top != window) { + return false; + } + + // TODO: match_about_blank. + + return true; + }, + + tryInject(extension, window, sandbox, shouldRun) { + if (!this.matches(window)) { + return; + } + + if (shouldRun("document_start")) { + let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils); + + for (let url of this.css) { + url = extension.baseURI.resolve(url); + runSafeWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET); + } + + if (this.options.cssCode) { + let url = "data:text/css;charset=utf-8," + encodeURIComponent(this.options.cssCode); + runSafeWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET); + } + } + + let scheduled = this.run_at || "document_idle"; + if (shouldRun(scheduled)) { + for (let url of this.js) { + url = extension.baseURI.resolve(url); + Services.scriptloader.loadSubScript(url, sandbox); + } + + if (this.options.jsCode) { + Cu.evalInSandbox(this.options.jsCode, sandbox, "latest"); + } + } + }, +}; + +function getWindowMessageManager(contentWindow) +{ + let ir = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDocShell) + .QueryInterface(Ci.nsIInterfaceRequestor); + try { + return ir.getInterface(Ci.nsIContentFrameMessageManager); + } catch (e) { + // Some windows don't support this interface (hidden window). + return null; + } +} + +// Scope in which extension content script code can run. It uses +// Cu.Sandbox to run the code. There is a separate scope for each +// frame. +function ExtensionContext(extensionId, contentWindow) +{ + this.extension = ExtensionManager.get(extensionId); + this.extensionId = extensionId; + this.contentWindow = contentWindow; + + let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + let outerWindowId = utils.outerWindowID; + let frameId = contentWindow == contentWindow.top ? 0 : outerWindowId; + this.frameId = frameId; + + let mm = getWindowMessageManager(contentWindow); + this.messageManager = mm; + + let prin = [contentWindow]; + if (Services.scriptSecurityManager.isSystemPrincipal(contentWindow.document.nodePrincipal)) { + // Make sure we don't hand out the system principal by accident. + prin = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal); + } + + this.sandbox = Cu.Sandbox(prin, {sandboxPrototype: contentWindow, wantXrays: true}); + + let delegate = { + getSender(context, target, sender) { + // Nothing to do here. + } + }; + + let url = contentWindow.location.href; + let broker = ExtensionContent.getBroker(mm); + this.messenger = new Messenger(this, broker, {id: extensionId, frameId, url}, + {id: extensionId, frameId}, delegate); + + let chromeObj = Cu.createObjectIn(this.sandbox, {defineAs: "chrome"}); + injectAPI(api(this), chromeObj); + + this.onClose = new Set(); +} + +ExtensionContext.prototype = { + get cloneScope() { + return this.sandbox; + }, + + execute(script, shouldRun) { + script.tryInject(this.extension, this.contentWindow, this.sandbox, shouldRun); + }, + + callOnClose(obj) { + this.onClose.add(obj); + }, + + forgetOnClose(obj) { + this.onClose.delete(obj); + }, + + close() { + for (let obj of this.onClose) { + obj.close(); + } + }, +}; + +// Responsible for creating ExtensionContexts and injecting content +// scripts into them when new documents are created. +let DocumentManager = { + extensionCount: 0, + + // WeakMap[window -> Map[extensionId -> ExtensionContext]] + windows: new WeakMap(), + + init() { + Services.obs.addObserver(this, "document-element-inserted", false); + Services.obs.addObserver(this, "dom-window-destroyed", false); + }, + + uninit() { + Services.obs.removeObserver(this, "document-element-inserted"); + Services.obs.removeObserver(this, "dom-window-destroyed"); + }, + + getWindowState(contentWindow) { + let readyState = contentWindow.document.readyState; + if (readyState == "loading") { + return "document_start"; + } else if (readyState == "interactive") { + return "document_end"; + } else { + return "document_idle"; + } + }, + + observe: function(subject, topic, data) { + if (topic == "document-element-inserted") { + let document = subject; + let window = document && document.defaultView; + if (!document || !document.location || !window) { + return; + } + + // Make sure we only load into frames that ExtensionContent.init + // was called on (i.e., not frames for social or sidebars). + let mm = getWindowMessageManager(window); + if (!mm || !ExtensionContent.globals.has(mm)) { + return; + } + + this.windows.delete(window); + + this.trigger("document_start", window); + window.addEventListener("DOMContentLoaded", this, true); + window.addEventListener("load", this, true); + } else if (topic == "dom-window-destroyed") { + let window = subject; + if (!this.windows.has(window)) { + return; + } + + let extensions = this.windows.get(window); + for (let [extensionId, context] of extensions) { + context.close(); + } + + this.windows.delete(window); + } + }, + + handleEvent: function(event) { + let window = event.target.defaultView; + window.removeEventListener(event.type, this, true); + + // Need to check if we're still on the right page? Greasemonkey does this. + + if (event.type == "DOMContentLoaded") { + this.trigger("document_end", window); + } else if (event.type == "load") { + this.trigger("document_idle", window); + } + }, + + executeScript(global, extensionId, script) { + let window = global.content; + let extensions = this.windows.get(window); + if (!extensions) { + return; + } + let context = extensions.get(extensionId); + if (!context) { + return; + } + + // TODO: Somehow make sure we have the right permissions for this origin! + // FIXME: Need to keep this around so that I will execute it later if we're not in the right state. + context.execute(script, scheduled => scheduled == state); + }, + + enumerateWindows: function*(docShell) { + let window = docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow) + yield [window, this.getWindowState(window)]; + + for (let i = 0; i < docShell.childCount; i++) { + let child = docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell); + yield* this.enumerateWindows(child); + } + }, + + getContext(extensionId, window) { + if (!this.windows.has(window)) { + this.windows.set(window, new Map()); + } + let extensions = this.windows.get(window); + if (!extensions.has(extensionId)) { + let context = new ExtensionContext(extensionId, window); + extensions.set(extensionId, context); + } + return extensions.get(extensionId); + }, + + startupExtension(extensionId) { + if (this.extensionCount == 0) { + this.init(); + } + this.extensionCount++; + + let extension = ExtensionManager.get(extensionId); + for (let global of ExtensionContent.globals.keys()) { + for (let [window, state] of this.enumerateWindows(global.docShell)) { + for (let script of extension.scripts) { + if (script.matches(window)) { + let context = this.getContext(extensionId, window); + context.execute(script, scheduled => isWhenBeforeOrSame(scheduled, state)); + } + } + } + } + }, + + shutdownExtension(extensionId) { + for (let global of ExtensionContent.globals.keys()) { + for (let [window, state] of this.enumerateWindows(global.docShell)) { + let extensions = this.windows.get(window); + if (!extensions) { + continue; + } + let context = extensions.get(extensionId); + if (context) { + context.close(); + extensions.delete(extensionId); + } + } + } + + this.extensionCount--; + if (this.extensionCount == 0) { + this.uninit(); + } + }, + + trigger(when, window) { + let state = this.getWindowState(window); + for (let [extensionId, extension] of ExtensionManager.extensions) { + for (let script of extension.scripts) { + if (script.matches(window)) { + let context = this.getContext(extensionId, window); + context.execute(script, scheduled => isWhenBeforeOrSame(scheduled, state)); + } + } + } + }, +}; + +// Represents a browser extension in the content process. +function BrowserExtensionContent(data) +{ + this.id = data.id; + this.uuid = data.uuid; + this.data = data; + this.scripts = [ for (scriptData of data.content_scripts) new Script(scriptData) ]; + this.webAccessibleResources = data.webAccessibleResources; + this.whiteListedHosts = data.whiteListedHosts; + + this.manifest = data.manifest; + this.baseURI = Services.io.newURI(data.baseURL, null, null); + + let uri = Services.io.newURI(data.resourceURL, null, null); + + if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { + // Extension.jsm takes care of this in the parent. + ExtensionManagement.startupExtension(this.uuid, uri, this); + } +}; + +BrowserExtensionContent.prototype = { + shutdown() { + if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { + ExtensionManagement.shutdownExtension(this.uuid); + } + }, +}; + +let ExtensionManager = { + // Map[extensionId, BrowserExtensionContent] + extensions: new Map(), + + init() { + Services.cpmm.addMessageListener("Extension:Startup", this); + Services.cpmm.addMessageListener("Extension:Shutdown", this); + + if (Services.cpmm.initialProcessData && "Extension:Extensions" in Services.cpmm.initialProcessData) { + let extensions = Services.cpmm.initialProcessData["Extension:Extensions"]; + for (let data of extensions) { + this.extensions.set(data.id, new BrowserExtensionContent(data)); + DocumentManager.startupExtension(data.id); + } + } + }, + + get(extensionId) { + return this.extensions.get(extensionId); + }, + + receiveMessage({name, data}) { + let extension; + switch (name) { + case "Extension:Startup": + extension = new BrowserExtensionContent(data); + this.extensions.set(data.id, extension); + DocumentManager.startupExtension(data.id); + break; + + case "Extension:Shutdown": + extension = this.extensions.get(data.id); + extension.shutdown(); + DocumentManager.shutdownExtension(data.id); + this.extensions.delete(data.id); + break; + } + } +}; + +let ExtensionContent = { + globals: new Map(), + + init(global) { + let broker = new MessageBroker([global]); + this.globals.set(global, broker); + + global.addMessageListener("Extension:Execute", this); + + let windowId = global.content + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .outerWindowID; + global.sendAsyncMessage("Extension:TopWindowID", {windowId}); + }, + + uninit(global) { + this.globals.delete(global); + + let windowId = global.content + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .outerWindowID; + global.sendAsyncMessage("Extension:RemoveTopWindowID", {windowId}); + }, + + getBroker(messageManager) { + return this.globals.get(messageManager); + }, + + receiveMessage({target, name, data}) { + switch (name) { + case "Extension:Execute": + data.options.matches = ""; + let script = new Script(data.options); + let {extensionId} = data; + DocumentManager.executeScript(target, extensionId, script); + break; + } + }, +}; + +ExtensionManager.init(); diff --git a/toolkit/components/extensions/ExtensionManagement.jsm b/toolkit/components/extensions/ExtensionManagement.jsm new file mode 100644 index 000000000000..a79735f54400 --- /dev/null +++ b/toolkit/components/extensions/ExtensionManagement.jsm @@ -0,0 +1,201 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["ExtensionManagement"]; + +const Ci = Components.interfaces; +const Cc = Components.classes; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +/* + * This file should be kept short and simple since it's loaded even + * when no extensions are running. + */ + +// Keep track of frame IDs for content windows. Mostly we can just use +// the outer window ID as the frame ID. However, the API specifies +// that top-level windows have a frame ID of 0. So we need to keep +// track of which windows are top-level. This code listens to messages +// from ExtensionContent to do that. +let Frames = { + // Window IDs of top-level content windows. + topWindowIds: new Set(), + + init() { + if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { + return; + } + + Services.mm.addMessageListener("Extension:TopWindowID", this); + Services.mm.addMessageListener("Extension:RemoveTopWindowID", this); + }, + + isTopWindowId(windowId) { + return this.topWindowIds.has(windowId); + }, + + // Convert an outer window ID to a frame ID. An outer window ID of 0 + // is invalid. + getId(windowId) { + if (this.isTopWindowId(windowId)) { + return 0; + } else if (windowId == 0) { + return -1; + } else { + return windowId; + } + }, + + // Convert an outer window ID for a parent window to a frame + // ID. Outer window IDs follow the same convention that + // |window.top.parent === window.top|. The API works differently, + // giving a frame ID of -1 for the the parent of a top-level + // window. This function handles the conversion. + getParentId(parentWindowId, windowId) { + if (parentWindowId == windowId) { + // We have a top-level window. + return -1; + } + + // Not a top-level window. Just return the ID as normal. + return this.getId(parentWindowId); + }, + + receiveMessage({name, data}) { + switch (name) { + case "Extension:TopWindowID": + // FIXME: Need to handle the case where the content process + // crashes. Right now we leak its top window IDs. + this.topWindowIds.add(data.windowId); + break; + + case "Extension:RemoveTopWindowID": + this.topWindowIds.delete(data.windowId); + break; + } + }, +}; +Frames.init(); + +// Manage the collection of ext-*.js scripts that define the extension API. +let Scripts = { + scripts: new Set(), + + register(script) { + this.scripts.add(script); + }, + + getScripts() { + return this.scripts; + }, +}; + +// This object manages various platform-level issues related to +// moz-extension:// URIs. It lives here so that it can be used in both +// the parent and child processes. +// +// moz-extension URIs have the form moz-extension://uuid/path. Each +// extension has its own UUID, unique to the machine it's installed +// on. This is easier and more secure than using the extension ID, +// since it makes it slightly harder to fingerprint for extensions if +// each user uses different URIs for the extension. +let Service = { + initialized: false, + + // Map[uuid -> extension]. + // extension can be an Extension (parent process) or BrowserExtensionContent (child process). + uuidMap: new Map(), + + init() { + let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService); + aps = aps.wrappedJSObject; + this.aps = aps; + aps.setExtensionURILoadCallback(this.extensionURILoadableByAnyone.bind(this)); + aps.setExtensionURIToAddonIdCallback(this.extensionURIToAddonID.bind(this)); + }, + + // Called when a new extension is loaded. + startupExtension(uuid, uri, extension) { + if (!this.initialized) { + this.initialized = true; + this.init(); + } + + // Create the moz-extension://uuid mapping. + let handler = Services.io.getProtocolHandler("moz-extension"); + handler.QueryInterface(Ci.nsISubstitutingProtocolHandler); + handler.setSubstitution(uuid, uri); + + this.uuidMap.set(uuid, extension); + this.aps.setAddonLoadURICallback(extension.id, this.checkAddonMayLoad.bind(this, extension)); + }, + + // Called when an extension is unloaded. + shutdownExtension(uuid) { + let extension = this.uuidMap.get(uuid); + this.uuidMap.delete(uuid); + this.aps.setAddonLoadURICallback(extension.id, null); + + let handler = Services.io.getProtocolHandler("moz-extension"); + handler.QueryInterface(Ci.nsISubstitutingProtocolHandler); + handler.setSubstitution(uuid, null); + }, + + // Return true if the given URI can be loaded from arbitrary web + // content. The manifest.json |web_accessible_resources| directive + // determines this. + extensionURILoadableByAnyone(uri) { + let uuid = uri.host; + let extension = this.uuidMap.get(uuid); + if (!extension) { + return false; + } + + let path = uri.path; + if (path.length > 0 && path[0] == '/') { + path = path.substr(1); + } + return extension.webAccessibleResources.has(path); + }, + + // Checks whether a given extension can load this URI (typically via + // an XML HTTP request). The manifest.json |permissions| directive + // determines this. + checkAddonMayLoad(extension, uri) { + return extension.whiteListedHosts.matchesIgnoringPath(uri); + }, + + // Finds the add-on ID associated with a given moz-extension:// URI. + // This is used to set the addonId on the originAttributes for the + // nsIPrincipal attached to the URI. + extensionURIToAddonID(uri) { + if (this.extensionURILoadableByAnyone(uri)) { + // We don't want webAccessibleResources to be associated with + // the add-on. That way they don't get any special privileges. + return null; + } + + let uuid = uri.host; + let extension = this.uuidMap.get(uuid); + return extension ? extension.id : undefined; + }, +}; + +let ExtensionManagement = { + startupExtension: Service.startupExtension.bind(Service), + shutdownExtension: Service.shutdownExtension.bind(Service), + + registerScript: Scripts.register.bind(Scripts), + getScripts: Scripts.getScripts.bind(Scripts), + + getFrameId: Frames.getId.bind(Frames), + getParentFrameId: Frames.getParentId.bind(Frames), +}; + diff --git a/toolkit/components/extensions/ExtensionStorage.jsm b/toolkit/components/extensions/ExtensionStorage.jsm new file mode 100644 index 000000000000..125e03ea4943 --- /dev/null +++ b/toolkit/components/extensions/ExtensionStorage.jsm @@ -0,0 +1,149 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["ExtensionStorage"]; + +const Ci = Components.interfaces; +const Cc = Components.classes; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/osfile.jsm") +Cu.import("resource://gre/modules/AsyncShutdown.jsm"); + +let Path = OS.Path; +let profileDir = OS.Constants.Path.profileDir; + +let ExtensionStorage = { + cache: new Map(), + listeners: new Map(), + + extensionDir: Path.join(profileDir, "browser-extension-data"), + + getExtensionDir(extensionId) { + return Path.join(this.extensionDir, extensionId); + }, + + getStorageFile(extensionId) { + return Path.join(this.extensionDir, extensionId, "storage.js"); + }, + + read(extensionId) { + if (this.cache.has(extensionId)) { + return this.cache.get(extensionId); + } + + let path = this.getStorageFile(extensionId); + let decoder = new TextDecoder(); + let promise = OS.File.read(path); + promise = promise.then(array => { + return JSON.parse(decoder.decode(array)); + }).catch(() => { + Cu.reportError("Unable to parse JSON data for extension storage."); + return {}; + }); + this.cache.set(extensionId, promise); + return promise; + }, + + write(extensionId) { + let promise = this.read(extensionId).then(extData => { + let encoder = new TextEncoder(); + let array = encoder.encode(JSON.stringify(extData)); + let path = this.getStorageFile(extensionId); + OS.File.makeDir(this.getExtensionDir(extensionId), {ignoreExisting: true, from: profileDir}); + let promise = OS.File.writeAtomic(path, array); + return promise; + }).catch(() => { + // Make sure this promise is never rejected. + Cu.reportError("Unable to write JSON data for extension storage."); + }); + + AsyncShutdown.profileBeforeChange.addBlocker( + "ExtensionStorage: Finish writing extension data", + promise); + + return promise.then(() => { + AsyncShutdown.profileBeforeChange.removeBlocker(promise); + }); + }, + + set(extensionId, items) { + return this.read(extensionId).then(extData => { + let changes = {}; + for (let prop in items) { + changes[prop] = {oldValue: extData[prop], newValue: items[prop]}; + extData[prop] = items[prop]; + } + + let listeners = this.listeners.get(extensionId); + if (listeners) { + for (let listener of listeners) { + listener(changes); + } + } + + return this.write(extensionId); + }); + }, + + remove(extensionId, items) { + return this.read(extensionId).then(extData => { + let changes = {}; + for (let prop in items) { + changes[prop] = {oldValue: extData[prop]}; + delete extData[prop]; + } + + let listeners = this.listeners.get(extensionId); + if (listeners) { + for (let listener of listeners) { + listener(changes); + } + } + + return this.write(extensionId); + }); + }, + + get(extensionId, keys) { + return this.read(extensionId).then(extData => { + let result = {}; + if (keys === null) { + Object.assign(result, extData); + } else if (typeof(keys) == "object") { + for (let prop in keys) { + if (prop in extData) { + result[prop] = extData[prop]; + } else { + result[prop] = keys[prop]; + } + } + } else if (typeof(keys) == "string") { + result[prop] = extData[prop] || undefined; + } else { + for (let prop of keys) { + result[prop] = extData[prop] || undefined; + } + } + + return result; + }); + }, + + addOnChangedListener(extensionId, listener) { + let listeners = this.listeners.get(extensionId) || new Set(); + listeners.add(listener); + this.listeners.set(extensionId, listeners); + }, + + removeOnChangedListener(extensionId, listener) { + let listeners = this.listeners.get(extensionId); + listeners.delete(listener); + }, +}; diff --git a/toolkit/components/extensions/ExtensionUtils.jsm b/toolkit/components/extensions/ExtensionUtils.jsm new file mode 100644 index 000000000000..a92185ab8015 --- /dev/null +++ b/toolkit/components/extensions/ExtensionUtils.jsm @@ -0,0 +1,540 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["ExtensionUtils"]; + +const Ci = Components.interfaces; +const Cc = Components.classes; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +// Run a function and report exceptions. +function runSafeWithoutClone(f, ...args) +{ + try { + return f(...args); + } catch (e) { + dump(`Extension error: ${e} ${e.fileName} ${e.lineNumber}\n${e.stack}\n${Error().stack}`); + Cu.reportError(e); + } +} + +// Run a function, cloning arguments into context.cloneScope, and +// report exceptions. |f| is expected to be in context.cloneScope. +function runSafe(context, f, ...args) +{ + try { + args = Cu.cloneInto(args, context.cloneScope); + } catch (e) { + dump(`runSafe failure\n${context.cloneScope}\n${Error().stack}`); + } + return runSafeWithoutClone(f, ...args); +} + +// Similar to a WeakMap, but returns a particular default value for +// |get| if a key is not present. +function DefaultWeakMap(defaultValue) +{ + this.defaultValue = defaultValue; + this.weakmap = new WeakMap(); +} + +DefaultWeakMap.prototype = { + get(key) { + if (this.weakmap.has(key)) { + return this.weakmap.get(key); + } + return this.defaultValue; + }, + + set(key, value) { + if (key) { + this.weakmap.set(key, value); + } else { + this.defaultValue = value; + } + }, +}; + +// This is a generic class for managing event listeners. Example usage: +// +// new EventManager(context, "api.subAPI", fire => { +// let listener = (...) => { +// // Fire any listeners registered with addListener. +// fire(arg1, arg2); +// }; +// // Register the listener. +// SomehowRegisterListener(listener); +// return () => { +// // Return a way to unregister the listener. +// SomehowUnregisterListener(listener); +// }; +// }).api() +// +// The result is an object with addListener, removeListener, and +// hasListener methods. |context| is an add-on scope (either an +// ExtensionPage in the chrome process or ExtensionContext in a +// content process). |name| is for debugging. |register| is a function +// to register the listener. |register| is only called once, event if +// multiple listeners are registered. |register| should return an +// unregister function that will unregister the listener. +function EventManager(context, name, register) +{ + this.context = context; + this.name = name; + this.register = register; + this.unregister = null; + this.callbacks = new Set(); + this.registered = false; +} + +EventManager.prototype = { + addListener(callback) { + if (!this.registered) { + this.context.callOnClose(this); + + let fireFunc = this.fire.bind(this); + let fireWithoutClone = this.fireWithoutClone.bind(this); + fireFunc.withoutClone = fireWithoutClone; + this.unregister = this.register(fireFunc); + } + this.callbacks.add(callback); + }, + + removeListener(callback) { + if (!this.registered) { + return; + } + + this.callbacks.delete(callback); + if (this.callbacks.length == 0) { + this.unregister(); + + this.context.forgetOnClose(this); + } + }, + + hasListener(callback) { + return this.callbacks.has(callback); + }, + + fire(...args) { + for (let callback of this.callbacks) { + runSafe(this.context, callback, ...args); + } + }, + + fireWithoutClone(...args) { + for (let callback of this.callbacks) { + runSafeWithoutClone(callback, ...args); + } + }, + + close() { + this.unregister(); + }, + + api() { + return { + addListener: callback => this.addListener(callback), + removeListener: callback => this.removeListener(callback), + hasListener: callback => this.hasListener(callback), + }; + }, +}; + +// Similar to EventManager, but it doesn't try to consolidate event +// notifications. Each addListener call causes us to register once. It +// allows extra arguments to be passed to addListener. +function SingletonEventManager(context, name, register) +{ + this.context = context; + this.name = name; + this.register = register; + this.unregister = new Map(); + context.callOnClose(this); +} + +SingletonEventManager.prototype = { + addListener(callback, ...args) { + let unregister = this.register(callback, ...args); + this.unregister.set(callback, unregister); + }, + + removeListener(callback) { + if (!this.unregister.has(callback)) { + return; + } + + let unregister = this.unregister.get(callback); + this.unregister.delete(callback); + this.unregister(); + }, + + hasListener(callback) { + return this.unregister.has(callback); + }, + + close() { + for (let unregister of this.unregister.values()) { + unregister(); + } + }, + + api() { + return { + addListener: (...args) => this.addListener(...args), + removeListener: (...args) => this.removeListener(...args), + hasListener: (...args) => this.hasListener(...args), + }; + }, +}; + +// Simple API for event listeners where events never fire. +function ignoreEvent() +{ + return { + addListener: function(context, callback) {}, + removeListener: function(context, callback) {}, + hasListener: function(context, callback) {}, + }; +} + +// Copy an API object from |source| into the scope |dest|. +function injectAPI(source, dest) +{ + for (let prop in source) { + // Skip names prefixed with '_'. + if (prop[0] == '_') { + continue; + } + + let value = source[prop]; + if (typeof(value) == "function") { + Cu.exportFunction(value, dest, {defineAs: prop}); + } else if (typeof(value) == "object") { + let obj = Cu.createObjectIn(dest, {defineAs: prop}); + injectAPI(value, obj); + } else { + dest[prop] = value; + } + } +} + +/* + * Messaging primitives. + */ + +let nextBrokerId = 1; + +let MESSAGES = [ + "Extension:Message", + "Extension:Connect", +]; + +// Receives messages from multiple message managers and directs them +// to a set of listeners. On the child side: one broker per frame +// script. On the parent side: one broker total, covering both the +// global MM and the ppmm. Message must be tagged with a recipient, +// which is an object with properties. Listeners can filter for +// messages that have a certain value for a particular property in the +// recipient. (If a message doesn't specify the given property, it's +// considered a match.) +function MessageBroker(messageManagers) +{ + this.messageManagers = messageManagers; + for (let mm of this.messageManagers) { + for (let message of MESSAGES) { + mm.addMessageListener(message, this); + } + } + + this.listeners = {message: [], connect: []}; +} + +MessageBroker.prototype = { + uninit() { + for (let mm of this.messageManagers) { + for (let message of MESSAGES) { + mm.removeMessageListener(message, this); + } + } + + this.listeners = null; + }, + + makeId() { + return nextBrokerId++; + }, + + addListener(type, listener, filter) { + this.listeners[type].push({filter, listener}); + }, + + removeListener(type, listener) { + let index = -1; + for (let i = 0; i < this.listeners[type].length; i++) { + if (this.listeners[type][i].listener == listener) { + this.listeners[type].splice(i, 1); + return; + } + } + }, + + runListeners(type, target, data) { + let listeners = []; + for (let {listener, filter} of this.listeners[type]) { + let pass = true; + for (let prop in filter) { + if (prop in data.recipient && filter[prop] != data.recipient[prop]) { + pass = false; + break; + } + } + + // Save up the list of listeners to call in case they modify the + // set of listeners. + if (pass) { + listeners.push(listener); + } + } + + for (let listener of listeners) { + listener(type, target, data.message, data.sender, data.recipient); + } + }, + + receiveMessage({name, data, target}) { + switch (name) { + case "Extension:Message": + this.runListeners("message", target, data); + break; + + case "Extension:Connect": + this.runListeners("connect", target, data); + break; + } + }, + + sendMessage(messageManager, type, message, sender, recipient) { + let data = {message, sender, recipient}; + let names = {message: "Extension:Message", connect: "Extension:Connect"}; + messageManager.sendAsyncMessage(names[type], data); + }, +}; + +// Abstraction for a Port object in the extension API. Each port has a unique ID. +function Port(context, messageManager, name, id, sender) +{ + this.context = context; + this.messageManager = messageManager; + this.name = name; + this.id = id; + this.listenerName = `Extension:Port-${this.id}`; + this.disconnectName = `Extension:Disconnect-${this.id}`; + this.sender = sender; + this.disconnected = false; +} + +Port.prototype = { + api() { + let portObj = Cu.createObjectIn(this.context.cloneScope); + + // We want a close() notification when the window is destroyed. + this.context.callOnClose(this); + + let publicAPI = { + name: this.name, + disconnect: () => { + this.disconnect(); + }, + postMessage: json => { + if (this.disconnected) { + throw "Attempt to postMessage on disconnected port"; + } + this.messageManager.sendAsyncMessage(this.listenerName, json); + }, + onDisconnect: new EventManager(this.context, "Port.onDisconnect", fire => { + let listener = () => { + if (!this.disconnected) { + fire(); + } + }; + + this.messageManager.addMessageListener(this.disconnectName, listener, true); + return () => { + this.messageManager.removeMessageListener(this.disconnectName, listener); + }; + }).api(), + onMessage: new EventManager(this.context, "Port.onMessage", fire => { + let listener = ({data}) => { + if (!this.disconnected) { + fire(data); + } + }; + + this.messageManager.addMessageListener(this.listenerName, listener); + return () => { + this.messageManager.removeMessageListener(this.listenerName, listener); + }; + }).api(), + }; + + if (this.sender) { + publicAPI.sender = this.sender; + } + + injectAPI(publicAPI, portObj); + return portObj; + }, + + disconnect() { + this.context.forgetOnClose(this); + this.disconnect = true; + this.messageManager.sendAsyncMessage(this.disconnectName); + }, + + close() { + this.disconnect(); + }, +}; + +function getMessageManager(target) +{ + if (target instanceof Ci.nsIDOMXULElement) { + return target.messageManager; + } else { + return target; + } +} + +// Each extension scope gets its own Messenger object. It handles the +// basics of sendMessage, onMessage, connect, and onConnect. +// +// |context| is the extension scope. +// |broker| is a MessageBroker used to receive and send messages. +// |sender| is an object describing the sender (usually giving its extensionId, tabId, etc.) +// |filter| is a recipient filter to apply to incoming messages from the broker. +// |delegate| is an object that must implement a few methods: +// getSender(context, messageManagerTarget, sender): returns a MessageSender +// See https://developer.chrome.com/extensions/runtime#type-MessageSender. +function Messenger(context, broker, sender, filter, delegate) +{ + this.context = context; + this.broker = broker; + this.sender = sender; + this.filter = filter; + this.delegate = delegate; +} + +Messenger.prototype = { + sendMessage(messageManager, msg, recipient, responseCallback) { + let id = this.broker.makeId(); + let replyName = `Extension:Reply-${id}`; + recipient.messageId = id; + this.broker.sendMessage(messageManager, "message", msg, this.sender, recipient); + + let onClose; + let listener = ({data: response}) => { + messageManager.removeMessageListener(replyName, listener); + this.context.forgetOnClose(onClose); + + if (response.gotData) { + // TODO: Handle failure to connect to the extension? + runSafe(this.context, responseCallback, response.data); + } + }; + onClose = { + close() { + messageManager.removeMessageListener(replyName, listener); + } + }; + if (responseCallback) { + messageManager.addMessageListener(replyName, listener); + this.context.callOnClose(onClose); + } + }, + + onMessage(name) { + return new EventManager(this.context, name, fire => { + let listener = (type, target, message, sender, recipient) => { + message = Cu.cloneInto(message, this.context.cloneScope); + if (this.delegate) { + this.delegate.getSender(this.context, target, sender); + } + sender = Cu.cloneInto(sender, this.context.cloneScope); + + let mm = getMessageManager(target); + let replyName = `Extension:Reply-${recipient.messageId}`; + + let valid = true, sent = false; + let sendResponse = data => { + if (!valid) { + return; + } + sent = true; + mm.sendAsyncMessage(replyName, {data, gotData: true}); + }; + sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope); + + let result = fire.withoutClone(message, sender, sendResponse); + if (result !== true) { + valid = false; + } + if (!sent) { + mm.sendAsyncMessage(replyName, {gotData: false}); + } + }; + + this.broker.addListener("message", listener, this.filter); + return () => { + this.broker.removeListener("message", listener); + }; + }).api(); + }, + + connect(messageManager, name, recipient) { + let portId = this.broker.makeId(); + let port = new Port(this.context, messageManager, name, portId, null); + let msg = {name, portId}; + this.broker.sendMessage(messageManager, "connect", msg, this.sender, recipient); + return port.api(); + }, + + onConnect(name) { + return new EventManager(this.context, name, fire => { + let listener = (type, target, message, sender, recipient) => { + let {name, portId} = message; + let mm = getMessageManager(target); + if (this.delegate) { + this.delegate.getSender(this.context, target, sender); + } + let port = new Port(this.context, mm, name, portId, sender); + fire.withoutClone(port.api()); + }; + + this.broker.addListener("connect", listener, this.filter); + return () => { + this.broker.removeListener("connect", listener); + }; + }).api(); + }, +}; + +let ExtensionUtils = { + runSafe, + DefaultWeakMap, + EventManager, + SingletonEventManager, + ignoreEvent, + injectAPI, + MessageBroker, + Messenger, +}; + diff --git a/toolkit/components/extensions/ext-alarms.js b/toolkit/components/extensions/ext-alarms.js new file mode 100644 index 000000000000..b534c674638c --- /dev/null +++ b/toolkit/components/extensions/ext-alarms.js @@ -0,0 +1,168 @@ +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + EventManager, + ignoreEvent, +} = ExtensionUtils; + +// WeakMap[Extension -> Set[Alarm]] +let alarmsMap = new WeakMap(); + +// WeakMap[Extension -> callback] +let alarmCallbacksMap = new WeakMap(); + +// Manages an alarm created by the extension (alarms API). +function Alarm(extension, name, alarmInfo) +{ + this.extension = extension; + this.name = name; + this.when = alarmInfo.when; + this.delayInMinutes = alarmInfo.delayInMinutes; + this.periodInMinutes = alarmInfo.periodInMinutes; + this.canceled = false; + + let delay, scheduledTime; + if (this.when) { + scheduledTime = this.when; + delay = this.when - Date.now(); + } else { + delay = this.delayInMinutes * 60 * 1000; + scheduledTime = Date.now() + delay; + } + + this.scheduledTime = scheduledTime; + + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT); + this.timer = timer; +} + +Alarm.prototype = { + clear() { + this.timer.cancel(); + alarmsMap.get(this.extension).delete(this); + this.canceled = true; + }, + + observe(subject, topic, data) { + if (alarmCallbacksMap.has(this.extension)) { + alarmCallbacksMap.get(this.extension)(this); + } + if (this.canceled) { + return; + } + + if (!this.periodInMinutes) { + this.clear(); + return; + } + + let delay = this.periodInMinutes * 60 * 1000; + this.scheduledTime = Date.now() + delay; + this.timer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT); + }, + + get data() { + return { + name: this.name, + scheduledTime: this.scheduledTime, + periodInMinutes: this.periodInMinutes, + }; + }, +}; + +extensions.on("startup", (type, extension) => { + alarmsMap.set(extension, new Set()); +}); + +extensions.on("shutdown", (type, extension) => { + for (let alarm of alarmsMap.get(extension)) { + alarm.clear(); + } + alarmsMap.delete(extension); +}); + +extensions.registerAPI((extension, context) => { + return { + alarms: { + create: function(...args) { + let name = "", alarmInfo; + if (args.length == 1) { + alarmInfo = args[0]; + } else { + [name, alarmInfo] = args; + } + + let alarm = new Alarm(extension, name, alarmInfo); + alarmsMap.get(extension).add(alarm); + }, + + get: function(args) { + let name = "", callback; + if (args.length == 1) { + callback = args[0]; + } else { + [name, callback] = args; + } + + for (let alarm of alarmsMap.get(extension)) { + if (alarm.name == name) { + runSafe(context, callback, alarm.data); + break; + } + } + }, + + getAll: function(callback) { + let alarms = alarmsMap.get(extension); + result = [ for (alarm of alarms) alarm.data ]; + runSafe(context, callback, result); + }, + + clear: function(...args) { + let name = "", callback; + if (args.length == 1) { + callback = args[0]; + } else { + [name, callback] = args; + } + + let alarms = alarmsMap.get(extension); + let cleared = false; + for (let alarm of alarms) { + if (alarm.name == name) { + alarm.clear(); + cleared = true; + break; + } + } + + if (callback) { + runSafe(context, callback, cleared); + } + }, + + clearAll: function(callback) { + let alarms = alarmsMap.get(extension); + let cleared = false; + for (let alarm of alarms) { + alarm.clear(); + cleared = true; + } + if (callback) { + runSafe(context, callback, cleared); + } + }, + + onAlarm: new EventManager(context, "alarms.onAlarm", fire => { + let callback = alarm => { + fire(alarm.data); + }; + + alarmCallbacksMap.set(extension, callback); + return () => { + alarmCallbacksMap.delete(extension); + }; + }).api(), + }, + }; +}); diff --git a/toolkit/components/extensions/ext-backgroundPage.js b/toolkit/components/extensions/ext-backgroundPage.js new file mode 100644 index 000000000000..4cdf28054205 --- /dev/null +++ b/toolkit/components/extensions/ext-backgroundPage.js @@ -0,0 +1,92 @@ +// WeakMap[Extension -> BackgroundPage] +let backgroundPagesMap = new WeakMap(); + +// Responsible for the background_page section of the manifest. +function BackgroundPage(options, extension) +{ + this.extension = extension; + this.scripts = options.scripts || []; + this.page = options.page || null; + this.contentWindow = null; + this.webNav = null; + this.context = null; +} + +BackgroundPage.prototype = { + build() { + let webNav = Services.appShell.createWindowlessBrowser(false); + this.webNav = webNav; + + let principal = Services.scriptSecurityManager.createCodebasePrincipal(this.extension.baseURI, + {addonId: this.extension.id}); + + let interfaceRequestor = webNav.QueryInterface(Ci.nsIInterfaceRequestor); + let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell); + + this.context = new ExtensionPage(this.extension, {type: "background", docShell}); + GlobalManager.injectInDocShell(docShell, this.extension, this.context); + + docShell.createAboutBlankContentViewer(principal); + + let window = webNav.document.defaultView; + this.contentWindow = window; + this.context.contentWindow = window; + + let url; + if (this.page) { + url = this.extension.baseURI.resolve(this.page); + } else { + url = this.extension.baseURI.resolve("_blank.html"); + } + webNav.loadURI(url, 0, null, null, null); + + // TODO: Right now we run onStartup after the background page + // finishes. See if this is what Chrome does. + window.windowRoot.addEventListener("load", () => { + if (this.scripts) { + let doc = window.document; + for (let script of this.scripts) { + let url = this.extension.baseURI.resolve(script); + let tag = doc.createElement("script"); + tag.setAttribute("src", url); + tag.async = false; + doc.body.appendChild(tag); + } + } + + if (this.extension.onStartup) { + this.extension.onStartup(); + } + }, true); + }, + + shutdown() { + // Navigate away from the background page to invalidate any + // setTimeouts or other callbacks. + this.webNav.loadURI("about:blank", 0, null, null, null); + this.webNav = null; + }, +}; + +extensions.on("manifest_background", (type, directive, extension, manifest) => { + let bgPage = new BackgroundPage(manifest.background, extension); + bgPage.build(); + backgroundPagesMap.set(extension, bgPage); +}); + +extensions.on("shutdown", (type, extension) => { + if (backgroundPagesMap.has(extension)) { + backgroundPagesMap.get(extension).shutdown(); + backgroundPagesMap.delete(extension); + } +}); + +extensions.registerAPI((extension, context) => { + return { + extension: { + getBackgroundPage: function() { + return backgroundPagesMap.get(extension).contentWindow; + }, + }, + }; +}); diff --git a/toolkit/components/extensions/ext-extension.js b/toolkit/components/extensions/ext-extension.js new file mode 100644 index 000000000000..deaebebc4fed --- /dev/null +++ b/toolkit/components/extensions/ext-extension.js @@ -0,0 +1,10 @@ +extensions.registerAPI((extension, context) => { + return { + extension: { + getURL: function(url) { + return extension.baseURI.resolve(url); + }, + }, + }; +}); + diff --git a/toolkit/components/extensions/ext-i18n.js b/toolkit/components/extensions/ext-i18n.js new file mode 100644 index 000000000000..769331e8ad7e --- /dev/null +++ b/toolkit/components/extensions/ext-i18n.js @@ -0,0 +1,9 @@ +extensions.registerAPI((extension, context) => { + return { + i18n: { + getMessage: function(messageName, substitutions) { + return extension.localizeMessage(messageName, substitutions); + }, + }, + }; +}); diff --git a/toolkit/components/extensions/ext-idle.js b/toolkit/components/extensions/ext-idle.js new file mode 100644 index 000000000000..96bc3ead80bd --- /dev/null +++ b/toolkit/components/extensions/ext-idle.js @@ -0,0 +1,9 @@ +extensions.registerPrivilegedAPI("idle", (extension, context) => { + return { + idle: { + queryState: function(detectionIntervalInSeconds, callback) { + runSafe(context, callback, "active"); + }, + }, + }; +}); diff --git a/toolkit/components/extensions/ext-notifications.js b/toolkit/components/extensions/ext-notifications.js new file mode 100644 index 000000000000..0138154408cf --- /dev/null +++ b/toolkit/components/extensions/ext-notifications.js @@ -0,0 +1,140 @@ +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + EventManager, + ignoreEvent, +} = ExtensionUtils; + +// WeakMap[Extension -> Set[Notification]] +let notificationsMap = new WeakMap(); + +// WeakMap[Extension -> callback] +let notificationCallbacksMap = new WeakMap(); + +// Manages a notification popup (notifications API) created by the extension. +function Notification(extension, id, options) +{ + this.extension = extension; + this.id = id; + this.options = options; + + let imageURL; + if (options.iconUrl) { + imageURL = this.extension.baseURI.resolve(options.iconUrl); + } + + try { + let svc = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); + svc.showAlertNotification(imageURL, + options.title, + options.message, + false, // textClickable + this.id, + this, + this.id); + } catch (e) { + // This will fail if alerts aren't available on the system. + } +} + +Notification.prototype = { + clear() { + try { + let svc = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); + svc.closeAlert(this.id); + } catch (e) { + // This will fail if the OS doesn't support this function. + } + notificationsMap.get(this.extension).delete(this); + }, + + observe(subject, topic, data) { + if (topic != "alertfinished") { + return; + } + + if (notificationCallbacksMap.has(this.extension)) { + notificationCallbackMap.get(this.extension)(this); + } + + notificationsMap.get(this.extension).delete(this); + }, +}; + +extensions.on("startup", (type, extension) => { + notificationsMap.set(extension, new Set()); +}); + +extensions.on("shutdown", (type, extension) => { + for (let notification of notificationsMap.get(extension)) { + notification.clear(); + } + notificationsMap.delete(extension); +}); + +let nextId = 0; + +extensions.registerPrivilegedAPI("notifications", (extension, context) => { + return { + notifications: { + create: function(...args) { + let notificationId, options, callback; + if (args.length == 1) { + options = args[0]; + } else { + [notificationId, options, callback] = args; + } + + if (!notificationId) { + notificationId = nextId++; + } + + // FIXME: Lots of options still aren't supported, especially + // buttons. + let notification = new Notification(extension, notificationId, options); + notificationsMap.get(extension).add(notification); + + if (callback) { + runSafe(context, callback, notificationId); + } + }, + + clear: function(notificationId, callback) { + let notifications = notificationsMap.get(extension); + let cleared = false; + for (let notification of notifications) { + if (notification.id == notificationId) { + notification.clear(); + cleared = true; + break; + } + } + + if (callback) { + runSafe(context, callback, cleared); + } + }, + + getAll: function(callback) { + let notifications = notificationsMap.get(extension); + notifications = [ for (notification of notifications) notification.id ]; + runSafe(context, callback, notifications); + }, + + onClosed: new EventManager(context, "notifications.onClosed", fire => { + let listener = notification => { + // FIXME: Support the byUser argument. + fire(notification.id, true); + }; + + notificationCallbackMap.set(extension, listener); + return () => { + notificationCallbackMap.delete(extension); + }; + }).api(), + + // FIXME + onButtonClicked: ignoreEvent(), + onClicked: ignoreEvent(), + }, + }; +}); diff --git a/toolkit/components/extensions/ext-runtime.js b/toolkit/components/extensions/ext-runtime.js new file mode 100644 index 000000000000..e9c633b26c00 --- /dev/null +++ b/toolkit/components/extensions/ext-runtime.js @@ -0,0 +1,47 @@ +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + EventManager, + ignoreEvent, +} = ExtensionUtils; + +extensions.registerAPI((extension, context) => { + return { + runtime: { + onStartup: new EventManager(context, "runtime.onStartup", fire => { + extension.onStartup = fire; + return () => { + extension.onStartup = null; + }; + }).api(), + + onInstalled: ignoreEvent(), + + onMessage: context.messenger.onMessage("runtime.onMessage"), + + onConnect: context.messenger.onConnect("runtime.onConnect"), + + sendMessage: function(...args) { + let extensionId, message, options, responseCallback; + if (args.length == 1) { + message = args[0]; + } else if (args.length == 2) { + [message, responseCallback] = args; + } else { + [extensionId, message, options, responseCallback] = args; + } + let recipient = {extensionId: extensionId ? extensionId : extension.id}; + return context.messenger.sendMessage(Services.cpmm, message, recipient, responseCallback); + }, + + getManifest() { + return Cu.cloneInto(extension.manifest, context.cloneScope); + }, + + id: extension.id, + + getURL: function(url) { + return extension.baseURI.resolve(url); + }, + }, + }; +}); diff --git a/toolkit/components/extensions/ext-storage.js b/toolkit/components/extensions/ext-storage.js new file mode 100644 index 000000000000..c6c1530460ad --- /dev/null +++ b/toolkit/components/extensions/ext-storage.js @@ -0,0 +1,48 @@ +XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage", + "resource://gre/modules/ExtensionStorage.jsm"); + +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + EventManager, + ignoreEvent, + runSafe, +} = ExtensionUtils; + +extensions.registerPrivilegedAPI("storage", (extension, context) => { + return { + storage: { + local: { + get: function(keys, callback) { + ExtensionStorage.get(extension.id, keys).then(result => { + runSafe(context, callback, result); + }); + }, + set: function(items, callback) { + ExtensionStorage.set(extension.id, items).then(() => { + if (callback) { + runSafe(context, callback); + } + }); + }, + remove: function(items, callback) { + ExtensionStorage.remove(extension.id, items).then(() => { + if (callback) { + runSafe(context, callback); + } + }); + }, + }, + + onChanged: new EventManager(context, "storage.local.onChanged", fire => { + let listener = changes => { + fire(changes, "local"); + }; + + ExtensionStorage.addOnChangedListener(extension.id, listener); + return () => { + ExtensionStorage.removeOnChangedListener(extension.id, listener); + }; + }).api(), + }, + }; +}); diff --git a/toolkit/components/extensions/ext-webNavigation.js b/toolkit/components/extensions/ext-webNavigation.js new file mode 100644 index 000000000000..32d63f1c8be9 --- /dev/null +++ b/toolkit/components/extensions/ext-webNavigation.js @@ -0,0 +1,70 @@ +XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement", + "resource://gre/modules/ExtensionManagement.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern", + "resource://gre/modules/MatchPattern.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "WebNavigation", + "resource://gre/modules/WebNavigation.jsm"); + +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + SingletonEventManager, + ignoreEvent, + runSafe, +} = ExtensionUtils; + +// Similar to WebRequestEventManager but for WebNavigation. +function WebNavigationEventManager(context, eventName) +{ + let name = `webNavigation.${eventName}`; + let register = callback => { + let listener = data => { + if (!data.browser) { + return; + } + + let tabId = TabManager.getBrowserId(data.browser); + if (tabId == -1) { + return; + } + + let data2 = { + url: data.url, + timeStamp: Date.now(), + frameId: ExtensionManagement.getFrameId(data.windowId), + parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId), + }; + + // Fills in tabId typically. + let result = {}; + extensions.emit("fill-browser-data", data.browser, data2, result); + if (result.cancel) { + return; + } + + return runSafe(context, callback, data2); + }; + + WebNavigation[eventName].addListener(listener); + return () => { + WebNavigation[eventName].removeListener(listener); + }; + }; + + return SingletonEventManager.call(this, context, name, register); +} + +WebNavigationEventManager.prototype = Object.create(SingletonEventManager.prototype); + +extensions.registerPrivilegedAPI("webNavigation", (extension, context) => { + return { + webNavigation: { + onBeforeNavigate: new WebNavigationEventManager(context, "onBeforeNavigate").api(), + onCommitted: new WebNavigationEventManager(context, "onCommitted").api(), + onDOMContentLoaded: new WebNavigationEventManager(context, "onDOMContentLoaded").api(), + onCompleted: new WebNavigationEventManager(context, "onCompleted").api(), + onErrorOccurred: new WebNavigationEventManager(context, "onErrorOccurred").api(), + onReferenceFragmentUpdated: new WebNavigationEventManager(context, "onReferenceFragmentUpdated").api(), + onCreatedNavigationTarget: ignoreEvent(), + }, + }; +}); diff --git a/toolkit/components/extensions/ext-webRequest.js b/toolkit/components/extensions/ext-webRequest.js new file mode 100644 index 000000000000..b6169c381c92 --- /dev/null +++ b/toolkit/components/extensions/ext-webRequest.js @@ -0,0 +1,104 @@ +XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern", + "resource://gre/modules/MatchPattern.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "WebRequest", + "resource://gre/modules/WebRequest.jsm"); + +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); +let { + SingletonEventManager, + runSafe, +} = ExtensionUtils; + +// EventManager-like class specifically for WebRequest. Inherits from +// SingletonEventManager. Takes care of converting |details| parameter +// when invoking listeners. +function WebRequestEventManager(context, eventName) +{ + let name = `webRequest.${eventName}`; + let register = (callback, filter, info) => { + let listener = data => { + if (!data.browser) { + return; + } + + let tabId = TabManager.getBrowserId(data.browser); + if (tabId == -1) { + return; + } + + let data2 = { + url: data.url, + method: data.method, + type: data.type, + timeStamp: Date.now(), + frameId: ExtensionManagement.getFrameId(data.windowId), + parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId), + }; + + // Fills in tabId typically. + let result = {}; + extensions.emit("fill-browser-data", data.browser, data2, result); + if (result.cancel) { + return; + } + + let optional = ["requestHeaders", "responseHeaders", "statusCode"]; + for (let opt of optional) { + if (opt in data) { + data2[opt] = data[opt]; + } + } + + return runSafe(context, callback, data2); + }; + + let filter2 = {}; + filter2.urls = new MatchPattern(filter.urls); + if (filter.types) { + filter2.types = filter.types; + } + if (filter.tabId) { + filter2.tabId = filter.tabId; + } + if (filter.windowId) { + filter2.windowId = filter.windowId; + } + + let info2 = []; + if (info) { + for (let desc of info) { + if (desc == "blocking" && !context.extension.hasPermission("webRequestBlocking")) { + Cu.reportError("Using webRequest.addListener with the blocking option " + + "requires the 'webRequestBlocking' permission."); + } else { + info2.push(desc); + } + } + } + + WebRequest[eventName].addListener(listener, filter2, info2); + return () => { + WebRequest[eventName].removeListener(listener); + }; + }; + + return SingletonEventManager.call(this, context, name, register); +} + +WebRequestEventManager.prototype = Object.create(SingletonEventManager.prototype); + +extensions.registerPrivilegedAPI("webRequest", (extension, context) => { + return { + webRequest: { + onBeforeRequest: new WebRequestEventManager(context, "onBeforeRequest").api(), + onBeforeSendHeaders: new WebRequestEventManager(context, "onBeforeSendHeaders").api(), + onSendHeaders: new WebRequestEventManager(context, "onSendHeaders").api(), + onHeadersReceived: new WebRequestEventManager(context, "onHeadersReceived").api(), + onResponseStarted: new WebRequestEventManager(context, "onResponseStarted").api(), + onCompleted: new WebRequestEventManager(context, "onCompleted").api(), + handlerBehaviorChanged: function() { + // TODO: Flush all caches. + }, + }, + }; +}); diff --git a/toolkit/components/extensions/jar.mn b/toolkit/components/extensions/jar.mn new file mode 100644 index 000000000000..8f48a5653733 --- /dev/null +++ b/toolkit/components/extensions/jar.mn @@ -0,0 +1,16 @@ +# 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/. + +toolkit.jar: +% content extensions %content/extensions/ + content/extensions/ext-alarms.js (ext-alarms.js) + content/extensions/ext-backgroundPage.js (ext-backgroundPage.js) + content/extensions/ext-notifications.js (ext-notifications.js) + content/extensions/ext-i18n.js (ext-i18n.js) + content/extensions/ext-idle.js (ext-idle.js) + content/extensions/ext-webRequest.js (ext-webRequest.js) + content/extensions/ext-webNavigation.js (ext-webNavigation.js) + content/extensions/ext-runtime.js (ext-runtime.js) + content/extensions/ext-extension.js (ext-extension.js) + content/extensions/ext-storage.js (ext-storage.js) diff --git a/toolkit/components/extensions/moz.build b/toolkit/components/extensions/moz.build new file mode 100644 index 000000000000..d6f48aa67686 --- /dev/null +++ b/toolkit/components/extensions/moz.build @@ -0,0 +1,15 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXTRA_JS_MODULES += [ + 'Extension.jsm', + 'ExtensionContent.jsm', + 'ExtensionManagement.jsm', + 'ExtensionStorage.jsm', + 'ExtensionUtils.jsm', +] + +JAR_MANIFESTS += ['jar.mn'] diff --git a/toolkit/components/moz.build b/toolkit/components/moz.build index 493e396881ff..479c90c58372 100644 --- a/toolkit/components/moz.build +++ b/toolkit/components/moz.build @@ -22,6 +22,7 @@ DIRS += [ 'crashmonitor', 'diskspacewatcher', 'downloads', + 'extensions', 'exthelper', 'filepicker', 'filewatcher', diff --git a/toolkit/components/utils/simpleServices.js b/toolkit/components/utils/simpleServices.js index 69b727d9eced..608c62475803 100644 --- a/toolkit/components/utils/simpleServices.js +++ b/toolkit/components/utils/simpleServices.js @@ -104,7 +104,11 @@ AddonPolicyService.prototype = { * directly. */ setAddonLoadURICallback(aAddonId, aCallback) { - this.mayLoadURICallbacks[aAddonId] = aCallback; + if (aCallback) { + this.mayLoadURICallbacks[aAddonId] = aCallback; + } else { + delete this.mayLoadURICallbacks[aAddonId]; + } }, /* diff --git a/toolkit/modules/Locale.jsm b/toolkit/modules/Locale.jsm new file mode 100644 index 000000000000..3b76a0071da0 --- /dev/null +++ b/toolkit/modules/Locale.jsm @@ -0,0 +1,93 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +this.EXPORTED_SYMBOLS = ["Locale"]; + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Preferences.jsm"); + +const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; +const PREF_SELECTED_LOCALE = "general.useragent.locale"; + +let Locale = { + /** + * Gets the currently selected locale for display. + * @return the selected locale or "en-US" if none is selected + */ + getLocale() { + if (Preferences.get(PREF_MATCH_OS_LOCALE, false)) + return Services.locale.getLocaleComponentForUserAgent(); + try { + let locale = Preferences.get(PREF_SELECTED_LOCALE, null, Ci.nsIPrefLocalizedString); + if (locale) + return locale; + } + catch (e) {} + return Preferences.get(PREF_SELECTED_LOCALE, "en-US"); + }, + + /** + * Selects the closest matching locale from a list of locales. + * + * @param aLocales + * An array of locales + * @return the best match for the currently selected locale + */ + findClosestLocale(aLocales) { + let appLocale = this.getLocale(); + + // Holds the best matching localized resource + var bestmatch = null; + // The number of locale parts it matched with + var bestmatchcount = 0; + // The number of locale parts in the match + var bestpartcount = 0; + + var matchLocales = [appLocale.toLowerCase()]; + /* If the current locale is English then it will find a match if there is + a valid match for en-US so no point searching that locale too. */ + if (matchLocales[0].substring(0, 3) != "en-") + matchLocales.push("en-us"); + + for (let locale of matchLocales) { + var lparts = locale.split("-"); + for (let localized of aLocales) { + for (let found of localized.locales) { + found = found.toLowerCase(); + // Exact match is returned immediately + if (locale == found) + return localized; + + var fparts = found.split("-"); + /* If we have found a possible match and this one isn't any longer + then we dont need to check further. */ + if (bestmatch && fparts.length < bestmatchcount) + continue; + + // Count the number of parts that match + var maxmatchcount = Math.min(fparts.length, lparts.length); + var matchcount = 0; + while (matchcount < maxmatchcount && + fparts[matchcount] == lparts[matchcount]) + matchcount++; + + /* If we matched more than the last best match or matched the same and + this locale is less specific than the last best match. */ + if (matchcount > bestmatchcount || + (matchcount == bestmatchcount && fparts.length < bestpartcount)) { + bestmatch = localized; + bestmatchcount = matchcount; + bestpartcount = fparts.length; + } + } + } + // If we found a valid match for this locale return it + if (bestmatch) + return bestmatch; + } + return null; + }, +}; diff --git a/toolkit/modules/addons/MatchPattern.jsm b/toolkit/modules/addons/MatchPattern.jsm index 91d4bf6b30ca..86f089f66f00 100644 --- a/toolkit/modules/addons/MatchPattern.jsm +++ b/toolkit/modules/addons/MatchPattern.jsm @@ -63,7 +63,7 @@ function SingleMatchPattern(pat) } SingleMatchPattern.prototype = { - matches(uri) { + matches(uri, ignorePath = false) { if (this.scheme.indexOf(uri.scheme) == -1) { return false; } @@ -83,7 +83,7 @@ SingleMatchPattern.prototype = { } } - if (!this.path.test(uri.path)) { + if (!ignorePath && !this.path.test(uri.path)) { return false; } @@ -114,6 +114,15 @@ MatchPattern.prototype = { return false; }, + matchesIgnoringPath(uri) { + for (let matcher of this.matchers) { + if (matcher.matches(uri, true)) { + return true; + } + } + return false; + }, + serialize() { return this.pat; }, diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index 2a0cfb3f3f5b..2d8e15397992 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -36,6 +36,7 @@ EXTRA_JS_MODULES += [ 'InlineSpellChecker.jsm', 'InlineSpellCheckerContent.jsm', 'LoadContextInfo.jsm', + 'Locale.jsm', 'Log.jsm', 'NewTabUtils.jsm', 'ObjectUtils.jsm', diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index d363960abcac..cb59cee344bb 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -22,6 +22,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ChromeManifestParser", "resource://gre/modules/ChromeManifestParser.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Locale", + "resource://gre/modules/Locale.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ZipUtils", @@ -67,8 +69,6 @@ const PREF_INSTALL_CACHE = "extensions.installCache"; const PREF_XPI_STATE = "extensions.xpiState"; const PREF_BOOTSTRAP_ADDONS = "extensions.bootstrappedAddons"; const PREF_PENDING_OPERATIONS = "extensions.pendingOperations"; -const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; -const PREF_SELECTED_LOCALE = "general.useragent.locale"; const PREF_EM_DSS_ENABLED = "extensions.dss.enabled"; const PREF_DSS_SWITCHPENDING = "extensions.dss.switchPending"; const PREF_DSS_SKIN_TO_SELECT = "extensions.lastSelectedSkin"; @@ -514,84 +514,6 @@ SafeInstallOperation.prototype = { } }; -/** - * Gets the currently selected locale for display. - * @return the selected locale or "en-US" if none is selected - */ -function getLocale() { - if (Preferences.get(PREF_MATCH_OS_LOCALE, false)) - return Services.locale.getLocaleComponentForUserAgent(); - try { - let locale = Preferences.get(PREF_SELECTED_LOCALE, null, Ci.nsIPrefLocalizedString); - if (locale) - return locale; - } - catch (e) {} - return Preferences.get(PREF_SELECTED_LOCALE, "en-US"); -} - -/** - * Selects the closest matching locale from a list of locales. - * - * @param aLocales - * An array of locales - * @return the best match for the currently selected locale - */ -function findClosestLocale(aLocales) { - let appLocale = getLocale(); - - // Holds the best matching localized resource - var bestmatch = null; - // The number of locale parts it matched with - var bestmatchcount = 0; - // The number of locale parts in the match - var bestpartcount = 0; - - var matchLocales = [appLocale.toLowerCase()]; - /* If the current locale is English then it will find a match if there is - a valid match for en-US so no point searching that locale too. */ - if (matchLocales[0].substring(0, 3) != "en-") - matchLocales.push("en-us"); - - for each (var locale in matchLocales) { - var lparts = locale.split("-"); - for each (var localized in aLocales) { - for each (let found in localized.locales) { - found = found.toLowerCase(); - // Exact match is returned immediately - if (locale == found) - return localized; - - var fparts = found.split("-"); - /* If we have found a possible match and this one isn't any longer - then we dont need to check further. */ - if (bestmatch && fparts.length < bestmatchcount) - continue; - - // Count the number of parts that match - var maxmatchcount = Math.min(fparts.length, lparts.length); - var matchcount = 0; - while (matchcount < maxmatchcount && - fparts[matchcount] == lparts[matchcount]) - matchcount++; - - /* If we matched more than the last best match or matched the same and - this locale is less specific than the last best match. */ - if (matchcount > bestmatchcount || - (matchcount == bestmatchcount && fparts.length < bestpartcount)) { - bestmatch = localized; - bestmatchcount = matchcount; - bestpartcount = fparts.length; - } - } - } - // If we found a valid match for this locale return it - if (bestmatch) - return bestmatch; - } - return null; -} - /** * Sets the userDisabled and softDisabled properties of an add-on based on what * values those properties had for a previous instance of the add-on. The @@ -6493,7 +6415,7 @@ AddonInternal.prototype = { get selectedLocale() { if (this._selectedLocale) return this._selectedLocale; - let locale = findClosestLocale(this.locales); + let locale = Locale.findClosestLocale(this.locales); this._selectedLocale = locale ? locale : this.defaultLocale; return this._selectedLocale; }, From b881db962e5f8cf23622cf877c6055baaa8c0fda Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 31 Jul 2015 19:02:51 -0700 Subject: [PATCH 089/113] Bug 1167215 - Composite on every vsync until the scheduled one. r=roc --- gfx/layers/Compositor.h | 26 +++++++++++++++----------- gfx/layers/composite/ImageHost.cpp | 2 +- gfx/layers/ipc/CompositorParent.cpp | 5 +++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h index f8e1e3e59d27..157004d72977 100644 --- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -496,17 +496,20 @@ public: } void SetCompositionTime(TimeStamp aTimeStamp) { mCompositionTime = aTimeStamp; - mCompositeAgainTime = TimeStamp(); - } - - void CompositeAgainAt(TimeStamp aTimeStamp) { - if (mCompositeAgainTime.IsNull() || - mCompositeAgainTime > aTimeStamp) { - mCompositeAgainTime = aTimeStamp; + if (!mCompositionTime.IsNull() && !mCompositeUntilTime.IsNull() && + mCompositionTime >= mCompositeUntilTime) { + mCompositeUntilTime = TimeStamp(); } } - TimeStamp GetCompositeAgainTime() const { - return mCompositeAgainTime; + + void CompositeUntil(TimeStamp aTimeStamp) { + if (mCompositeUntilTime.IsNull() || + mCompositeUntilTime < aTimeStamp) { + mCompositeUntilTime = aTimeStamp; + } + } + TimeStamp GetCompositeUntilTime() const { + return mCompositeUntilTime; } protected: @@ -529,9 +532,10 @@ protected: TimeStamp mCompositionTime; /** * When nonnull, during rendering, some compositable indicated that it will - * change its rendering at this time (and this is the earliest such time). + * change its rendering at this time. In order not to miss it, we composite + * on every vsync until this time occurs (this is the latest such time). */ - TimeStamp mCompositeAgainTime; + TimeStamp mCompositeUntilTime; uint32_t mCompositorID; DiagnosticTypes mDiagnosticTypes; diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index 2979bc5858cf..909ba884165f 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -257,7 +257,7 @@ ImageHost::Composite(LayerComposite* aLayer, } if (uint32_t(imageIndex) + 1 < mImages.Length()) { - GetCompositor()->CompositeAgainAt(mImages[imageIndex + 1].mTimeStamp); + GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp); } TimedImage* img = &mImages[imageIndex]; diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index f736c4d48318..66d70f0d6c82 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -1180,8 +1180,9 @@ CompositorParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRe // We're not really taking advantage of the stored composite-again-time here. // We might be able to skip the next few composites altogether. However, // that's a bit complex to implement and we'll get most of the advantage - // by skipping compositing when we detect there's nothing invalid. - if (!mCompositor->GetCompositeAgainTime().IsNull() || + // by skipping compositing when we detect there's nothing invalid. This is why + // we do "composite until" rather than "composite again at". + if (!mCompositor->GetCompositeUntilTime().IsNull() || mLayerManager->DebugOverlayWantsNextFrame()) { ScheduleComposition(); } From b3cb004ac946caa0a6b0ee749df1e944a2b1edd5 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Sat, 1 Aug 2015 15:37:11 -0700 Subject: [PATCH 090/113] Bug 1167215 - Re-apply CompositeUntil calls when we get a new batch of textures. r=roc --- gfx/layers/composite/ImageHost.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index 909ba884165f..d38fdf2d54cd 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -19,6 +19,8 @@ class nsIntRegion; +#define BIAS_TIME_MS 1.0 + namespace mozilla { namespace gfx { class Matrix4x4; @@ -102,6 +104,22 @@ ImageHost::UseTextureHost(const nsTArray& aTextures) img.mFrontBuffer->PrepareTextureSource(img.mTextureSource); } mImages.SwapElements(newImages); + + // Video producers generally send replacement images with the same frameID but + // slightly different timestamps in order to sync with the audio clock. This + // means that any CompositeUntil() call we made in Composite() may no longer + // guarantee that we'll composite until the next frame is ready. Fix that here. + if (GetCompositor() && mLastFrameID >= 0) { + for (size_t i = 0; i < mImages.Length(); ++i) { + bool frameComesAfter = mImages[i].mFrameID > mLastFrameID || + mImages[i].mProducerID != mLastProducerID; + if (frameComesAfter && !mImages[i].mTimeStamp.IsNull()) { + GetCompositor()->CompositeUntil(mImages[i].mTimeStamp + + TimeDuration::FromMilliseconds(BIAS_TIME_MS)); + break; + } + } + } } void @@ -124,9 +142,9 @@ GetBiasedTime(const TimeStamp& aInput, ImageHost::Bias aBias) { switch (aBias) { case ImageHost::BIAS_NEGATIVE: - return aInput - TimeDuration::FromMilliseconds(1.0); + return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS); case ImageHost::BIAS_POSITIVE: - return aInput + TimeDuration::FromMilliseconds(1.0); + return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS); default: return aInput; } @@ -257,7 +275,7 @@ ImageHost::Composite(LayerComposite* aLayer, } if (uint32_t(imageIndex) + 1 < mImages.Length()) { - GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp); + GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS)); } TimedImage* img = &mImages[imageIndex]; From 0c13d419892ac8af363207ca6f6905cd87c421b1 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Mon, 3 Aug 2015 10:05:05 +0900 Subject: [PATCH 091/113] Bug 830801 - Part 1. Set NOMINMAX define as default. r=mshal --- configure.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.in b/configure.in index 53b533b7efdb..d3ff2a6ba939 100644 --- a/configure.in +++ b/configure.in @@ -2332,6 +2332,8 @@ ia64*-hpux*) AC_DEFINE(HW_THREADS) AC_DEFINE(STDC_HEADERS) AC_DEFINE(WIN32_LEAN_AND_MEAN) + dnl See http://support.microsoft.com/kb/143208 to use STL + AC_DEFINE(NOMINMAX) TARGET_MD_ARCH=win32 _PLATFORM_DEFAULT_TOOLKIT='cairo-windows' BIN_SUFFIX='.exe' From 50e851b877093498d7e344233ae2ec15a599ab45 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Mon, 3 Aug 2015 10:07:09 +0900 Subject: [PATCH 092/113] Bug 830801 - Part 2. Remove NOMINMAX define from moz.build. r=mshal --- accessible/windows/ia2/moz.build | 5 ----- accessible/windows/sdn/moz.build | 4 ---- accessible/windows/uia/moz.build | 5 ----- dom/canvas/compiledtest/moz.build | 3 --- dom/canvas/moz.build | 3 --- dom/media/directshow/moz.build | 3 --- dom/media/fmp4/moz.build | 3 --- dom/media/platforms/agnostic/eme/moz.build | 3 --- dom/media/platforms/moz.build | 3 --- dom/media/platforms/wmf/moz.build | 3 --- dom/media/webrtc/moz.build | 3 --- gfx/angle/moz.build | 1 - gfx/angle/src/libEGL/moz.build | 1 - gfx/angle/src/libGLESv2/moz.build | 1 - gfx/ots/src/moz.build | 1 - ipc/chromium/chromium-config.mozbuild | 1 - media/mtransport/common.build | 1 - media/mtransport/test/moz.build | 1 - security/sandbox/moz.build | 2 +- security/sandbox/staticruntime/moz.build | 2 +- security/sandbox/win/src/sandboxbroker/moz.build | 3 +-- toolkit/crashreporter/breakpad-windows-libxul/moz.build | 2 +- .../breakpad-windows-libxul/staticruntime/moz.build | 2 +- toolkit/crashreporter/breakpad-windows-standalone/moz.build | 2 +- toolkit/crashreporter/test/moz.build | 2 -- toolkit/mozapps/update/updater/updater-common.build | 1 - 26 files changed, 6 insertions(+), 55 deletions(-) diff --git a/accessible/windows/ia2/moz.build b/accessible/windows/ia2/moz.build index 7c593cf85f8a..43499cfe83c5 100644 --- a/accessible/windows/ia2/moz.build +++ b/accessible/windows/ia2/moz.build @@ -47,11 +47,6 @@ LOCAL_INCLUDES += [ FINAL_LIBRARY = 'xul' -# The midl generated code include Windows headers which defines min and max -# macros which conflicts with std::min/max. Suppress the macros: -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True - FAIL_ON_WARNINGS = True include('/ipc/chromium/chromium-config.mozbuild') diff --git a/accessible/windows/sdn/moz.build b/accessible/windows/sdn/moz.build index 9dba32e8378d..4cdf63d565db 100644 --- a/accessible/windows/sdn/moz.build +++ b/accessible/windows/sdn/moz.build @@ -19,10 +19,6 @@ LOCAL_INCLUDES += [ '/accessible/xul', ] -# The midl generated code include Windows headers which defines min and max -# macros which conflicts with std::min/max. Suppress the macros: -DEFINES['NOMINMAX'] = True - FINAL_LIBRARY = 'xul' FAIL_ON_WARNINGS = True diff --git a/accessible/windows/uia/moz.build b/accessible/windows/uia/moz.build index c50e52807121..df904b3af08b 100644 --- a/accessible/windows/uia/moz.build +++ b/accessible/windows/uia/moz.build @@ -19,9 +19,4 @@ LOCAL_INCLUDES += [ FINAL_LIBRARY = 'xul' -# The midl generated code include Windows headers which defines min and max -# macros which conflicts with std::min/max. Suppress the macros: -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True - FAIL_ON_WARNINGS = True diff --git a/dom/canvas/compiledtest/moz.build b/dom/canvas/compiledtest/moz.build index e0728e373d00..c8a6c0fbc343 100644 --- a/dom/canvas/compiledtest/moz.build +++ b/dom/canvas/compiledtest/moz.build @@ -4,9 +4,6 @@ # 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/. -# http://support.microsoft.com/kb/143208 -DEFINES['NOMINMAX'] = True - GeckoCppUnitTests([ 'TestWebGLElementArrayCache', ]) diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build index d8384e884f50..140a1ce25ef5 100644 --- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -36,9 +36,6 @@ EXPORTS.mozilla.dom += [ 'WebGLVertexArrayObject.h', ] -# http://support.microsoft.com/kb/143208 -DEFINES['NOMINMAX'] = True - # Canvas 2D and common sources UNIFIED_SOURCES += [ 'CanvasImageCache.cpp', diff --git a/dom/media/directshow/moz.build b/dom/media/directshow/moz.build index 11e71544ac4b..e658ff395943 100644 --- a/dom/media/directshow/moz.build +++ b/dom/media/directshow/moz.build @@ -41,6 +41,3 @@ FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '/media/webrtc/trunk/webrtc/modules/video_capture/windows', ] - -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True diff --git a/dom/media/fmp4/moz.build b/dom/media/fmp4/moz.build index 54166c5647c1..29deb6781a00 100644 --- a/dom/media/fmp4/moz.build +++ b/dom/media/fmp4/moz.build @@ -23,8 +23,5 @@ FINAL_LIBRARY = 'xul' FAIL_ON_WARNINGS = True -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True - if CONFIG['MOZ_GONK_MEDIACODEC']: DEFINES['MOZ_GONK_MEDIACODEC'] = True diff --git a/dom/media/platforms/agnostic/eme/moz.build b/dom/media/platforms/agnostic/eme/moz.build index 57224c1ef049..a73d33891b20 100644 --- a/dom/media/platforms/agnostic/eme/moz.build +++ b/dom/media/platforms/agnostic/eme/moz.build @@ -23,6 +23,3 @@ include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' FAIL_ON_WARNINGS = True - -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True diff --git a/dom/media/platforms/moz.build b/dom/media/platforms/moz.build index c06cb92d727a..42e9a4f03faa 100644 --- a/dom/media/platforms/moz.build +++ b/dom/media/platforms/moz.build @@ -82,6 +82,3 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': FINAL_LIBRARY = 'xul' FAIL_ON_WARNINGS = True - -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True diff --git a/dom/media/platforms/wmf/moz.build b/dom/media/platforms/wmf/moz.build index 25f51071900f..54de727030e1 100644 --- a/dom/media/platforms/wmf/moz.build +++ b/dom/media/platforms/wmf/moz.build @@ -31,9 +31,6 @@ include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True - FAIL_ON_WARNINGS = True CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] diff --git a/dom/media/webrtc/moz.build b/dom/media/webrtc/moz.build index 56bc112b7ebc..7452d8d94ac0 100644 --- a/dom/media/webrtc/moz.build +++ b/dom/media/webrtc/moz.build @@ -83,9 +83,6 @@ if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']: ] FINAL_LIBRARY = 'xul' -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True - if CONFIG['_MSC_VER']: CXXFLAGS += [ diff --git a/gfx/angle/moz.build b/gfx/angle/moz.build index adefe8d3b71d..a6436441e84c 100644 --- a/gfx/angle/moz.build +++ b/gfx/angle/moz.build @@ -98,7 +98,6 @@ if CONFIG['GNU_CXX']: if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: CXXFLAGS += ['-I\'%s/include/\'' % CONFIG['MOZ_DIRECTX_SDK_PATH']] -DEFINES['NOMINMAX'] = True DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True DEFINES['_HAS_EXCEPTIONS'] = 0 diff --git a/gfx/angle/src/libEGL/moz.build b/gfx/angle/src/libEGL/moz.build index 6ab700d26dd0..0bbcb836d514 100644 --- a/gfx/angle/src/libEGL/moz.build +++ b/gfx/angle/src/libEGL/moz.build @@ -30,7 +30,6 @@ if CONFIG['GNU_CXX']: if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: CXXFLAGS += ['-I\'%s/include/\'' % CONFIG['MOZ_DIRECTX_SDK_PATH']] -DEFINES['NOMINMAX'] = True DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True DEFINES['_HAS_EXCEPTIONS'] = 0 diff --git a/gfx/angle/src/libGLESv2/moz.build b/gfx/angle/src/libGLESv2/moz.build index 5795f3817e60..3cb95181d353 100644 --- a/gfx/angle/src/libGLESv2/moz.build +++ b/gfx/angle/src/libGLESv2/moz.build @@ -193,7 +193,6 @@ if CONFIG['GNU_CXX']: if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']: CXXFLAGS += ['-I\'%s/include/\'' % CONFIG['MOZ_DIRECTX_SDK_PATH']] -DEFINES['NOMINMAX'] = True DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True DEFINES['_HAS_EXCEPTIONS'] = 0 diff --git a/gfx/ots/src/moz.build b/gfx/ots/src/moz.build index 9575c0f17d34..d1bd140d8b0c 100644 --- a/gfx/ots/src/moz.build +++ b/gfx/ots/src/moz.build @@ -54,7 +54,6 @@ FINAL_LIBRARY = 'gkmedias' DEFINES['PACKAGE_VERSION'] = '"moz"' DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"' -DEFINES['NOMINMAX'] = True if CONFIG['GKMEDIAS_SHARED_LIBRARY']: DEFINES['OTS_DLL'] = True diff --git a/ipc/chromium/chromium-config.mozbuild b/ipc/chromium/chromium-config.mozbuild index 3cf4f52e2a91..53bd38348dc0 100644 --- a/ipc/chromium/chromium-config.mozbuild +++ b/ipc/chromium/chromium-config.mozbuild @@ -23,7 +23,6 @@ if CONFIG['OS_ARCH'] == 'WINNT': DEFINES.update({ 'UNICODE': True, '_UNICODE': True, - 'NOMINMAX': True, '_CRT_RAND_S': True, 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS': True, '_SECURE_ATL': True, diff --git a/media/mtransport/common.build b/media/mtransport/common.build index 0d441f79c882..5184f2aa1c1c 100644 --- a/media/mtransport/common.build +++ b/media/mtransport/common.build @@ -77,7 +77,6 @@ elif CONFIG['OS_TARGET'] == 'WINNT': DEFINES['WIN'] = True # for stun.h DEFINES['WIN32'] = True - DEFINES['NOMINMAX'] = True LOCAL_INCLUDES += [ '/media/mtransport/third_party/nrappkit/src/port/win32/include', ] diff --git a/media/mtransport/test/moz.build b/media/mtransport/test/moz.build index fedd1fc77f9d..de6a1f7e939d 100644 --- a/media/mtransport/test/moz.build +++ b/media/mtransport/test/moz.build @@ -72,7 +72,6 @@ if CONFIG['OS_TARGET'] == 'WINNT': DEFINES['WIN'] = True # for stun.h DEFINES['WIN32'] = True - DEFINES['NOMINMAX'] = True DEFINES['__Userspace_os_Windows'] = 1 else: # Works for Darwin, Linux, Android. Probably doesn't work for others. diff --git a/security/sandbox/moz.build b/security/sandbox/moz.build index c45c1c252dd9..9fb6d727d058 100644 --- a/security/sandbox/moz.build +++ b/security/sandbox/moz.build @@ -39,7 +39,7 @@ elif CONFIG['OS_ARCH'] == 'WINNT': SOURCES['/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc'].no_pgo = True for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'SANDBOX_EXPORTS', - 'NOMINMAX', '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'): + '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'): DEFINES[var] = True LOCAL_INCLUDES += ['/security/sandbox/chromium-shim'] diff --git a/security/sandbox/staticruntime/moz.build b/security/sandbox/staticruntime/moz.build index 708fd344fd38..ee583b2488f7 100644 --- a/security/sandbox/staticruntime/moz.build +++ b/security/sandbox/staticruntime/moz.build @@ -17,7 +17,7 @@ if CONFIG['OS_ARCH'] == 'WINNT': SOURCES['/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc'].no_pgo = True for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'SANDBOX_EXPORTS', - 'NOMINMAX', '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'): + '_CRT_RAND_S', 'CHROMIUM_SANDBOX_BUILD'): DEFINES[var] = True LOCAL_INCLUDES += ['/security/sandbox/chromium-shim'] diff --git a/security/sandbox/win/src/sandboxbroker/moz.build b/security/sandbox/win/src/sandboxbroker/moz.build index 076f0adb737e..c90d9ebf6c53 100644 --- a/security/sandbox/win/src/sandboxbroker/moz.build +++ b/security/sandbox/win/src/sandboxbroker/moz.build @@ -19,8 +19,7 @@ EXPORTS += [ 'sandboxBroker.h', ] -for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'NOMINMAX', - 'SANDBOX_EXPORTS'): +for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM', 'SANDBOX_EXPORTS'): DEFINES[var] = True LOCAL_INCLUDES += [ diff --git a/toolkit/crashreporter/breakpad-windows-libxul/moz.build b/toolkit/crashreporter/breakpad-windows-libxul/moz.build index d638b4ef0f10..122f96611f57 100644 --- a/toolkit/crashreporter/breakpad-windows-libxul/moz.build +++ b/toolkit/crashreporter/breakpad-windows-libxul/moz.build @@ -16,7 +16,7 @@ Library('google_breakpad_libxul_s') FINAL_LIBRARY = 'xul' -for var in ('UNICODE', 'UNICODE_', 'BREAKPAD_NO_TERMINATE_THREAD', 'NOMINMAX'): +for var in ('UNICODE', 'UNICODE_', 'BREAKPAD_NO_TERMINATE_THREAD'): DEFINES[var] = True LOCAL_INCLUDES += [ diff --git a/toolkit/crashreporter/breakpad-windows-libxul/staticruntime/moz.build b/toolkit/crashreporter/breakpad-windows-libxul/staticruntime/moz.build index f7b45f2ecb2c..edaa26c66ad6 100644 --- a/toolkit/crashreporter/breakpad-windows-libxul/staticruntime/moz.build +++ b/toolkit/crashreporter/breakpad-windows-libxul/staticruntime/moz.build @@ -10,7 +10,7 @@ SOURCES += [ Library('google_breakpad_libxul_staticruntime_s') -for var in ('UNICODE', 'UNICODE_', 'BREAKPAD_NO_TERMINATE_THREAD', 'NOMINMAX'): +for var in ('UNICODE', 'UNICODE_', 'BREAKPAD_NO_TERMINATE_THREAD'): DEFINES[var] = True LOCAL_INCLUDES += [ diff --git a/toolkit/crashreporter/breakpad-windows-standalone/moz.build b/toolkit/crashreporter/breakpad-windows-standalone/moz.build index 3e2917657c90..89b35a43fe5d 100644 --- a/toolkit/crashreporter/breakpad-windows-standalone/moz.build +++ b/toolkit/crashreporter/breakpad-windows-standalone/moz.build @@ -6,7 +6,7 @@ FINAL_LIBRARY = 'breakpadinjector' -for var in ('UNICODE', 'UNICODE_', 'BREAKPAD_NO_TERMINATE_THREAD', 'NOMINMAX'): +for var in ('UNICODE', 'UNICODE_', 'BREAKPAD_NO_TERMINATE_THREAD'): DEFINES[var] = True LOCAL_INCLUDES += [ diff --git a/toolkit/crashreporter/test/moz.build b/toolkit/crashreporter/test/moz.build index b27079f2b9ee..ca1db0bff40a 100644 --- a/toolkit/crashreporter/test/moz.build +++ b/toolkit/crashreporter/test/moz.build @@ -29,8 +29,6 @@ DEFINES['SHARED_LIBRARY'] = '%s%s%s' % ( CONFIG['DLL_SUFFIX'] ) -DEFINES['NOMINMAX'] = True - LOCAL_INCLUDES += [ '../google-breakpad/src/', ] diff --git a/toolkit/mozapps/update/updater/updater-common.build b/toolkit/mozapps/update/updater/updater-common.build index ae1d680a630b..a39d47d88019 100644 --- a/toolkit/mozapps/update/updater/updater-common.build +++ b/toolkit/mozapps/update/updater/updater-common.build @@ -27,7 +27,6 @@ if CONFIG['OS_ARCH'] == 'WINNT': RCINCLUDE = '%supdater.rc' % updater_rel_path DEFINES['UNICODE'] = True DEFINES['_UNICODE'] = True - DEFINES['NOMINMAX'] = True USE_STATIC_LIBS = True # Pick up nsWindowsRestart.cpp From ed0bf31fba19ce6bb361267b4ecdde50a47a2afc Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Mon, 3 Aug 2015 10:07:20 +0900 Subject: [PATCH 093/113] Bug 830801 - Part 3. Remove NOMINMAX define from cpp source. r=mshal --- config/gcc-stl-wrapper.template.h | 5 ----- config/msvc-stl-wrapper.template.h | 3 --- embedding/components/printingui/win/nsPrintDialogUtil.cpp | 2 -- memory/replace/logalloc/replay/Replay.cpp | 3 --- toolkit/crashreporter/client/crashreporter_win.cpp | 2 -- 5 files changed, 15 deletions(-) diff --git a/config/gcc-stl-wrapper.template.h b/config/gcc-stl-wrapper.template.h index 7fbf6b5190ec..75bbe755b931 100644 --- a/config/gcc-stl-wrapper.template.h +++ b/config/gcc-stl-wrapper.template.h @@ -17,11 +17,6 @@ // Silence "warning: #include_next is a GCC extension" #pragma GCC system_header -#ifdef _WIN32 -// Suppress windef.h min and max macros - they make std::min/max not compile. -#define NOMINMAX 1 -#endif - // mozalloc.h wants ; break the cycle by always explicitly // including here. NB: this is a tad sneaky. Sez the gcc docs: // diff --git a/config/msvc-stl-wrapper.template.h b/config/msvc-stl-wrapper.template.h index 4b3b9fcfd900..0a01a6dd1b5f 100644 --- a/config/msvc-stl-wrapper.template.h +++ b/config/msvc-stl-wrapper.template.h @@ -12,9 +12,6 @@ # error "STL code can only be used with -fno-exceptions" #endif -// Suppress windef.h min and max macros - they make std::min/max not compile. -#define NOMINMAX 1 - // Code built with !_HAS_EXCEPTIONS calls std::_Throw(), but the win2k // CRT doesn't export std::_Throw(). So we define it. #ifndef mozilla_Throw_h diff --git a/embedding/components/printingui/win/nsPrintDialogUtil.cpp b/embedding/components/printingui/win/nsPrintDialogUtil.cpp index 2370edb445a9..4ab579618b0c 100644 --- a/embedding/components/printingui/win/nsPrintDialogUtil.cpp +++ b/embedding/components/printingui/win/nsPrintDialogUtil.cpp @@ -20,8 +20,6 @@ WIN_LIBS= \ ---------------------------------------------------------------------- */ -#define NOMINMAX 1 - #include "plstr.h" #include #include diff --git a/memory/replace/logalloc/replay/Replay.cpp b/memory/replace/logalloc/replay/Replay.cpp index 56e283aad99b..9059de0433a7 100644 --- a/memory/replace/logalloc/replay/Replay.cpp +++ b/memory/replace/logalloc/replay/Replay.cpp @@ -8,9 +8,6 @@ #include "mozmemory_wrap.h" #ifdef _WIN32 -/* windef.h, which windows.h includes, #defines min and max, which - * breaks std::min. Defining NOMINMAX prevents those #defines. */ -#define NOMINMAX #include #include typedef int ssize_t; diff --git a/toolkit/crashreporter/client/crashreporter_win.cpp b/toolkit/crashreporter/client/crashreporter_win.cpp index f86606b3a66c..a49df3f898e9 100644 --- a/toolkit/crashreporter/client/crashreporter_win.cpp +++ b/toolkit/crashreporter/client/crashreporter_win.cpp @@ -7,8 +7,6 @@ #undef WIN32_LEAN_AND_MEAN #endif -#define NOMINMAX - #include "crashreporter.h" #include From bb9c2f839d3e76211251b03ab0b6e02b742993b4 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sun, 2 Aug 2015 22:50:54 -0400 Subject: [PATCH 094/113] Bug 1188172 - Don't update the resolution/SPCSPS for meta-viewport changes before the first-paint. r=botond --- layout/base/MobileViewportManager.cpp | 8 +++++--- layout/base/MobileViewportManager.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/layout/base/MobileViewportManager.cpp b/layout/base/MobileViewportManager.cpp index d29219c1e385..fbb129a2f137 100644 --- a/layout/base/MobileViewportManager.cpp +++ b/layout/base/MobileViewportManager.cpp @@ -26,6 +26,7 @@ MobileViewportManager::MobileViewportManager(nsIPresShell* aPresShell, : mDocument(aDocument) , mPresShell(aPresShell) , mIsFirstPaint(false) + , mPainted(false) { MOZ_ASSERT(mPresShell); MOZ_ASSERT(mDocument); @@ -85,7 +86,7 @@ MobileViewportManager::HandleEvent(nsIDOMEvent* event) if (type.Equals(DOM_META_ADDED)) { MVM_LOG("%p: got a dom-meta-added event\n", this); - RefreshViewportSize(true); + RefreshViewportSize(mPainted); } else if (type.Equals(FULL_ZOOM_CHANGE)) { MVM_LOG("%p: got a full-zoom-change event\n", this); RefreshViewportSize(false); @@ -99,6 +100,7 @@ MobileViewportManager::Observe(nsISupports* aSubject, const char* aTopic, const if (SameCOMIdentity(aSubject, mDocument) && BEFORE_FIRST_PAINT.EqualsASCII(aTopic)) { MVM_LOG("%p: got a before-first-paint event\n", this); mIsFirstPaint = true; + mPainted = true; RefreshViewportSize(false); } return NS_OK; @@ -149,8 +151,8 @@ MobileViewportManager::UpdateResolution(const nsViewportInfo& aViewportInfo, // // aDisplayWidthChangeRatio is non-empty if: // (a) The meta-viewport tag information changes, and so the CSS viewport - // might change as a result. In this case, we want to adjust the zoom to - // compensate. OR + // might change as a result. If this happens after the content has been + // painted, we want to adjust the zoom to compensate. OR // (b) The display size changed from a nonzero value to another nonzero value. // This covers the case where e.g. the device was rotated, and again we // want to adjust the zoom to compensate. diff --git a/layout/base/MobileViewportManager.h b/layout/base/MobileViewportManager.h index 26676c602a4f..5419852a3111 100644 --- a/layout/base/MobileViewportManager.h +++ b/layout/base/MobileViewportManager.h @@ -54,6 +54,7 @@ private: nsIPresShell* MOZ_NON_OWNING_REF mPresShell; // raw ref since the presShell owns this nsCOMPtr mEventTarget; bool mIsFirstPaint; + bool mPainted; mozilla::LayoutDeviceIntSize mDisplaySize; mozilla::CSSSize mMobileViewportSize; }; From 04c4aa1ea95e63dff836744a63fedaaf8d537425 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 2 Aug 2015 21:03:08 -0700 Subject: [PATCH 095/113] Bug 1189605 - Fix over-indentation of a few parts of nsIDOMWindowUtils.idl. r=khuey IGNORE IDL --HG-- extra : commitid : EwaZah7Y7Xg --- dom/interfaces/base/nsIDOMWindowUtils.idl | 238 +++++++++++----------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 9b7f0df9beae..5df7f9ac8993 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -499,29 +499,29 @@ interface nsIDOMWindowUtils : nsISupports { * scroll. * @param aOptions Set following flags. */ - const unsigned long WHEEL_EVENT_CAUSED_BY_NO_LINE_OR_PAGE_DELTA_DEVICE = 0x0001; - // @deprecated Use WHEEL_EVENT_CAUSED_BY_NO_LINE_OR_PAGE_DELTA_DEVICE. - const unsigned long WHEEL_EVENT_CAUSED_BY_PIXEL_ONLY_DEVICE = 0x0001; - const unsigned long WHEEL_EVENT_CAUSED_BY_MOMENTUM = 0x0002; - const unsigned long WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS = 0x0004; - // If any of the following flags is specified this method will throw an - // exception in case the relevant overflowDelta has an unexpected value. - const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO = 0x0010; - const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE = 0x0020; - const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE = 0x0040; - const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO = 0x0100; - const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE = 0x0200; - const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE = 0x0400; - void sendWheelEvent(in float aX, - in float aY, - in double aDeltaX, - in double aDeltaY, - in double aDeltaZ, - in unsigned long aDeltaMode, - in long aModifiers, - in long aLineOrPageDeltaX, - in long aLineOrPageDeltaY, - in unsigned long aOptions); + const unsigned long WHEEL_EVENT_CAUSED_BY_NO_LINE_OR_PAGE_DELTA_DEVICE = 0x0001; + // @deprecated Use WHEEL_EVENT_CAUSED_BY_NO_LINE_OR_PAGE_DELTA_DEVICE. + const unsigned long WHEEL_EVENT_CAUSED_BY_PIXEL_ONLY_DEVICE = 0x0001; + const unsigned long WHEEL_EVENT_CAUSED_BY_MOMENTUM = 0x0002; + const unsigned long WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS = 0x0004; + // If any of the following flags is specified this method will throw an + // exception in case the relevant overflowDelta has an unexpected value. + const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO = 0x0010; + const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE = 0x0020; + const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE = 0x0040; + const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO = 0x0100; + const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE = 0x0200; + const unsigned long WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE = 0x0400; + void sendWheelEvent(in float aX, + in float aY, + in double aDeltaX, + in double aDeltaY, + in double aDeltaZ, + in unsigned long aDeltaMode, + in long aModifiers, + in long aLineOrPageDeltaX, + in long aLineOrPageDeltaY, + in unsigned long aOptions); /** * Synthesize a key event to the window. The event types supported are: @@ -1359,16 +1359,16 @@ interface nsIDOMWindowUtils : nsISupports { /** * Returns a handle which represents current recording start position. */ - void startFrameTimeRecording([retval] out unsigned long startIndex); + void startFrameTimeRecording([retval] out unsigned long startIndex); /** * Returns number of recorded frames since startIndex was issued, * and allocates+populates 2 arraye with the recorded data. * - Allocation is infallible. Should be released even if size is 0. */ - void stopFrameTimeRecording(in unsigned long startIndex, - [optional] out unsigned long frameCount, - [retval, array, size_is(frameCount)] out float frameIntervals); + void stopFrameTimeRecording(in unsigned long startIndex, + [optional] out unsigned long frameCount, + [retval, array, size_is(frameCount)] out float frameIntervals); /** * Signals that we're begining to tab switch. This is used by painting code to @@ -1697,7 +1697,7 @@ interface nsIDOMWindowUtils : nsISupports { * After calling the method, the window for which this DOMWindowUtils * was created can be closed using scripts. */ - void allowScriptsToClose(); + void allowScriptsToClose(); /** * Is the parent window's main widget visible? If it isn't, we probably @@ -1710,109 +1710,109 @@ interface nsIDOMWindowUtils : nsISupports { */ readonly attribute boolean isParentWindowMainWidgetVisible; - /** - * In certain cases the event handling of nodes, form controls in practice, - * may be disabled. Such cases are for example the existence of disabled - * attribute or -moz-user-input: none/disabled. - */ - boolean isNodeDisabledForEvents(in nsIDOMNode aNode); + /** + * In certain cases the event handling of nodes, form controls in practice, + * may be disabled. Such cases are for example the existence of disabled + * attribute or -moz-user-input: none/disabled. + */ + boolean isNodeDisabledForEvents(in nsIDOMNode aNode); - /** - * Setting paintFlashing to true will flash newly painted area. - */ - attribute boolean paintFlashing; + /** + * Setting paintFlashing to true will flash newly painted area. + */ + attribute boolean paintFlashing; - /** - * Add a "synchronous section", in the form of an nsIRunnable run once the - * event loop has reached a "stable state". |runnable| must not cause any - * queued events to be processed (i.e. must not spin the event loop). - * We've reached a stable state when the currently executing task/event has - * finished, see: - * http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section - * In practice this runs aRunnable once the currently executing event - * finishes. If called multiple times per task/event, all the runnables will - * be executed, in the order in which runInStableState() was called. - * - * XXX - This can wreak havoc if you're not using this for very simple - * purposes, eg testing or setting a flag. - */ - void runInStableState(in nsIRunnable runnable); + /** + * Add a "synchronous section", in the form of an nsIRunnable run once the + * event loop has reached a "stable state". |runnable| must not cause any + * queued events to be processed (i.e. must not spin the event loop). + * We've reached a stable state when the currently executing task/event has + * finished, see: + * http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section + * In practice this runs aRunnable once the currently executing event + * finishes. If called multiple times per task/event, all the runnables will + * be executed, in the order in which runInStableState() was called. + * + * XXX - This can wreak havoc if you're not using this for very simple + * purposes, eg testing or setting a flag. + */ + void runInStableState(in nsIRunnable runnable); - /** - * Run the given runnable before the next iteration of the event loop (this - * includes native events too). If a nested loop is spawned within the current - * event then the runnable will not be run until that loop has terminated. - * - * XXX - This can wreak havoc if you're not using this for very simple - * purposes, eg testing or setting a flag. - */ - void runBeforeNextEvent(in nsIRunnable runnable); + /** + * Run the given runnable before the next iteration of the event loop (this + * includes native events too). If a nested loop is spawned within the current + * event then the runnable will not be run until that loop has terminated. + * + * XXX - This can wreak havoc if you're not using this for very simple + * purposes, eg testing or setting a flag. + */ + void runBeforeNextEvent(in nsIRunnable runnable); - /* - * Returns the value of a given property animated on the compositor thread. - * If the property is NOT currently being animated on the compositor thread, - * returns an empty string. - */ - AString getOMTAStyle(in nsIDOMElement aElement, in AString aProperty, - [optional] in AString aPseudoElement); + /* + * Returns the value of a given property animated on the compositor thread. + * If the property is NOT currently being animated on the compositor thread, + * returns an empty string. + */ + AString getOMTAStyle(in nsIDOMElement aElement, in AString aProperty, + [optional] in AString aPseudoElement); - /** - * Special function that gets a property syncronously from the last composite - * that occured. - * - * Supported properties: - * "overdraw": Report a percentage between 0 and 999 indicate how many times - * each pixels on the destination window have been touched. - * "missed_hwc": Report a bool if hardware composer is supported but was - * not used for the last frame. - */ - float requestCompositorProperty(in AString aProperty); + /** + * Special function that gets a property syncronously from the last composite + * that occured. + * + * Supported properties: + * "overdraw": Report a percentage between 0 and 999 indicate how many times + * each pixels on the destination window have been touched. + * "missed_hwc": Report a bool if hardware composer is supported but was + * not used for the last frame. + */ + float requestCompositorProperty(in AString aProperty); - /** - * If aHandlingInput is true, this informs the event state manager that - * we're handling user input. Otherwise, this is a no-op (as by default - * we're not handling user input). - * Remember to call destruct() on the return value! - * See also nsIDOMWindowUtils::isHandlingUserInput. - */ - nsIJSRAIIHelper setHandlingUserInput(in boolean aHandlingInput); + /** + * If aHandlingInput is true, this informs the event state manager that + * we're handling user input. Otherwise, this is a no-op (as by default + * we're not handling user input). + * Remember to call destruct() on the return value! + * See also nsIDOMWindowUtils::isHandlingUserInput. + */ + nsIJSRAIIHelper setHandlingUserInput(in boolean aHandlingInput); - /** - * Get the content- and compositor-side APZ test data instances. - * The return values are of type APZTestData (see APZTestData.webidl). - */ - [implicit_jscontext] jsval getContentAPZTestData(); - [implicit_jscontext] jsval getCompositorAPZTestData(); + /** + * Get the content- and compositor-side APZ test data instances. + * The return values are of type APZTestData (see APZTestData.webidl). + */ + [implicit_jscontext] jsval getContentAPZTestData(); + [implicit_jscontext] jsval getCompositorAPZTestData(); - /** - * Posts an eRestyle_Self restyle event for the given element. - */ - void postRestyleSelfEvent(in nsIDOMElement aElement); + /** + * Posts an eRestyle_Self restyle event for the given element. + */ + void postRestyleSelfEvent(in nsIDOMElement aElement); - /** - * With this it's possible to mute all the MediaElements in this window. - * We have audioMuted and audioVolume to preserve the volume across - * mute/umute. - */ - attribute boolean audioMuted; + /** + * With this it's possible to mute all the MediaElements in this window. + * We have audioMuted and audioVolume to preserve the volume across + * mute/umute. + */ + attribute boolean audioMuted; - /** - * range: greater or equal to 0. The real volume level is affected by the - * volume of all ancestor windows. - */ - attribute float audioVolume; + /** + * range: greater or equal to 0. The real volume level is affected by the + * volume of all ancestor windows. + */ + attribute float audioVolume; - /** - * This method doesn't do anything useful. It was solely added for the - * purpose of the test for bug 503926. - */ - void xpconnectArgument(in nsIDOMWindowUtils aThis); + /** + * This method doesn't do anything useful. It was solely added for the + * purpose of the test for bug 503926. + */ + void xpconnectArgument(in nsIDOMWindowUtils aThis); - /** - * Helper for JS components that need to send permission requests with - * e10s support properly. - */ - void askPermission(in nsIContentPermissionRequest aRequest); + /** + * Helper for JS components that need to send permission requests with + * e10s support properly. + */ + void askPermission(in nsIContentPermissionRequest aRequest); /** * Number of frames constructed (excluding breaking) for the curent From e92e2b3d342cd89543964e781cdc59b91192411c Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 2 Aug 2015 21:03:09 -0700 Subject: [PATCH 096/113] Bug 538194 patch 1 - Refactor some code dealing with fitting block formatting contexts around floats into separate functions. r=roc --HG-- extra : commitid : 5GnXOwsYaJA --- layout/generic/nsBlockFrame.cpp | 2 +- layout/generic/nsBlockFrame.h | 2 +- layout/generic/nsBlockReflowState.cpp | 60 +++++++++++++-------------- layout/generic/nsBlockReflowState.h | 28 ++++++++++++- 4 files changed, 59 insertions(+), 33 deletions(-) diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 5f3d9c99ded9..ea69b03f85a1 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -7205,7 +7205,7 @@ nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame) // matter. /* static */ nsBlockFrame::ReplacedElementISizeToClear -nsBlockFrame::ISizeToClearPastFloats(nsBlockReflowState& aState, +nsBlockFrame::ISizeToClearPastFloats(const nsBlockReflowState& aState, const LogicalRect& aFloatAvailableSpace, nsIFrame* aFrame) { diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h index 637be9cc4859..e649825608c7 100644 --- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -324,7 +324,7 @@ public: { return marginIStart + borderBoxISize + marginIEnd; } }; static ReplacedElementISizeToClear - ISizeToClearPastFloats(nsBlockReflowState& aState, + ISizeToClearPastFloats(const nsBlockReflowState& aState, const mozilla::LogicalRect& aFloatAvailableSpace, nsIFrame* aFrame); diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index 4e461d2acfc9..e94ea78b8f32 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -161,7 +161,7 @@ nsBlockReflowState::ComputeReplacedBlockOffsetsForFloats( nsIFrame* aFrame, const LogicalRect& aFloatAvailableSpace, nscoord& aIStartResult, - nscoord& aIEndResult) + nscoord& aIEndResult) const { WritingMode wm = mReflowState.GetWritingMode(); // The frame is clueless about the float manager and therefore we @@ -302,6 +302,30 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, #endif } +bool +nsBlockReflowState::ReplacedBlockFitsInAvailSpace(nsIFrame* aReplacedBlock, + const nsFlowAreaRect& aFloatAvailableSpace) const +{ + if (!aFloatAvailableSpace.mHasFloats) { + // If there aren't any floats here, then we always fit. + // We check this before calling ISizeToClearPastFloats, which is + // somewhat expensive. + return true; + } + WritingMode wm = mReflowState.GetWritingMode(); + nsBlockFrame::ReplacedElementISizeToClear replacedISize = + nsBlockFrame::ISizeToClearPastFloats(*this, aFloatAvailableSpace.mRect, + aReplacedBlock); + return std::max(aFloatAvailableSpace.mRect.IStart(wm) - + mContentArea.IStart(wm), + replacedISize.marginIStart) + + replacedISize.borderBoxISize + + std::max(mContentArea.IEnd(wm) - + aFloatAvailableSpace.mRect.IEnd(wm), + replacedISize.marginIEnd) <= + mContentArea.ISize(wm); +} + nsFlowAreaRect nsBlockReflowState::GetFloatAvailableSpaceWithState( nscoord aBCoord, @@ -1078,7 +1102,6 @@ nsBlockReflowState::ClearFloats(nscoord aBCoord, uint8_t aBreakType, } nscoord newBCoord = aBCoord; - WritingMode wm = mReflowState.GetWritingMode(); if (aBreakType != NS_STYLE_CLEAR_NONE) { newBCoord = mFloatManager->ClearFloats(newBCoord, aBreakType, aFlags); @@ -1087,37 +1110,14 @@ nsBlockReflowState::ClearFloats(nscoord aBCoord, uint8_t aBreakType, if (aReplacedBlock) { for (;;) { nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(newBCoord); - if (!floatAvailableSpace.mHasFloats) { - // If there aren't any floats here, then we always fit. - // We check this before calling ISizeToClearPastFloats, which is - // somewhat expensive. - break; - } - nsBlockFrame::ReplacedElementISizeToClear replacedISize = - nsBlockFrame::ISizeToClearPastFloats(*this, floatAvailableSpace.mRect, - aReplacedBlock); - if (std::max(floatAvailableSpace.mRect.IStart(wm) - - mContentArea.IStart(wm), - replacedISize.marginIStart) + - replacedISize.borderBoxISize + - std::max(mContentArea.IEnd(wm) - - floatAvailableSpace.mRect.IEnd(wm), - replacedISize.marginIEnd) <= - mContentArea.ISize(wm)) { + if (ReplacedBlockFitsInAvailSpace(aReplacedBlock, floatAvailableSpace)) { break; } // See the analogous code for inlines in nsBlockFrame::DoReflowInlineFrames - if (floatAvailableSpace.mRect.BSize(wm) > 0) { - // See if there's room in the next band. - newBCoord += floatAvailableSpace.mRect.BSize(wm); - } else { - if (mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE) { - // Stop trying to clear here; we'll just get pushed to the - // next column or page and try again there. - break; - } - NS_NOTREACHED("avail space rect with zero height!"); - newBCoord += 1; + if (!AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) { + // Stop trying to clear here; we'll just get pushed to the + // next column or page and try again there. + break; } } } diff --git a/layout/generic/nsBlockReflowState.h b/layout/generic/nsBlockReflowState.h index a8f94ff0d43f..e3574a3d155c 100644 --- a/layout/generic/nsBlockReflowState.h +++ b/layout/generic/nsBlockReflowState.h @@ -93,6 +93,32 @@ public: nsIFrame *aReplacedBlock = nullptr, uint32_t aFlags = 0); + // Advances to the next band, i.e., the next horizontal stripe in + // which there is a different set of floats. + // Return false if it did not advance, which only happens for + // constrained heights (and means that we should get pushed to the + // next column/page). + bool AdvanceToNextBand(const mozilla::LogicalRect& aFloatAvailableSpace, + nscoord *aBCoord) const { + mozilla::WritingMode wm = mReflowState.GetWritingMode(); + if (aFloatAvailableSpace.BSize(wm) > 0) { + // See if there's room in the next band. + *aBCoord += aFloatAvailableSpace.BSize(wm); + } else { + if (mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE) { + // Stop trying to clear here; we'll just get pushed to the + // next column or page and try again there. + return false; + } + NS_NOTREACHED("avail space rect with zero height!"); + *aBCoord += 1; + } + return true; + } + + bool ReplacedBlockFitsInAvailSpace(nsIFrame* aReplacedBlock, + const nsFlowAreaRect& aFloatAvailableSpace) const; + bool IsAdjacentWithTop() const { return mBCoord == mBorderPadding.BStart(mReflowState.GetWritingMode()); } @@ -117,7 +143,7 @@ public: void ComputeReplacedBlockOffsetsForFloats(nsIFrame* aFrame, const mozilla::LogicalRect& aFloatAvailableSpace, nscoord& aIStartResult, - nscoord& aIEndResult); + nscoord& aIEndResult) const; // Caller must have called GetAvailableSpace for the current mBCoord void ComputeBlockAvailSpace(nsIFrame* aFrame, From 119b5574601667662e413e2bcaf4382a62301544 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 2 Aug 2015 21:03:09 -0700 Subject: [PATCH 097/113] Bug 538194 patch 2 - Redo block reflow below floats when the height of a block that does not intersect floats pushes it into the way of other floats. r=roc This adds an additional retry loop in block reflow that we can only trigger when reflowing a block formatting context (replacedBlock non-null). It can retry in two different ways, either with a narrower width but at the same vertical position (when ReplacedBlockFitsInAvailSpace is still true) or at a new vertical position (which is treated as a form of clearance). Fortunately we don't have to worry about margins collapsing *through* such a boundary since we're dealing with a new block formatting context. Note that Chromium passes all of the new bfc-displace-* tests, although it moves the block formating context down unnecessarily in bfc-shrink-1.html (which we do neither before nor after the patch), though agrees with the width we have after the patch (but not before the patch). --HG-- extra : commitid : FgDc4hjTxHp --- layout/generic/nsBlockFrame.cpp | 133 +++++++++++++++--- layout/generic/nsBlockReflowContext.cpp | 4 + layout/reftests/bugs/reftest.list | 16 +-- .../reftests/floats/bfc-displace-1a-ref.html | 34 +++++ layout/reftests/floats/bfc-displace-1a.html | 37 +++++ .../reftests/floats/bfc-displace-1b-ref.html | 34 +++++ layout/reftests/floats/bfc-displace-1b.html | 36 +++++ .../reftests/floats/bfc-displace-2a-ref.html | 40 ++++++ layout/reftests/floats/bfc-displace-2a.html | 43 ++++++ .../reftests/floats/bfc-displace-2b-ref.html | 40 ++++++ layout/reftests/floats/bfc-displace-2b.html | 42 ++++++ .../reftests/floats/bfc-displace-3a-ref.html | 33 +++++ layout/reftests/floats/bfc-displace-3a.html | 43 ++++++ .../reftests/floats/bfc-displace-3b-ref.html | 34 +++++ layout/reftests/floats/bfc-displace-3b.html | 43 ++++++ layout/reftests/floats/bfc-shrink-1-ref.html | 35 +++++ layout/reftests/floats/bfc-shrink-1.html | 34 +++++ layout/reftests/floats/reftest.list | 8 ++ 18 files changed, 664 insertions(+), 25 deletions(-) create mode 100644 layout/reftests/floats/bfc-displace-1a-ref.html create mode 100644 layout/reftests/floats/bfc-displace-1a.html create mode 100644 layout/reftests/floats/bfc-displace-1b-ref.html create mode 100644 layout/reftests/floats/bfc-displace-1b.html create mode 100644 layout/reftests/floats/bfc-displace-2a-ref.html create mode 100644 layout/reftests/floats/bfc-displace-2a.html create mode 100644 layout/reftests/floats/bfc-displace-2b-ref.html create mode 100644 layout/reftests/floats/bfc-displace-2b.html create mode 100644 layout/reftests/floats/bfc-displace-3a-ref.html create mode 100644 layout/reftests/floats/bfc-displace-3a.html create mode 100644 layout/reftests/floats/bfc-displace-3b-ref.html create mode 100644 layout/reftests/floats/bfc-displace-3b.html create mode 100644 layout/reftests/floats/bfc-shrink-1-ref.html create mode 100644 layout/reftests/floats/bfc-shrink-1.html diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index ea69b03f85a1..f236df359a6f 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -3261,14 +3261,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } } + aLine->SetLineIsImpactedByFloat(false); + // Here aState.mBCoord is the block-start border-edge of the block. // Compute the available space for the block nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace(); -#ifdef REALLY_NOISY_REFLOW - printf("setting line %p isImpacted to %s\n", - aLine.get(), floatAvailableSpace.mHasFloats?"true":"false"); -#endif - aLine->SetLineIsImpactedByFloat(floatAvailableSpace.mHasFloats); WritingMode wm = aState.mReflowState.GetWritingMode(); LogicalRect availSpace(wm); aState.ComputeBlockAvailSpace(frame, display, floatAvailableSpace, @@ -3304,14 +3301,13 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } // Now put the block-dir coordinate back to the start of the - // block-start-margin + clearance, and flow the block. + // block-start-margin + clearance. aState.mBCoord -= bStartMargin; availSpace.BStart(wm) -= bStartMargin; if (NS_UNCONSTRAINEDSIZE != availSpace.BSize(wm)) { availSpace.BSize(wm) += bStartMargin; } - // Reflow the block into the available space // construct the html reflow state for the block. ReflowBlock // will initialize it nsHTMLReflowState @@ -3320,17 +3316,120 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance(); nsFloatManager::SavedState floatManagerState; - if (mayNeedRetry) { - blockHtmlRS.mDiscoveredClearance = &clearanceFrame; - aState.mFloatManager->PushState(&floatManagerState); - } else if (!applyBStartMargin) { - blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance; - } + nsReflowStatus frameReflowStatus; + do { + if (floatAvailableSpace.mHasFloats) { + // Set if floatAvailableSpace.mHasFloats is true for any + // iteration of the loop. + aLine->SetLineIsImpactedByFloat(true); + } - nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE; - brc.ReflowBlock(availSpace, applyBStartMargin, aState.mPrevBEndMargin, - clearance, aState.IsAdjacentWithTop(), - aLine.get(), blockHtmlRS, frameReflowStatus, aState); + // We might need to store into mDiscoveredClearance later if it's + // currently null; we want to overwrite any writes that + // brc.ReflowBlock() below does, so we need to remember now + // whether it's empty. + const bool shouldStoreClearance = + aState.mReflowState.mDiscoveredClearance && + !*aState.mReflowState.mDiscoveredClearance; + + // Reflow the block into the available space + if (mayNeedRetry || replacedBlock) { + aState.mFloatManager->PushState(&floatManagerState); + } + + if (mayNeedRetry) { + blockHtmlRS.mDiscoveredClearance = &clearanceFrame; + } else if (!applyBStartMargin) { + blockHtmlRS.mDiscoveredClearance = + aState.mReflowState.mDiscoveredClearance; + } + + frameReflowStatus = NS_FRAME_COMPLETE; + brc.ReflowBlock(availSpace, applyBStartMargin, aState.mPrevBEndMargin, + clearance, aState.IsAdjacentWithTop(), + aLine.get(), blockHtmlRS, frameReflowStatus, aState); + + // Now the block has a height. Using that height, get the + // available space again and call ComputeBlockAvailSpace again. + // If ComputeBlockAvailSpace gives a different result, we need to + // reflow again. + if (!replacedBlock) { + break; + } + + LogicalRect oldFloatAvailableSpaceRect(floatAvailableSpace.mRect); + floatAvailableSpace = aState.GetFloatAvailableSpaceForBSize( + aState.mBCoord + bStartMargin, + brc.GetMetrics().Height(), + &floatManagerState); + NS_ASSERTION(floatAvailableSpace.mRect.BStart(wm) == + oldFloatAvailableSpaceRect.BStart(wm), + "yikes"); + // Restore the height to the position of the next band. + floatAvailableSpace.mRect.BSize(wm) = + oldFloatAvailableSpaceRect.BSize(wm); + if (!AvailableSpaceShrunk(wm, oldFloatAvailableSpaceRect, + floatAvailableSpace.mRect)) { + break; + } + + bool advanced = false; + if (!aState.ReplacedBlockFitsInAvailSpace(replacedBlock, + floatAvailableSpace)) { + // Advance to the next band. + nscoord newBCoord = aState.mBCoord; + if (aState.AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) { + advanced = true; + } + // ClearFloats might be able to advance us further once we're there. + aState.mBCoord = + aState.ClearFloats(newBCoord, NS_STYLE_CLEAR_NONE, replacedBlock); + // Start over with a new available space rect at the new height. + floatAvailableSpace = + aState.GetFloatAvailableSpaceWithState(aState.mBCoord, + &floatManagerState); + } + + LogicalRect oldAvailSpace(availSpace); + aState.ComputeBlockAvailSpace(frame, display, floatAvailableSpace, + replacedBlock != nullptr, availSpace); + + if (!advanced && availSpace.IsEqualEdges(oldAvailSpace)) { + break; + } + + // We need another reflow. + aState.mFloatManager->PopState(&floatManagerState); + + if (!treatWithClearance && !applyBStartMargin && + aState.mReflowState.mDiscoveredClearance) { + // We set shouldStoreClearance above to record only the first + // frame that requires clearance. + if (shouldStoreClearance) { + *aState.mReflowState.mDiscoveredClearance = frame; + } + aState.mPrevChild = frame; + // Exactly what we do now is flexible since we'll definitely be + // reflowed. + return; + } + + if (advanced) { + // We're pushing down the border-box, so we don't apply margin anymore. + // This should never cause us to move up since the call to + // GetFloatAvailableSpaceForBSize above included the margin. + applyBStartMargin = false; + bStartMargin = 0; + treatWithClearance = true; // avoid hitting test above + clearance = 0; + } + + blockHtmlRS.~nsHTMLReflowState(); + new (&blockHtmlRS) nsHTMLReflowState(aState.mPresContext, + aState.mReflowState, frame, + availSpace.Size(wm).ConvertTo( + frame->GetWritingMode(), wm)); + } while (true); if (mayNeedRetry && clearanceFrame) { aState.mFloatManager->PopState(&floatManagerState); diff --git a/layout/generic/nsBlockReflowContext.cpp b/layout/generic/nsBlockReflowContext.cpp index 16ddc9f1c5e6..8684b064ec1b 100644 --- a/layout/generic/nsBlockReflowContext.cpp +++ b/layout/generic/nsBlockReflowContext.cpp @@ -257,6 +257,10 @@ nsBlockReflowContext::ReflowBlock(const LogicalRect& aSpace, aFrameRS.AvailableBSize() -= mBStartMargin.get() + aClearance; } } + } else { + // nsBlockFrame::ReflowBlock might call us multiple times with + // *different* values of aApplyBStartMargin. + mBStartMargin.Zero(); } nscoord tI = 0, tB = 0; diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 20855034b8db..104d119ea8ac 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -46,14 +46,14 @@ asserts(2) skip-if(!cocoaWidget) HTTP(..) == 10209-3.html 10209-3-ref.html # Ass == 25888-2r.html 25888-2r-ref.html == 25888-3l.html 25888-3l-ref.html == 25888-3r.html 25888-3r-ref.html -fails == 25888-1l-block.html 25888-1l-ref.html # Bug 25888 -fails != 25888-1l-block.html 25888-1l-notref.html # Bug 25888 -fails == 25888-1r-block.html 25888-1r-ref.html # Bug 25888 -fails != 25888-1r-block.html 25888-1r-notref.html # Bug 25888 -fails == 25888-2l-block.html 25888-2l-ref.html # Bug 25888 -fails == 25888-2r-block.html 25888-2r-ref.html # Bug 25888 -fails == 25888-3l-block.html 25888-3l-ref.html # Bug 25888 -fails == 25888-3r-block.html 25888-3r-ref.html # Bug 25888 +== 25888-1l-block.html 25888-1l-ref.html +!= 25888-1l-block.html 25888-1l-notref.html +== 25888-1r-block.html 25888-1r-ref.html +!= 25888-1r-block.html 25888-1r-notref.html +== 25888-2l-block.html 25888-2l-ref.html +== 25888-2r-block.html 25888-2r-ref.html +== 25888-3l-block.html 25888-3l-ref.html +== 25888-3r-block.html 25888-3r-ref.html skip-if(B2G||Mulet) == 28811-1a.html 28811-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop fuzzy-if(gtkWidget,6,26200) == 28811-1b.html 28811-1-ref.html # Bug 1128229 skip-if(B2G||Mulet) == 28811-2a.html 28811-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop diff --git a/layout/reftests/floats/bfc-displace-1a-ref.html b/layout/reftests/floats/bfc-displace-1a-ref.html new file mode 100644 index 000000000000..3171bdc03eec --- /dev/null +++ b/layout/reftests/floats/bfc-displace-1a-ref.html @@ -0,0 +1,34 @@ + +Test of block formatting context displacement by floats + + + +
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-1a.html b/layout/reftests/floats/bfc-displace-1a.html new file mode 100644 index 000000000000..6189510401ab --- /dev/null +++ b/layout/reftests/floats/bfc-displace-1a.html @@ -0,0 +1,37 @@ + +Test of block formatting context displacement by floats + + + + + +
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-1b-ref.html b/layout/reftests/floats/bfc-displace-1b-ref.html new file mode 100644 index 000000000000..1eddcbdf6cd0 --- /dev/null +++ b/layout/reftests/floats/bfc-displace-1b-ref.html @@ -0,0 +1,34 @@ + +Test of block formatting context displacement by floats + + + +
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-1b.html b/layout/reftests/floats/bfc-displace-1b.html new file mode 100644 index 000000000000..b7d4ec483861 --- /dev/null +++ b/layout/reftests/floats/bfc-displace-1b.html @@ -0,0 +1,36 @@ + +Test of block formatting context displacement by floats + + + + +
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-2a-ref.html b/layout/reftests/floats/bfc-displace-2a-ref.html new file mode 100644 index 000000000000..0855ce8de451 --- /dev/null +++ b/layout/reftests/floats/bfc-displace-2a-ref.html @@ -0,0 +1,40 @@ + +Test of block formatting context displacement by floats + + + +
+
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-2a.html b/layout/reftests/floats/bfc-displace-2a.html new file mode 100644 index 000000000000..615b4a75956c --- /dev/null +++ b/layout/reftests/floats/bfc-displace-2a.html @@ -0,0 +1,43 @@ + +Test of block formatting context displacement by floats + + + + + +
+
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-2b-ref.html b/layout/reftests/floats/bfc-displace-2b-ref.html new file mode 100644 index 000000000000..bb6ede1cee5a --- /dev/null +++ b/layout/reftests/floats/bfc-displace-2b-ref.html @@ -0,0 +1,40 @@ + +Test of block formatting context displacement by floats + + + +
+
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-2b.html b/layout/reftests/floats/bfc-displace-2b.html new file mode 100644 index 000000000000..2e95777248d4 --- /dev/null +++ b/layout/reftests/floats/bfc-displace-2b.html @@ -0,0 +1,42 @@ + +Test of block formatting context displacement by floats + + + + +
+
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-3a-ref.html b/layout/reftests/floats/bfc-displace-3a-ref.html new file mode 100644 index 000000000000..b0744d3205ac --- /dev/null +++ b/layout/reftests/floats/bfc-displace-3a-ref.html @@ -0,0 +1,33 @@ + +Test of block formatting context displacement by floats + + + +
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-3a.html b/layout/reftests/floats/bfc-displace-3a.html new file mode 100644 index 000000000000..6622fa723c64 --- /dev/null +++ b/layout/reftests/floats/bfc-displace-3a.html @@ -0,0 +1,43 @@ + +Test of block formatting context displacement by floats + + + + + +
+
+
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-3b-ref.html b/layout/reftests/floats/bfc-displace-3b-ref.html new file mode 100644 index 000000000000..284301c1f797 --- /dev/null +++ b/layout/reftests/floats/bfc-displace-3b-ref.html @@ -0,0 +1,34 @@ + +Test of block formatting context displacement by floats + + + +
+
+
+
+
diff --git a/layout/reftests/floats/bfc-displace-3b.html b/layout/reftests/floats/bfc-displace-3b.html new file mode 100644 index 000000000000..d4dc7f9b3a95 --- /dev/null +++ b/layout/reftests/floats/bfc-displace-3b.html @@ -0,0 +1,43 @@ + +Test of block formatting context displacement by floats + + + + + +
+
+
+
+
+
+
diff --git a/layout/reftests/floats/bfc-shrink-1-ref.html b/layout/reftests/floats/bfc-shrink-1-ref.html new file mode 100644 index 000000000000..071b66300acb --- /dev/null +++ b/layout/reftests/floats/bfc-shrink-1-ref.html @@ -0,0 +1,35 @@ + +Test of block formatting context displacement by floats + + + + + +
+
+
+
+
diff --git a/layout/reftests/floats/bfc-shrink-1.html b/layout/reftests/floats/bfc-shrink-1.html new file mode 100644 index 000000000000..6f89a90a5252 --- /dev/null +++ b/layout/reftests/floats/bfc-shrink-1.html @@ -0,0 +1,34 @@ + +Test of block formatting context displacement by floats + + + + + +
+
+
+
+
diff --git a/layout/reftests/floats/reftest.list b/layout/reftests/floats/reftest.list index 06aa7f9b1ba0..4058fa23878f 100644 --- a/layout/reftests/floats/reftest.list +++ b/layout/reftests/floats/reftest.list @@ -37,6 +37,14 @@ fails == 345369-2.html 345369-2-ref.html == float-in-rtl-4c.html float-in-rtl-4-ref.html == float-in-rtl-4d.html float-in-rtl-4-ref.html +== bfc-displace-1a.html bfc-displace-1a-ref.html +== bfc-displace-1b.html bfc-displace-1b-ref.html +== bfc-displace-2a.html bfc-displace-2a-ref.html +== bfc-displace-2b.html bfc-displace-2b-ref.html +== bfc-displace-3a.html bfc-displace-3a-ref.html +== bfc-displace-3b.html bfc-displace-3b-ref.html +== bfc-shrink-1.html bfc-shrink-1-ref.html + # Testcases that involve vertical writing mode. # # XXX The default-preferences setting here can be removed after the From e78b42c1b20776bfdff1ea5d28123636477c73c2 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 2 Aug 2015 21:03:09 -0700 Subject: [PATCH 098/113] Bug 1187851 patch 1 - Make dynamic changes to filter and perspective change fixed position containing block for descendants. r=roc Note that this now uses AddAndRemoveTransform hints for changes that are other than adding and removing a transform. Since there's still a little bit of transform-related stuff there too (which I did make conditional), I figure it's probably best to leave the name as-is, although I'd be open to renaming it as well. As expected, without the patch, the filter and perspective tests fail, but the added transform test passes. All the tests pass locally with the patch. --HG-- extra : commitid : 3ZfnPHG701V --- layout/base/RestyleManager.cpp | 7 ++- ...filter-containing-block-dynamic-1-ref.html | 19 +++++++ .../filter-containing-block-dynamic-1a.html | 47 ++++++++++++++++++ .../filter-containing-block-dynamic-1b.html | 47 ++++++++++++++++++ .../w3c-css/submitted/filters/reftest.list | 2 + .../reftests/w3c-css/submitted/reftest.list | 5 +- .../containing-block-dynamic-1-ref.html | 19 +++++++ ...rspective-containing-block-dynamic-1a.html | 47 ++++++++++++++++++ ...rspective-containing-block-dynamic-1b.html | 47 ++++++++++++++++++ .../w3c-css/submitted/transforms/reftest.list | 4 ++ ...transform-containing-block-dynamic-1a.html | 49 +++++++++++++++++++ ...transform-containing-block-dynamic-1b.html | 49 +++++++++++++++++++ layout/style/nsStyleStruct.cpp | 10 ++++ layout/style/nsStyleStruct.h | 10 ++-- layout/style/nsStyleStructInlines.h | 4 +- 15 files changed, 356 insertions(+), 10 deletions(-) create mode 100644 layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1a.html create mode 100644 layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1b.html create mode 100644 layout/reftests/w3c-css/submitted/filters/reftest.list create mode 100644 layout/reftests/w3c-css/submitted/transforms/containing-block-dynamic-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html create mode 100644 layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html create mode 100644 layout/reftests/w3c-css/submitted/transforms/reftest.list create mode 100644 layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1a.html create mode 100644 layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1b.html diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 2ca84cba5146..03f46caf2d73 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -776,10 +776,9 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) // that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up // to ancestors (i.e. it can't be an inherited change hint). if (cont->IsAbsPosContaininingBlock()) { - // If a transform has been added, we'll be taking this path, - // but we may be taking this path even if a transform has been - // removed. It's OK to add the bit even if it's not needed. - cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED); + if (cont->StyleDisplay()->HasTransform(cont)) { + cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED); + } if (!cont->IsAbsoluteContainer() && (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) { cont->MarkAsAbsoluteContainingBlock(); diff --git a/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1-ref.html b/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1-ref.html new file mode 100644 index 000000000000..b45b8f7d45e7 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1-ref.html @@ -0,0 +1,19 @@ + + +CSS filters: Creating containing block for fixed positioned elements + + + + +
+ diff --git a/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1a.html b/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1a.html new file mode 100644 index 000000000000..d025f713538c --- /dev/null +++ b/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1a.html @@ -0,0 +1,47 @@ + + +CSS filters: Creating containing block for fixed positioned elements + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1b.html b/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1b.html new file mode 100644 index 000000000000..72ba0f566095 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1b.html @@ -0,0 +1,47 @@ + + +CSS filters: Creating containing block for fixed positioned elements + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/filters/reftest.list b/layout/reftests/w3c-css/submitted/filters/reftest.list new file mode 100644 index 000000000000..36b5451867bd --- /dev/null +++ b/layout/reftests/w3c-css/submitted/filters/reftest.list @@ -0,0 +1,2 @@ +== filter-containing-block-dynamic-1a.html filter-containing-block-dynamic-1-ref.html +== filter-containing-block-dynamic-1b.html filter-containing-block-dynamic-1-ref.html diff --git a/layout/reftests/w3c-css/submitted/reftest.list b/layout/reftests/w3c-css/submitted/reftest.list index 290b9eef3e01..d11b480e32e1 100644 --- a/layout/reftests/w3c-css/submitted/reftest.list +++ b/layout/reftests/w3c-css/submitted/reftest.list @@ -19,6 +19,9 @@ include css21/reftest.list # Conditional Rules Level 3 include conditional3/reftest.list +# Filter Effects Module +include filters/reftest.list + # Flexible Box Layout Module include flexbox/reftest.list @@ -53,7 +56,7 @@ include text3/reftest.list include text-decor-3/reftest.list # Transforms -# include transforms/reftest.list +include transforms/reftest.list # Transitions # include transitions/reftest.list diff --git a/layout/reftests/w3c-css/submitted/transforms/containing-block-dynamic-1-ref.html b/layout/reftests/w3c-css/submitted/transforms/containing-block-dynamic-1-ref.html new file mode 100644 index 000000000000..cf325d0c5365 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/transforms/containing-block-dynamic-1-ref.html @@ -0,0 +1,19 @@ + + +CSS transforms: Creating containing block for fixed positioned elements + + + + +
+ diff --git a/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html b/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html new file mode 100644 index 000000000000..31200fa35861 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html @@ -0,0 +1,47 @@ + + +CSS transforms: Creating containing block for fixed positioned elements + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html b/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html new file mode 100644 index 000000000000..b5f34d9a78a0 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html @@ -0,0 +1,47 @@ + + +CSS transforms: Creating containing block for fixed positioned elements + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/transforms/reftest.list b/layout/reftests/w3c-css/submitted/transforms/reftest.list new file mode 100644 index 000000000000..c861c7bd18b6 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/transforms/reftest.list @@ -0,0 +1,4 @@ +== transform-containing-block-dynamic-1a.html containing-block-dynamic-1-ref.html +== transform-containing-block-dynamic-1b.html containing-block-dynamic-1-ref.html +== perspective-containing-block-dynamic-1a.html containing-block-dynamic-1-ref.html +== perspective-containing-block-dynamic-1b.html containing-block-dynamic-1-ref.html diff --git a/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1a.html b/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1a.html new file mode 100644 index 000000000000..601680616d29 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1a.html @@ -0,0 +1,49 @@ + + +CSS transforms: Creating containing block for fixed positioned elements + + + + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1b.html b/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1b.html new file mode 100644 index 000000000000..7247ab0c97bd --- /dev/null +++ b/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1b.html @@ -0,0 +1,49 @@ + + +CSS transforms: Creating containing block for fixed positioned elements + + + + + + + + + +
+
+
+
+ + diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index a019e79b3f1c..c037e0964861 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1305,6 +1305,11 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons { nsChangeHint hint = nsChangeHint(0); + if (HasFilters() != aOther.HasFilters()) { + // A change from/to being a containing block for position:fixed. + NS_UpdateHint(hint, nsChangeHint_AddOrRemoveTransform); + } + if (mClipPath != aOther.mClipPath || !EqualURIs(mMask, aOther.mMask) || mFilters != aOther.mFilters) { @@ -2847,6 +2852,11 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const break; } + if (HasPerspectiveStyle() != aOther.HasPerspectiveStyle()) { + // A change from/to being a containing block for position:fixed. + NS_UpdateHint(hint, nsChangeHint_AddOrRemoveTransform); + } + if (mChildPerspective != aOther.mChildPerspective || mTransformStyle != aOther.mTransformStyle || mTransformBox != aOther.mTransformBox) diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 15e59f13ae23..3ffb1eaf0da1 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2133,7 +2133,7 @@ struct nsStyleDisplay { uint8_t mTransformBox; // [reset] see nsStyleConsts.h nsRefPtr mSpecifiedTransform; // [reset] nsStyleCoord mTransformOrigin[3]; // [reset] percent, coord, calc, 3rd param is coord, calc only - nsStyleCoord mChildPerspective; // [reset] coord + nsStyleCoord mChildPerspective; // [reset] none, coord nsStyleCoord mPerspectiveOrigin[2]; // [reset] percent, coord, calc nsAutoTArray mTransitions; // [reset] @@ -3163,8 +3163,10 @@ struct nsStyleSVGReset { nsChangeHint CalcDifference(const nsStyleSVGReset& aOther) const; static nsChangeHint MaxDifference() { - return NS_CombineHint(nsChangeHint_UpdateEffects, - NS_CombineHint(nsChangeHint_UpdateOverflow, NS_STYLE_HINT_REFLOW)); + return nsChangeHint_UpdateEffects | + nsChangeHint_UpdateOverflow | + nsChangeHint_AddOrRemoveTransform | + NS_STYLE_HINT_REFLOW; } static nsChangeHint DifferenceAlwaysHandledForDescendants() { // CalcDifference never returns the reflow hints that are sometimes @@ -3175,7 +3177,7 @@ struct nsStyleSVGReset { } bool HasFilters() const { - return mFilters.Length() > 0; + return !mFilters.IsEmpty(); } bool HasNonScalingStroke() const { diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h index 3883d7ceb1f0..e2da9c5d5844 100644 --- a/layout/style/nsStyleStructInlines.h +++ b/layout/style/nsStyleStructInlines.h @@ -138,8 +138,10 @@ nsStyleDisplay::HasTransform(const nsIFrame* aContextFrame) const bool nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const { + NS_ASSERTION(aContextFrame->StyleDisplay() == this, + "unexpected aContextFrame"); return (HasTransform(aContextFrame) || HasPerspectiveStyle() || - !aContextFrame->StyleSVGReset()->mFilters.IsEmpty()) && + aContextFrame->StyleSVGReset()->HasFilters()) && !aContextFrame->IsSVGText(); } From 990ae820cf10319f22a4ef1264b147f07c2daf2f Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 2 Aug 2015 21:03:09 -0700 Subject: [PATCH 099/113] Bug 1187851 patch 2 - Rename nsChangeHint_AddOrRemoveTransform to nsChangeHint_UpdateContainingBlock. r=roc --HG-- extra : commitid : 60f2ryYqjzB --- layout/base/RestyleManager.cpp | 6 +++--- layout/base/nsChangeHint.h | 6 +++--- layout/style/nsStyleStruct.cpp | 6 +++--- layout/style/nsStyleStruct.h | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 03f46caf2d73..7fa32c537f5f 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -759,7 +759,7 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) } } - if ((hint & nsChangeHint_AddOrRemoveTransform) && frame && + if ((hint & nsChangeHint_UpdateContainingBlock) && frame && !(hint & nsChangeHint_ReconstructFrame)) { if (NeedToReframeForAddingOrRemovingTransform(frame) || frame->GetType() == nsGkAtoms::fieldSetFrame || @@ -773,7 +773,7 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) // Normally frame construction would set state bits as needed, // but we're not going to reconstruct the frame so we need to set them. // It's because we need to set this state on each affected frame - // that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up + // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up // to ancestors (i.e. it can't be an inherited change hint). if (cont->IsAbsPosContaininingBlock()) { if (cont->StyleDisplay()->HasTransform(cont)) { @@ -2627,7 +2627,7 @@ ElementRestyler::AddLayerChangesForAnimation() // If we have a transform layer but don't have any transform style, we // probably just removed the transform but haven't destroyed the layer // yet. In this case we will add the appropriate change hint - // (nsChangeHint_AddOrRemoveTransform) when we compare style contexts + // (nsChangeHint_UpdateContainingBlock) when we compare style contexts // so we can skip adding any change hint here. (If we *were* to add // nsChangeHint_UpdateTransformLayer, ApplyRenderingChangeToTree would // complain that we're updating a transform layer without a transform). diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index ab7ae4aeb3c9..789dd8d403f1 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -126,7 +126,7 @@ enum nsChangeHint { * has changed whether the frame is a container for fixed-pos or abs-pos * elements, but reframing is otherwise not needed. */ - nsChangeHint_AddOrRemoveTransform = 0x20000, + nsChangeHint_UpdateContainingBlock = 0x20000, /** * This change hint has *no* change handling behavior. However, it @@ -284,7 +284,7 @@ inline nsChangeHint operator^=(nsChangeHint& aLeft, nsChangeHint aRight) nsChangeHint_UpdateParentOverflow | \ nsChangeHint_ChildrenOnlyTransform | \ nsChangeHint_RecomputePosition | \ - nsChangeHint_AddOrRemoveTransform | \ + nsChangeHint_UpdateContainingBlock | \ nsChangeHint_BorderStyleNoneChange | \ nsChangeHint_NeedReflow | \ nsChangeHint_ReflowChangesSizeOrPosition | \ @@ -302,7 +302,7 @@ inline nsChangeHint NS_HintsNotHandledForDescendantsIn(nsChangeHint aChangeHint) nsChangeHint_UpdateParentOverflow | nsChangeHint_ChildrenOnlyTransform | nsChangeHint_RecomputePosition | - nsChangeHint_AddOrRemoveTransform | + nsChangeHint_UpdateContainingBlock | nsChangeHint_BorderStyleNoneChange | nsChangeHint_UpdateComputedBSize)); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index c037e0964861..ccf7d18518f9 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1307,7 +1307,7 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons if (HasFilters() != aOther.HasFilters()) { // A change from/to being a containing block for position:fixed. - NS_UpdateHint(hint, nsChangeHint_AddOrRemoveTransform); + NS_UpdateHint(hint, nsChangeHint_UpdateContainingBlock); } if (mClipPath != aOther.mClipPath || @@ -2808,7 +2808,7 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const // We do not need to apply nsChangeHint_UpdateTransformLayer since // nsChangeHint_RepaintFrame will forcibly invalidate the frame area and // ensure layers are rebuilt (or removed). - NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_AddOrRemoveTransform, + NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_UpdateContainingBlock, NS_CombineHint(nsChangeHint_UpdateOverflow, nsChangeHint_RepaintFrame))); } else { @@ -2854,7 +2854,7 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const if (HasPerspectiveStyle() != aOther.HasPerspectiveStyle()) { // A change from/to being a containing block for position:fixed. - NS_UpdateHint(hint, nsChangeHint_AddOrRemoveTransform); + NS_UpdateHint(hint, nsChangeHint_UpdateContainingBlock); } if (mChildPerspective != aOther.mChildPerspective || diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 3ffb1eaf0da1..cbb74f95809e 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2065,7 +2065,7 @@ struct nsStyleDisplay { nsChangeHint_UpdateTransformLayer | nsChangeHint_UpdateOverflow | nsChangeHint_UpdatePostTransformOverflow | - nsChangeHint_AddOrRemoveTransform | + nsChangeHint_UpdateContainingBlock | nsChangeHint_NeutralChange); } static nsChangeHint DifferenceAlwaysHandledForDescendants() { @@ -3165,7 +3165,7 @@ struct nsStyleSVGReset { static nsChangeHint MaxDifference() { return nsChangeHint_UpdateEffects | nsChangeHint_UpdateOverflow | - nsChangeHint_AddOrRemoveTransform | + nsChangeHint_UpdateContainingBlock | NS_STYLE_HINT_REFLOW; } static nsChangeHint DifferenceAlwaysHandledForDescendants() { From 70c88b7d2c71d8fe51d672ace71b24450720928d Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 2 Aug 2015 22:34:49 -0700 Subject: [PATCH 100/113] Backed out changeset 5dcb38c7f1b8 (Bug 1187851 patch 2) for reflow counting failures in Gaia integration tests apps/system/test/marionette/edges_gesture_test.js and apps/system/test/marionette/homescreen_navigation_test.js . --HG-- extra : commitid : BV4uUnaUOht --- layout/base/RestyleManager.cpp | 6 +++--- layout/base/nsChangeHint.h | 6 +++--- layout/style/nsStyleStruct.cpp | 6 +++--- layout/style/nsStyleStruct.h | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 7fa32c537f5f..03f46caf2d73 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -759,7 +759,7 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) } } - if ((hint & nsChangeHint_UpdateContainingBlock) && frame && + if ((hint & nsChangeHint_AddOrRemoveTransform) && frame && !(hint & nsChangeHint_ReconstructFrame)) { if (NeedToReframeForAddingOrRemovingTransform(frame) || frame->GetType() == nsGkAtoms::fieldSetFrame || @@ -773,7 +773,7 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) // Normally frame construction would set state bits as needed, // but we're not going to reconstruct the frame so we need to set them. // It's because we need to set this state on each affected frame - // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up + // that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up // to ancestors (i.e. it can't be an inherited change hint). if (cont->IsAbsPosContaininingBlock()) { if (cont->StyleDisplay()->HasTransform(cont)) { @@ -2627,7 +2627,7 @@ ElementRestyler::AddLayerChangesForAnimation() // If we have a transform layer but don't have any transform style, we // probably just removed the transform but haven't destroyed the layer // yet. In this case we will add the appropriate change hint - // (nsChangeHint_UpdateContainingBlock) when we compare style contexts + // (nsChangeHint_AddOrRemoveTransform) when we compare style contexts // so we can skip adding any change hint here. (If we *were* to add // nsChangeHint_UpdateTransformLayer, ApplyRenderingChangeToTree would // complain that we're updating a transform layer without a transform). diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index 789dd8d403f1..ab7ae4aeb3c9 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -126,7 +126,7 @@ enum nsChangeHint { * has changed whether the frame is a container for fixed-pos or abs-pos * elements, but reframing is otherwise not needed. */ - nsChangeHint_UpdateContainingBlock = 0x20000, + nsChangeHint_AddOrRemoveTransform = 0x20000, /** * This change hint has *no* change handling behavior. However, it @@ -284,7 +284,7 @@ inline nsChangeHint operator^=(nsChangeHint& aLeft, nsChangeHint aRight) nsChangeHint_UpdateParentOverflow | \ nsChangeHint_ChildrenOnlyTransform | \ nsChangeHint_RecomputePosition | \ - nsChangeHint_UpdateContainingBlock | \ + nsChangeHint_AddOrRemoveTransform | \ nsChangeHint_BorderStyleNoneChange | \ nsChangeHint_NeedReflow | \ nsChangeHint_ReflowChangesSizeOrPosition | \ @@ -302,7 +302,7 @@ inline nsChangeHint NS_HintsNotHandledForDescendantsIn(nsChangeHint aChangeHint) nsChangeHint_UpdateParentOverflow | nsChangeHint_ChildrenOnlyTransform | nsChangeHint_RecomputePosition | - nsChangeHint_UpdateContainingBlock | + nsChangeHint_AddOrRemoveTransform | nsChangeHint_BorderStyleNoneChange | nsChangeHint_UpdateComputedBSize)); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index ccf7d18518f9..c037e0964861 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1307,7 +1307,7 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons if (HasFilters() != aOther.HasFilters()) { // A change from/to being a containing block for position:fixed. - NS_UpdateHint(hint, nsChangeHint_UpdateContainingBlock); + NS_UpdateHint(hint, nsChangeHint_AddOrRemoveTransform); } if (mClipPath != aOther.mClipPath || @@ -2808,7 +2808,7 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const // We do not need to apply nsChangeHint_UpdateTransformLayer since // nsChangeHint_RepaintFrame will forcibly invalidate the frame area and // ensure layers are rebuilt (or removed). - NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_UpdateContainingBlock, + NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_AddOrRemoveTransform, NS_CombineHint(nsChangeHint_UpdateOverflow, nsChangeHint_RepaintFrame))); } else { @@ -2854,7 +2854,7 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const if (HasPerspectiveStyle() != aOther.HasPerspectiveStyle()) { // A change from/to being a containing block for position:fixed. - NS_UpdateHint(hint, nsChangeHint_UpdateContainingBlock); + NS_UpdateHint(hint, nsChangeHint_AddOrRemoveTransform); } if (mChildPerspective != aOther.mChildPerspective || diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index cbb74f95809e..3ffb1eaf0da1 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2065,7 +2065,7 @@ struct nsStyleDisplay { nsChangeHint_UpdateTransformLayer | nsChangeHint_UpdateOverflow | nsChangeHint_UpdatePostTransformOverflow | - nsChangeHint_UpdateContainingBlock | + nsChangeHint_AddOrRemoveTransform | nsChangeHint_NeutralChange); } static nsChangeHint DifferenceAlwaysHandledForDescendants() { @@ -3165,7 +3165,7 @@ struct nsStyleSVGReset { static nsChangeHint MaxDifference() { return nsChangeHint_UpdateEffects | nsChangeHint_UpdateOverflow | - nsChangeHint_UpdateContainingBlock | + nsChangeHint_AddOrRemoveTransform | NS_STYLE_HINT_REFLOW; } static nsChangeHint DifferenceAlwaysHandledForDescendants() { From 7bf9f07a6e8f3fe7868005d8cf7165eebbd60019 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 2 Aug 2015 22:35:28 -0700 Subject: [PATCH 101/113] Backed out changeset f24dbdeeaef1 (Bug 1187851 patch 1) for reflow counting failures in Gaia integration tests apps/system/test/marionette/edges_gesture_test.js and apps/system/test/marionette/homescreen_navigation_test.js . --HG-- extra : commitid : A94PEds11qA --- layout/base/RestyleManager.cpp | 7 +-- ...filter-containing-block-dynamic-1-ref.html | 19 ------- .../filter-containing-block-dynamic-1a.html | 47 ------------------ .../filter-containing-block-dynamic-1b.html | 47 ------------------ .../w3c-css/submitted/filters/reftest.list | 2 - .../reftests/w3c-css/submitted/reftest.list | 5 +- .../containing-block-dynamic-1-ref.html | 19 ------- ...rspective-containing-block-dynamic-1a.html | 47 ------------------ ...rspective-containing-block-dynamic-1b.html | 47 ------------------ .../w3c-css/submitted/transforms/reftest.list | 4 -- ...transform-containing-block-dynamic-1a.html | 49 ------------------- ...transform-containing-block-dynamic-1b.html | 49 ------------------- layout/style/nsStyleStruct.cpp | 10 ---- layout/style/nsStyleStruct.h | 10 ++-- layout/style/nsStyleStructInlines.h | 4 +- 15 files changed, 10 insertions(+), 356 deletions(-) delete mode 100644 layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1-ref.html delete mode 100644 layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1a.html delete mode 100644 layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1b.html delete mode 100644 layout/reftests/w3c-css/submitted/filters/reftest.list delete mode 100644 layout/reftests/w3c-css/submitted/transforms/containing-block-dynamic-1-ref.html delete mode 100644 layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html delete mode 100644 layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html delete mode 100644 layout/reftests/w3c-css/submitted/transforms/reftest.list delete mode 100644 layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1a.html delete mode 100644 layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1b.html diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 03f46caf2d73..2ca84cba5146 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -776,9 +776,10 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) // that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up // to ancestors (i.e. it can't be an inherited change hint). if (cont->IsAbsPosContaininingBlock()) { - if (cont->StyleDisplay()->HasTransform(cont)) { - cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED); - } + // If a transform has been added, we'll be taking this path, + // but we may be taking this path even if a transform has been + // removed. It's OK to add the bit even if it's not needed. + cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED); if (!cont->IsAbsoluteContainer() && (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) { cont->MarkAsAbsoluteContainingBlock(); diff --git a/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1-ref.html b/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1-ref.html deleted file mode 100644 index b45b8f7d45e7..000000000000 --- a/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1-ref.html +++ /dev/null @@ -1,19 +0,0 @@ - - -CSS filters: Creating containing block for fixed positioned elements - - - - -
- diff --git a/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1a.html b/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1a.html deleted file mode 100644 index d025f713538c..000000000000 --- a/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1a.html +++ /dev/null @@ -1,47 +0,0 @@ - - -CSS filters: Creating containing block for fixed positioned elements - - - - - - - - -
-
-
-
- - diff --git a/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1b.html b/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1b.html deleted file mode 100644 index 72ba0f566095..000000000000 --- a/layout/reftests/w3c-css/submitted/filters/filter-containing-block-dynamic-1b.html +++ /dev/null @@ -1,47 +0,0 @@ - - -CSS filters: Creating containing block for fixed positioned elements - - - - - - - - -
-
-
-
- - diff --git a/layout/reftests/w3c-css/submitted/filters/reftest.list b/layout/reftests/w3c-css/submitted/filters/reftest.list deleted file mode 100644 index 36b5451867bd..000000000000 --- a/layout/reftests/w3c-css/submitted/filters/reftest.list +++ /dev/null @@ -1,2 +0,0 @@ -== filter-containing-block-dynamic-1a.html filter-containing-block-dynamic-1-ref.html -== filter-containing-block-dynamic-1b.html filter-containing-block-dynamic-1-ref.html diff --git a/layout/reftests/w3c-css/submitted/reftest.list b/layout/reftests/w3c-css/submitted/reftest.list index d11b480e32e1..290b9eef3e01 100644 --- a/layout/reftests/w3c-css/submitted/reftest.list +++ b/layout/reftests/w3c-css/submitted/reftest.list @@ -19,9 +19,6 @@ include css21/reftest.list # Conditional Rules Level 3 include conditional3/reftest.list -# Filter Effects Module -include filters/reftest.list - # Flexible Box Layout Module include flexbox/reftest.list @@ -56,7 +53,7 @@ include text3/reftest.list include text-decor-3/reftest.list # Transforms -include transforms/reftest.list +# include transforms/reftest.list # Transitions # include transitions/reftest.list diff --git a/layout/reftests/w3c-css/submitted/transforms/containing-block-dynamic-1-ref.html b/layout/reftests/w3c-css/submitted/transforms/containing-block-dynamic-1-ref.html deleted file mode 100644 index cf325d0c5365..000000000000 --- a/layout/reftests/w3c-css/submitted/transforms/containing-block-dynamic-1-ref.html +++ /dev/null @@ -1,19 +0,0 @@ - - -CSS transforms: Creating containing block for fixed positioned elements - - - - -
- diff --git a/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html b/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html deleted file mode 100644 index 31200fa35861..000000000000 --- a/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1a.html +++ /dev/null @@ -1,47 +0,0 @@ - - -CSS transforms: Creating containing block for fixed positioned elements - - - - - - - - -
-
-
-
- - diff --git a/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html b/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html deleted file mode 100644 index b5f34d9a78a0..000000000000 --- a/layout/reftests/w3c-css/submitted/transforms/perspective-containing-block-dynamic-1b.html +++ /dev/null @@ -1,47 +0,0 @@ - - -CSS transforms: Creating containing block for fixed positioned elements - - - - - - - - -
-
-
-
- - diff --git a/layout/reftests/w3c-css/submitted/transforms/reftest.list b/layout/reftests/w3c-css/submitted/transforms/reftest.list deleted file mode 100644 index c861c7bd18b6..000000000000 --- a/layout/reftests/w3c-css/submitted/transforms/reftest.list +++ /dev/null @@ -1,4 +0,0 @@ -== transform-containing-block-dynamic-1a.html containing-block-dynamic-1-ref.html -== transform-containing-block-dynamic-1b.html containing-block-dynamic-1-ref.html -== perspective-containing-block-dynamic-1a.html containing-block-dynamic-1-ref.html -== perspective-containing-block-dynamic-1b.html containing-block-dynamic-1-ref.html diff --git a/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1a.html b/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1a.html deleted file mode 100644 index 601680616d29..000000000000 --- a/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1a.html +++ /dev/null @@ -1,49 +0,0 @@ - - -CSS transforms: Creating containing block for fixed positioned elements - - - - - - - - - -
-
-
-
- - diff --git a/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1b.html b/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1b.html deleted file mode 100644 index 7247ab0c97bd..000000000000 --- a/layout/reftests/w3c-css/submitted/transforms/transform-containing-block-dynamic-1b.html +++ /dev/null @@ -1,49 +0,0 @@ - - -CSS transforms: Creating containing block for fixed positioned elements - - - - - - - - - -
-
-
-
- - diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index c037e0964861..a019e79b3f1c 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1305,11 +1305,6 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons { nsChangeHint hint = nsChangeHint(0); - if (HasFilters() != aOther.HasFilters()) { - // A change from/to being a containing block for position:fixed. - NS_UpdateHint(hint, nsChangeHint_AddOrRemoveTransform); - } - if (mClipPath != aOther.mClipPath || !EqualURIs(mMask, aOther.mMask) || mFilters != aOther.mFilters) { @@ -2852,11 +2847,6 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const break; } - if (HasPerspectiveStyle() != aOther.HasPerspectiveStyle()) { - // A change from/to being a containing block for position:fixed. - NS_UpdateHint(hint, nsChangeHint_AddOrRemoveTransform); - } - if (mChildPerspective != aOther.mChildPerspective || mTransformStyle != aOther.mTransformStyle || mTransformBox != aOther.mTransformBox) diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 3ffb1eaf0da1..15e59f13ae23 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2133,7 +2133,7 @@ struct nsStyleDisplay { uint8_t mTransformBox; // [reset] see nsStyleConsts.h nsRefPtr mSpecifiedTransform; // [reset] nsStyleCoord mTransformOrigin[3]; // [reset] percent, coord, calc, 3rd param is coord, calc only - nsStyleCoord mChildPerspective; // [reset] none, coord + nsStyleCoord mChildPerspective; // [reset] coord nsStyleCoord mPerspectiveOrigin[2]; // [reset] percent, coord, calc nsAutoTArray mTransitions; // [reset] @@ -3163,10 +3163,8 @@ struct nsStyleSVGReset { nsChangeHint CalcDifference(const nsStyleSVGReset& aOther) const; static nsChangeHint MaxDifference() { - return nsChangeHint_UpdateEffects | - nsChangeHint_UpdateOverflow | - nsChangeHint_AddOrRemoveTransform | - NS_STYLE_HINT_REFLOW; + return NS_CombineHint(nsChangeHint_UpdateEffects, + NS_CombineHint(nsChangeHint_UpdateOverflow, NS_STYLE_HINT_REFLOW)); } static nsChangeHint DifferenceAlwaysHandledForDescendants() { // CalcDifference never returns the reflow hints that are sometimes @@ -3177,7 +3175,7 @@ struct nsStyleSVGReset { } bool HasFilters() const { - return !mFilters.IsEmpty(); + return mFilters.Length() > 0; } bool HasNonScalingStroke() const { diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h index e2da9c5d5844..3883d7ceb1f0 100644 --- a/layout/style/nsStyleStructInlines.h +++ b/layout/style/nsStyleStructInlines.h @@ -138,10 +138,8 @@ nsStyleDisplay::HasTransform(const nsIFrame* aContextFrame) const bool nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const { - NS_ASSERTION(aContextFrame->StyleDisplay() == this, - "unexpected aContextFrame"); return (HasTransform(aContextFrame) || HasPerspectiveStyle() || - aContextFrame->StyleSVGReset()->HasFilters()) && + !aContextFrame->StyleSVGReset()->mFilters.IsEmpty()) && !aContextFrame->IsSVGText(); } From 329cd406f90098cf7e398aae6a09cb0d8fdbbe52 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 3 Aug 2015 15:15:30 +0900 Subject: [PATCH 102/113] Bug 1187566 TSFTextStore::Content should compute mMinTextModified Offset only with the latest composition string and original composition string, and also the hack should be enabled on Win10 r=emk --- widget/windows/TSFTextStore.cpp | 47 +++++++++++++++++++++++++-------- widget/windows/TSFTextStore.h | 12 +++++++++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/widget/windows/TSFTextStore.cpp b/widget/windows/TSFTextStore.cpp index 21e3009fc65f..aa6b5cfe8d7d 100644 --- a/widget/windows/TSFTextStore.cpp +++ b/widget/windows/TSFTextStore.cpp @@ -1969,8 +1969,11 @@ TSFTextStore::LockedContent() MOZ_LOG(sTextStoreLog, LogLevel::Debug, ("TSF: 0x%p TSFTextStore::LockedContent(): " - "mLockedContent={ mText.Length()=%d }", - this, mLockedContent.Text().Length())); + "mLockedContent={ mText.Length()=%d, mLastCompositionString=\"%s\", " + "mMinTextModifiedOffset=%u }", + this, mLockedContent.Text().Length(), + NS_ConvertUTF16toUTF8(mLockedContent.LastCompositionString()).get(), + mLockedContent.MinTextModifiedOffset())); return mLockedContent; } @@ -3399,7 +3402,9 @@ TSFTextStore::GetTextExt(TsViewCookie vcView, if (mComposition.IsComposing() && mComposition.mStart < acpEnd && mLockedContent.IsLayoutChangedAfter(acpEnd)) { const Selection& currentSel = CurrentSelection(); - if (!IsWin10OrLater()) { + if ((sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar || + sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret) && + kSink->IsGoogleJapaneseInputActive()) { // Google Japanese Input doesn't handle ITfContextView::GetTextExt() // properly due to the same bug of TSF mentioned above. Google Japanese // Input calls this twice for the first character of changing range of @@ -3407,10 +3412,9 @@ TSFTextStore::GetTextExt(TsViewCookie vcView, // composition string. The formar is used for showing candidate window. // This is typically shown at wrong position. We should avoid only this // case. This is not necessary on Windows 10. - if (!mLockedContent.IsLayoutChangedAfter(acpStart) && - acpStart < acpEnd && - sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar && - kSink->IsGoogleJapaneseInputActive()) { + if (sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar && + !mLockedContent.IsLayoutChangedAfter(acpStart) && + acpStart < acpEnd) { acpEnd = acpStart; MOZ_LOG(sTextStoreLog, LogLevel::Debug, ("TSF: 0x%p TSFTextStore::GetTextExt() hacked the offsets of " @@ -3423,10 +3427,9 @@ TSFTextStore::GetTextExt(TsViewCookie vcView, // offset of selected clause. However, it's difficult to get where is // selected clause for now. Instead, we should use the first character // which is modified. This is useful in most cases. - else if (acpStart == acpEnd && - currentSel.IsCollapsed() && currentSel.EndOffset() == acpEnd && - sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret && - kSink->IsGoogleJapaneseInputActive()) { + else if (sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret && + acpStart == acpEnd && + currentSel.IsCollapsed() && currentSel.EndOffset() == acpEnd) { acpEnd = acpStart = mLockedContent.MinOffsetOfLayoutChanged(); MOZ_LOG(sTextStoreLog, LogLevel::Debug, ("TSF: 0x%p TSFTextStore::GetTextExt() hacked the offsets of " @@ -5311,7 +5314,29 @@ TSFTextStore::Content::ReplaceTextWith(LONG aStart, mComposition.mStart + FirstDifferentCharOffset(mComposition.mString, mLastCompositionString); + // The previous change to the composition string is canceled. + if (mMinTextModifiedOffset >= + static_cast(mComposition.mStart) && + mMinTextModifiedOffset < firstDifferentOffset) { + mMinTextModifiedOffset = firstDifferentOffset; + } + } else if (mMinTextModifiedOffset >= + static_cast(mComposition.mStart) && + mMinTextModifiedOffset < + static_cast(mComposition.EndOffset())) { + // The previous change to the composition string is canceled. + mMinTextModifiedOffset = firstDifferentOffset = + mComposition.EndOffset(); } + MOZ_LOG(sTextStoreLog, LogLevel::Debug, + ("TSF: 0x%p TSFTextStore::Content::ReplaceTextWith(aStart=%d, " + "aLength=%d, aReplaceString=\"%s\"), mComposition={ mStart=%d, " + "mString=\"%s\" }, mLastCompositionString=\"%s\", " + "mMinTextModifiedOffset=%u, firstDifferentOffset=%u", + this, aStart, aLength, NS_ConvertUTF16toUTF8(aReplaceString).get(), + mComposition.mStart, NS_ConvertUTF16toUTF8(mComposition.mString).get(), + NS_ConvertUTF16toUTF8(mLastCompositionString).get(), + mMinTextModifiedOffset, firstDifferentOffset)); } else { firstDifferentOffset = static_cast(aStart) + diff --git a/widget/windows/TSFTextStore.h b/widget/windows/TSFTextStore.h index 7b7892e53180..1a0aff977eff 100644 --- a/widget/windows/TSFTextStore.h +++ b/widget/windows/TSFTextStore.h @@ -621,6 +621,8 @@ protected: mText = aText; if (mComposition.IsComposing()) { mLastCompositionString = mComposition.mString; + } else { + mLastCompositionString.Truncate(); } mMinTextModifiedOffset = NOT_MODIFIED; mInitialized = true; @@ -647,6 +649,16 @@ protected: MOZ_ASSERT(mInitialized); return mText; } + const nsString& LastCompositionString() const + { + MOZ_ASSERT(mInitialized); + return mLastCompositionString; + } + uint32_t MinTextModifiedOffset() const + { + MOZ_ASSERT(mInitialized); + return mMinTextModifiedOffset; + } // Returns true if layout of the character at the aOffset has not been // calculated. From 4fcbf798173ee586d627a0bfd95c0b83c719b671 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 3 Aug 2015 15:15:30 +0900 Subject: [PATCH 103/113] Bug 1050644 part.1 Add methods to check whether the active TIP is Chinese TIP which deosn't show candidate window in e10s mode r=emk --- widget/windows/TSFTextStore.cpp | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/widget/windows/TSFTextStore.cpp b/widget/windows/TSFTextStore.cpp index aa6b5cfe8d7d..346f76b3b12f 100644 --- a/widget/windows/TSFTextStore.cpp +++ b/widget/windows/TSFTextStore.cpp @@ -807,6 +807,10 @@ public: bool EnsureInitActiveTIPKeyboard(); + /**************************************************************************** + * Japanese TIP + ****************************************************************************/ + // Note that TIP name may depend on the language of the environment. // For example, some TIP may use localized name for its target language // environment but English name for the others. @@ -824,6 +828,28 @@ public: NS_LITERAL_STRING("ATOK ")); } + /**************************************************************************** + * Traditional Chinese TIP + ****************************************************************************/ + + bool IsMSChangJieActive() const + { + return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft ChangJie") || + mActiveTIPKeyboardDescription.Equals( + NS_LITERAL_STRING("\x5FAE\x8F6F\x4ED3\x9889")) || + mActiveTIPKeyboardDescription.Equals( + NS_LITERAL_STRING("\x5FAE\x8EDF\x5009\x9821")); + } + + bool IsMSQuickQuickActive() const + { + return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft Quick") || + mActiveTIPKeyboardDescription.Equals( + NS_LITERAL_STRING("\x5FAE\x8F6F\x901F\x6210")) || + mActiveTIPKeyboardDescription.Equals( + NS_LITERAL_STRING("\x5FAE\x8EDF\x901F\x6210")); + } + bool IsFreeChangJieActive() const { // FYI: The TIP name is misspelled... @@ -838,6 +864,28 @@ public: "\x4E2D\x6587 (\x7E41\x9AD4) - \x6613\x9821\x8F38\x5165\x6CD5")); } + /**************************************************************************** + * Simplified Chinese TIP + ****************************************************************************/ + + bool IsMSPinyinActive() const + { + return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft Pinyin") || + mActiveTIPKeyboardDescription.Equals( + NS_LITERAL_STRING("\x5FAE\x8F6F\x62FC\x97F3")) || + mActiveTIPKeyboardDescription.Equals( + NS_LITERAL_STRING("\x5FAE\x8EDF\x62FC\x97F3")); + } + + bool IsMSWubiActive() const + { + return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft Wubi") || + mActiveTIPKeyboardDescription.Equals( + NS_LITERAL_STRING("\x5FAE\x8F6F\x4E94\x7B14")) || + mActiveTIPKeyboardDescription.Equals( + NS_LITERAL_STRING("\x5FAE\x8EDF\x4E94\x7B46")); + } + public: // ITfActiveLanguageProfileNotifySink STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile, BOOL fActivated); From e0bbfb1c3216ac3f562777f12abe7d3fc7602a03 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 3 Aug 2015 15:15:30 +0900 Subject: [PATCH 104/113] Bug 1050644 part.2 Hack ITextStoreACP::GetTextExt()'s offset for some Chinese TIPs of MS r=emk --- modules/libpref/init/all.js | 4 ++++ widget/windows/TSFTextStore.cpp | 25 +++++++++++++++++++++++++ widget/windows/TSFTextStore.h | 2 ++ 3 files changed, 31 insertions(+) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 23c742e38094..68e1793c49db 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3155,6 +3155,10 @@ pref("intl.tsf.hack.atok.create_native_caret", true); // composition start offset. // For Free ChangJie 2010 pref("intl.tsf.hack.free_chang_jie.do_not_return_no_layout_error", true); +// For Microsoft Pinyin and Microsoft Wubi +pref("intl.tsf.hack.ms_simplified_chinese.do_not_return_no_layout_error", true); +// For Microsoft ChangJie and Microsoft Quick +pref("intl.tsf.hack.ms_traditional_chinese.do_not_return_no_layout_error", true); // For Easy Changjei pref("intl.tsf.hack.easy_changjei.do_not_return_no_layout_error", true); // Whether use previous character rect for the result of diff --git a/widget/windows/TSFTextStore.cpp b/widget/windows/TSFTextStore.cpp index 346f76b3b12f..0a85e9644e77 100644 --- a/widget/windows/TSFTextStore.cpp +++ b/widget/windows/TSFTextStore.cpp @@ -1281,6 +1281,8 @@ StaticRefPtr TSFTextStore::sEnabledTextStore; DWORD TSFTextStore::sClientId = 0; bool TSFTextStore::sCreateNativeCaretForATOK = false; +bool TSFTextStore::sDoNotReturnNoLayoutErrorToMSSimplifiedTIP = false; +bool TSFTextStore::sDoNotReturnNoLayoutErrorToMSTraditionalTIP = false; bool TSFTextStore::sDoNotReturnNoLayoutErrorToFreeChangJie = false; bool TSFTextStore::sDoNotReturnNoLayoutErrorToEasyChangjei = false; bool TSFTextStore::sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar = false; @@ -3499,6 +3501,21 @@ TSFTextStore::GetTextExt(TsViewCookie vcView, ("TSF: 0x%p TSFTextStore::GetTextExt() hacked the offsets for " "TIP acpStart=%d, acpEnd=%d", this, acpStart, acpEnd)); } + // Some Chinese TIPs of Microsoft doesn't show candidate window in e10s + // mode on Win8 or later. + else if (IsWin8OrLater() && + ((sDoNotReturnNoLayoutErrorToMSTraditionalTIP && + (kSink->IsMSChangJieActive() || + kSink->IsMSQuickQuickActive())) || + (sDoNotReturnNoLayoutErrorToMSSimplifiedTIP && + (kSink->IsMSPinyinActive() || + kSink->IsMSWubiActive())))) { + acpEnd = mComposition.mStart; + acpStart = std::min(acpStart, acpEnd); + MOZ_LOG(sTextStoreLog, LogLevel::Debug, + ("TSF: 0x%p TSFTextStore::GetTextExt() hacked the offsets for " + "TIP acpStart=%d, acpEnd=%d", this, acpStart, acpEnd)); + } } if (mLockedContent.IsLayoutChangedAfter(acpEnd)) { @@ -5151,6 +5168,14 @@ TSFTextStore::Initialize() sCreateNativeCaretForATOK = Preferences::GetBool("intl.tsf.hack.atok.create_native_caret", true); + sDoNotReturnNoLayoutErrorToMSSimplifiedTIP = + Preferences::GetBool( + "intl.tsf.hack.ms_simplified_chinese.do_not_return_no_layout_error", + true); + sDoNotReturnNoLayoutErrorToMSTraditionalTIP = + Preferences::GetBool( + "intl.tsf.hack.ms_traditional_chinese.do_not_return_no_layout_error", + true); sDoNotReturnNoLayoutErrorToFreeChangJie = Preferences::GetBool( "intl.tsf.hack.free_chang_jie.do_not_return_no_layout_error", true); diff --git a/widget/windows/TSFTextStore.h b/widget/windows/TSFTextStore.h index 1a0aff977eff..410f2615ef0c 100644 --- a/widget/windows/TSFTextStore.h +++ b/widget/windows/TSFTextStore.h @@ -835,6 +835,8 @@ protected: // Enables/Disables hack for specific TIP. static bool sCreateNativeCaretForATOK; + static bool sDoNotReturnNoLayoutErrorToMSSimplifiedTIP; + static bool sDoNotReturnNoLayoutErrorToMSTraditionalTIP; static bool sDoNotReturnNoLayoutErrorToFreeChangJie; static bool sDoNotReturnNoLayoutErrorToEasyChangjei; static bool sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar; From 6f089c96294997ef234020d4653eb0f8d15163d6 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Mon, 3 Aug 2015 07:58:43 +0100 Subject: [PATCH 105/113] Bug 1189352: Fix NSPR_LOG_FILE with absolute paths for sandboxed child on Windows. r=bbondy --- ipc/glue/GeckoChildProcessHost.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index e90f521298fe..1441cc07a660 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -564,26 +564,27 @@ MaybeAddNsprLogFileAccess(std::vector& aAllowedFilesReadWrite) return; } - nsCOMPtr file; - nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, - getter_AddRefs(file)); - if (NS_FAILED(rv) || !file) { - NS_WARNING("Failed to get current working directory"); - return; - } - - nsDependentCString nsprLogFile(nsprLogFileEnv); - rv = file->AppendRelativeNativePath(nsprLogFile); + nsDependentCString nsprLogFilePath(nsprLogFileEnv); + nsCOMPtr nsprLogFile; + nsresult rv = NS_NewNativeLocalFile(nsprLogFilePath, true, + getter_AddRefs(nsprLogFile)); if (NS_FAILED(rv)) { - // Not a relative path, try it as an absolute one. - rv = file->InitWithNativePath(nsprLogFile); + // Not an absolute path, try it as a relative one. + nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, + getter_AddRefs(nsprLogFile)); + if (NS_FAILED(rv) || !nsprLogFile) { + NS_WARNING("Failed to get current working directory"); + return; + } + + rv = nsprLogFile->AppendRelativeNativePath(nsprLogFilePath); if (NS_WARN_IF(NS_FAILED(rv))) { return; } } nsAutoString resolvedFilePath; - rv = file->GetPath(resolvedFilePath); + rv = nsprLogFile->GetPath(resolvedFilePath); if (NS_WARN_IF(NS_FAILED(rv))) { return; } From 03d48d195136612705d7645b90a4e42bb8ffa7f4 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 3 Aug 2015 09:37:54 +0200 Subject: [PATCH 106/113] Backed out changeset 4e3821b236f9 (bug 1175770) --- browser/base/content/tab-content.js | 6 - browser/components/extensions/bootstrap.js | 20 - .../extensions/ext-browserAction.js | 326 ---------- .../components/extensions/ext-contextMenus.js | 8 - browser/components/extensions/ext-tabs.js | 486 --------------- browser/components/extensions/ext-utils.js | 324 ---------- browser/components/extensions/ext-windows.js | 151 ----- browser/components/extensions/extension.svg | 19 - browser/components/extensions/jar.mn | 11 - browser/components/extensions/moz.build | 7 - browser/components/extensions/prepare.py | 83 --- browser/components/moz.build | 1 - browser/components/nsBrowserGlue.js | 9 - browser/modules/E10SUtils.jsm | 9 - browser/themes/linux/browser.css | 4 - browser/themes/osx/browser.css | 4 - browser/themes/windows/browser.css | 4 - toolkit/components/extensions/Extension.jsm | 578 ------------------ .../extensions/ExtensionContent.jsm | 521 ---------------- .../extensions/ExtensionManagement.jsm | 201 ------ .../extensions/ExtensionStorage.jsm | 149 ----- .../components/extensions/ExtensionUtils.jsm | 540 ---------------- toolkit/components/extensions/ext-alarms.js | 168 ----- .../extensions/ext-backgroundPage.js | 92 --- .../components/extensions/ext-extension.js | 10 - toolkit/components/extensions/ext-i18n.js | 9 - toolkit/components/extensions/ext-idle.js | 9 - .../extensions/ext-notifications.js | 140 ----- toolkit/components/extensions/ext-runtime.js | 47 -- toolkit/components/extensions/ext-storage.js | 48 -- .../extensions/ext-webNavigation.js | 70 --- .../components/extensions/ext-webRequest.js | 104 ---- toolkit/components/extensions/jar.mn | 16 - toolkit/components/extensions/moz.build | 15 - toolkit/components/moz.build | 1 - toolkit/components/utils/simpleServices.js | 6 +- toolkit/modules/Locale.jsm | 93 --- toolkit/modules/addons/MatchPattern.jsm | 13 +- toolkit/modules/moz.build | 1 - .../extensions/internal/XPIProvider.jsm | 84 ++- 40 files changed, 84 insertions(+), 4303 deletions(-) delete mode 100644 browser/components/extensions/bootstrap.js delete mode 100644 browser/components/extensions/ext-browserAction.js delete mode 100644 browser/components/extensions/ext-contextMenus.js delete mode 100644 browser/components/extensions/ext-tabs.js delete mode 100644 browser/components/extensions/ext-utils.js delete mode 100644 browser/components/extensions/ext-windows.js delete mode 100644 browser/components/extensions/extension.svg delete mode 100644 browser/components/extensions/jar.mn delete mode 100644 browser/components/extensions/moz.build delete mode 100644 browser/components/extensions/prepare.py delete mode 100644 toolkit/components/extensions/Extension.jsm delete mode 100644 toolkit/components/extensions/ExtensionContent.jsm delete mode 100644 toolkit/components/extensions/ExtensionManagement.jsm delete mode 100644 toolkit/components/extensions/ExtensionStorage.jsm delete mode 100644 toolkit/components/extensions/ExtensionUtils.jsm delete mode 100644 toolkit/components/extensions/ext-alarms.js delete mode 100644 toolkit/components/extensions/ext-backgroundPage.js delete mode 100644 toolkit/components/extensions/ext-extension.js delete mode 100644 toolkit/components/extensions/ext-i18n.js delete mode 100644 toolkit/components/extensions/ext-idle.js delete mode 100644 toolkit/components/extensions/ext-notifications.js delete mode 100644 toolkit/components/extensions/ext-runtime.js delete mode 100644 toolkit/components/extensions/ext-storage.js delete mode 100644 toolkit/components/extensions/ext-webNavigation.js delete mode 100644 toolkit/components/extensions/ext-webRequest.js delete mode 100644 toolkit/components/extensions/jar.mn delete mode 100644 toolkit/components/extensions/moz.build delete mode 100644 toolkit/modules/Locale.jsm diff --git a/browser/base/content/tab-content.js b/browser/base/content/tab-content.js index 75ede343ec1a..ea7179586b41 100644 --- a/browser/base/content/tab-content.js +++ b/browser/base/content/tab-content.js @@ -9,7 +9,6 @@ let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/ExtensionContent.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils", "resource:///modules/E10SUtils.jsm"); @@ -657,8 +656,3 @@ let DOMFullscreenHandler = { } }; DOMFullscreenHandler.init(); - -ExtensionContent.init(this); -addEventListener("unload", () => { - ExtensionContent.uninit(this); -}); diff --git a/browser/components/extensions/bootstrap.js b/browser/components/extensions/bootstrap.js deleted file mode 100644 index 19a648bb7274..000000000000 --- a/browser/components/extensions/bootstrap.js +++ /dev/null @@ -1,20 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -Components.utils.import("resource://gre/modules/Extension.jsm"); - -let extension; - -function startup(data, reason) -{ - extension = new Extension(data); - extension.startup(); -} - -function shutdown(data, reason) -{ - extension.shutdown(); -} diff --git a/browser/components/extensions/ext-browserAction.js b/browser/components/extensions/ext-browserAction.js deleted file mode 100644 index f29cd2038a47..000000000000 --- a/browser/components/extensions/ext-browserAction.js +++ /dev/null @@ -1,326 +0,0 @@ -XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI", - "resource:///modules/CustomizableUI.jsm"); - -Cu.import("resource://gre/modules/devtools/event-emitter.js"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - EventManager, - DefaultWeakMap, - ignoreEvent, - runSafe, -} = ExtensionUtils; - -// WeakMap[Extension -> BrowserAction] -let browserActionMap = new WeakMap(); - -function browserActionOf(extension) -{ - return browserActionMap.get(extension); -} - -function makeWidgetId(id) -{ - id = id.toLowerCase(); - return id.replace(/[^a-z0-9_-]/g, "_"); -} - -let nextActionId = 0; - -// Responsible for the browser_action section of the manifest as well -// as the associated popup. -function BrowserAction(options, extension) -{ - this.extension = extension; - this.id = makeWidgetId(extension.id) + "-browser-action"; - this.widget = null; - - this.title = new DefaultWeakMap(extension.localize(options.default_title)); - this.badgeText = new DefaultWeakMap(); - this.badgeBackgroundColor = new DefaultWeakMap(); - this.icon = new DefaultWeakMap(options.default_icon); - this.popup = new DefaultWeakMap(options.default_popup); - - // Make the default something that won't compare equal to anything. - this.prevPopups = new DefaultWeakMap({}); - - this.context = null; -} - -BrowserAction.prototype = { - build() { - let widget = CustomizableUI.createWidget({ - id: this.id, - type: "custom", - removable: true, - defaultArea: CustomizableUI.AREA_NAVBAR, - onBuild: document => { - let node = document.createElement("toolbarbutton"); - node.id = this.id; - node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional badged-button"); - node.setAttribute("constrain-size", "true"); - - this.updateTab(null, node); - - let tabbrowser = document.defaultView.gBrowser; - tabbrowser.ownerDocument.addEventListener("TabSelect", () => { - this.updateTab(tabbrowser.selectedTab, node); - }); - - node.addEventListener("command", event => { - if (node.getAttribute("type") != "panel") { - this.emit("click"); - } - }); - - return node; - }, - }); - this.widget = widget; - }, - - // Initialize the toolbar icon and popup given that |tab| is the - // current tab and |node| is the CustomizableUI node. Note: |tab| - // will be null if we don't know the current tab yet (during - // initialization). - updateTab(tab, node) { - let window = node.ownerDocument.defaultView; - - let title = this.getProperty(tab, "title"); - if (title) { - node.setAttribute("tooltiptext", title); - node.setAttribute("label", title); - } else { - node.removeAttribute("tooltiptext"); - node.removeAttribute("label"); - } - - let badgeText = this.badgeText.get(tab); - if (badgeText) { - node.setAttribute("badge", badgeText); - } else { - node.removeAttribute("badge"); - } - - function toHex(n) { - return Math.floor(n / 16).toString(16) + (n % 16).toString(16); - } - - let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node, - 'class', 'toolbarbutton-badge'); - if (badgeNode) { - let color = this.badgeBackgroundColor.get(tab); - if (Array.isArray(color)) { - color = `rgb(${color[0]}, ${color[1]}, ${color[2]})`; - } - badgeNode.style.backgroundColor = color; - } - - let iconURL = this.getIcon(tab, node); - node.setAttribute("image", iconURL); - - let popup = this.getProperty(tab, "popup"); - - if (popup != this.prevPopups.get(window)) { - this.prevPopups.set(window, popup); - - let panel = node.querySelector("panel"); - if (panel) { - panel.remove(); - } - - if (popup) { - let popupURL = this.extension.baseURI.resolve(popup); - node.setAttribute("type", "panel"); - - let document = node.ownerDocument; - let panel = document.createElement("panel"); - panel.setAttribute("class", "browser-action-panel"); - panel.setAttribute("type", "arrow"); - panel.setAttribute("flip", "slide"); - node.appendChild(panel); - - let browser = document.createElementNS(XUL_NS, "browser"); - browser.setAttribute("type", "content"); - browser.setAttribute("disableglobalhistory", "true"); - browser.setAttribute("width", "500"); - browser.setAttribute("height", "500"); - panel.appendChild(browser); - - let loadListener = () => { - panel.removeEventListener("load", loadListener); - - if (this.context) { - this.context.unload(); - } - - this.context = new ExtensionPage(this.extension, { - type: "popup", - contentWindow: browser.contentWindow, - uri: Services.io.newURI(popupURL, null, null), - docShell: browser.docShell, - }); - GlobalManager.injectInDocShell(browser.docShell, this.extension, this.context); - browser.setAttribute("src", popupURL); - }; - panel.addEventListener("load", loadListener); - } else { - node.removeAttribute("type"); - } - } - }, - - // Note: tab is allowed to be null here. - getIcon(tab, node) { - let icon = this.icon.get(tab); - - let url; - if (typeof(icon) != "object") { - url = icon; - } else { - let window = node.ownerDocument.defaultView; - let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindowUtils); - let res = {value: 1} - utils.getResolution(res); - - let size = res.value == 1 ? 19 : 38; - url = icon[size]; - } - - if (url) { - return this.extension.baseURI.resolve(url); - } else { - return "chrome://browser/content/extension.svg"; - } - }, - - // Update the toolbar button for a given window. - updateWindow(window) { - let tab = window.gBrowser ? window.gBrowser.selectedTab : null; - let node = CustomizableUI.getWidget(this.id).forWindow(window).node; - this.updateTab(tab, node); - }, - - // Update the toolbar button when the extension changes the icon, - // title, badge, etc. If it only changes a parameter for a single - // tab, |tab| will be that tab. Otherwise it will be null. - updateOnChange(tab) { - if (tab) { - if (tab.selected) { - this.updateWindow(tab.ownerDocument.defaultView); - } - } else { - let e = Services.wm.getEnumerator("navigator:browser"); - while (e.hasMoreElements()) { - let window = e.getNext(); - if (window.gBrowser) { - this.updateWindow(window); - } - } - } - }, - - // tab is allowed to be null. - // prop should be one of "icon", "title", "badgeText", "popup", or "badgeBackgroundColor". - setProperty(tab, prop, value) { - this[prop].set(tab, value); - this.updateOnChange(tab); - }, - - // tab is allowed to be null. - // prop should be one of "title", "badgeText", "popup", or "badgeBackgroundColor". - getProperty(tab, prop) { - return this[prop].get(tab); - }, - - shutdown() { - CustomizableUI.destroyWidget(this.id); - }, -}; - -EventEmitter.decorate(BrowserAction.prototype); - -extensions.on("manifest_browser_action", (type, directive, extension, manifest) => { - let browserAction = new BrowserAction(manifest.browser_action, extension); - browserAction.build(); - browserActionMap.set(extension, browserAction); -}); - -extensions.on("shutdown", (type, extension) => { - if (browserActionMap.has(extension)) { - browserActionMap.get(extension).shutdown(); - browserActionMap.delete(extension); - } -}); - -extensions.registerAPI((extension, context) => { - return { - browserAction: { - onClicked: new EventManager(context, "browserAction.onClicked", fire => { - let listener = () => { - let tab = TabManager.activeTab; - fire(TabManager.convert(extension, tab)); - }; - browserActionOf(extension).on("click", listener); - return () => { - browserActionOf(extension).off("click", listener); - }; - }).api(), - - setTitle: function(details) { - let tab = details.tabId ? TabManager.getTab(details.tabId) : null; - browserActionOf(extension).setProperty(tab, "title", details.title); - }, - - getTitle: function(details, callback) { - let tab = details.tabId ? TabManager.getTab(details.tabId) : null; - let title = browserActionOf(extension).getProperty(tab, "title"); - runSafe(context, callback, title); - }, - - setIcon: function(details, callback) { - let tab = details.tabId ? TabManager.getTab(details.tabId) : null; - if (details.imageData) { - // FIXME: Support the imageData attribute. - return; - } - browserActionOf(extension).setProperty(tab, "icon", details.path); - }, - - setBadgeText: function(details) { - let tab = details.tabId ? TabManager.getTab(details.tabId) : null; - browserActionOf(extension).setProperty(tab, "badgeText", details.text); - }, - - getBadgeText: function(details, callback) { - let tab = details.tabId ? TabManager.getTab(details.tabId) : null; - let text = browserActionOf(extension).getProperty(tab, "badgeText"); - runSafe(context, callback, text); - }, - - setPopup: function(details) { - let tab = details.tabId ? TabManager.getTab(details.tabId) : null; - browserActionOf(extension).setProperty(tab, "popup", details.popup); - }, - - getPopup: function(details, callback) { - let tab = details.tabId ? TabManager.getTab(details.tabId) : null; - let popup = browserActionOf(extension).getProperty(tab, "popup"); - runSafe(context, callback, popup); - }, - - setBadgeBackgroundColor: function(details) { - let color = details.color; - let tab = details.tabId ? TabManager.getTab(details.tabId) : null; - browserActionOf(extension).setProperty(tab, "badgeBackgroundColor", details.color); - }, - - getBadgeBackgroundColor: function(details, callback) { - let tab = details.tabId ? TabManager.getTab(details.tabId) : null; - let color = browserActionOf(extension).getProperty(tab, "badgeBackgroundColor"); - runSafe(context, callback, color); - }, - } - }; -}); diff --git a/browser/components/extensions/ext-contextMenus.js b/browser/components/extensions/ext-contextMenus.js deleted file mode 100644 index 671318ab322e..000000000000 --- a/browser/components/extensions/ext-contextMenus.js +++ /dev/null @@ -1,8 +0,0 @@ -extensions.registerPrivilegedAPI("contextMenus", (extension, context) => { - return { - contextMenus: { - create() {}, - removeAll() {}, - }, - }; -}); diff --git a/browser/components/extensions/ext-tabs.js b/browser/components/extensions/ext-tabs.js deleted file mode 100644 index 4050cd67d2ff..000000000000 --- a/browser/components/extensions/ext-tabs.js +++ /dev/null @@ -1,486 +0,0 @@ -XPCOMUtils.defineLazyModuleGetter(this, "NewTabURL", - "resource:///modules/NewTabURL.jsm"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - EventManager, - ignoreEvent, - runSafe, -} = ExtensionUtils; - -// This function is pretty tightly tied to Extension.jsm. -// Its job is to fill in the |tab| property of the sender. -function getSender(context, target, sender) -{ - // The message was sent from a content script to a element. - // We can just get the |tab| from |target|. - if (target instanceof Ci.nsIDOMXULElement) { - // The message came from a content script. - let tabbrowser = target.ownerDocument.defaultView.gBrowser; - if (!tabbrowser) { - return; - } - let tab = tabbrowser.getTabForBrowser(target); - - sender.tab = TabManager.convert(context.extension, tab); - } else { - // The message came from an ExtensionPage. In that case, it should - // include a tabId property (which is filled in by the page-open - // listener below). - if ("tabId" in sender) { - sender.tab = TabManager.convert(context.extension, TabManager.getTab(sender.tabId)); - delete sender.tabId; - } - } -} - -// WeakMap[ExtensionPage -> {tab, parentWindow}] -let pageDataMap = new WeakMap(); - -// This listener fires whenever an extension page opens in a tab -// (either initiated by the extension or the user). Its job is to fill -// in some tab-specific details and keep data around about the -// ExtensionPage. -extensions.on("page-load", (type, page, params, sender, delegate) => { - if (params.type == "tab") { - let browser = params.docShell.chromeEventHandler; - let parentWindow = browser.ownerDocument.defaultView; - let tab = parentWindow.gBrowser.getTabForBrowser(browser); - sender.tabId = TabManager.getId(tab); - - pageDataMap.set(page, {tab, parentWindow}); - } - - delegate.getSender = getSender; -}); - -extensions.on("page-unload", (type, page) => { - pageDataMap.delete(page); -}); - -extensions.on("page-shutdown", (type, page) => { - if (pageDataMap.has(page)) { - let {tab, parentWindow} = pageDataMap.get(page); - pageDataMap.delete(page); - - parentWindow.gBrowser.removeTab(tab); - } -}); - -extensions.on("fill-browser-data", (type, browser, data, result) => { - let tabId = TabManager.getBrowserId(browser); - if (tabId == -1) { - result.cancel = true; - return; - } - - data.tabId = tabId; -}); - -// TODO: activeTab permission - -extensions.registerAPI((extension, context) => { - let self = { - tabs: { - onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => { - let tab = event.originalTarget; - let tabId = TabManager.getId(tab); - let windowId = WindowManager.getId(tab.ownerDocument.defaultView); - fire({tabId, windowId}); - }).api(), - - onCreated: new EventManager(context, "tabs.onCreated", fire => { - let listener = event => { - let tab = event.originalTarget; - fire({tab: TabManager.convert(extension, tab)}); - }; - - let windowListener = window => { - for (let tab of window.gBrowser.tabs) { - fire({tab: TabManager.convert(extension, tab)}); - } - }; - - WindowListManager.addOpenListener(windowListener, false); - AllWindowEvents.addListener("TabOpen", listener); - return () => { - WindowListManager.removeOpenListener(windowListener); - AllWindowEvents.removeListener("TabOpen", listener); - }; - }).api(), - - onUpdated: new EventManager(context, "tabs.onUpdated", fire => { - function sanitize(extension, changeInfo) { - let result = {}; - let nonempty = false; - for (let prop in changeInfo) { - if ((prop != "favIconUrl" && prop != "url") || extension.hasPermission("tabs")) { - nonempty = true; - result[prop] = changeInfo[prop]; - } - } - return [nonempty, result]; - } - - let listener = event => { - let tab = event.originalTarget; - let window = tab.ownerDocument.defaultView; - let tabId = TabManager.getId(tab); - - let changeInfo = {}; - let needed = false; - if (event.type == "TabAttrModified") { - if (event.detail.changed.indexOf("image") != -1) { - changeInfo.favIconUrl = window.gBrowser.getIcon(tab); - needed = true; - } - } else if (event.type == "TabPinned") { - changeInfo.pinned = true; - needed = true; - } else if (event.type == "TabUnpinned") { - changeInfo.pinned = false; - needed = true; - } - - [needed, changeInfo] = sanitize(extension, changeInfo); - if (needed) { - fire(tabId, changeInfo, TabManager.convert(extension, tab)); - } - }; - let progressListener = { - onStateChange(browser, webProgress, request, stateFlags, statusCode) { - if (!webProgress.isTopLevel) { - return; - } - - let status; - if (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) { - if (stateFlags & Ci.nsIWebProgressListener.STATE_START) { - status = "loading"; - } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) { - status = "complete"; - } - } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP && - statusCode == Cr.NS_BINDING_ABORTED) { - status = "complete"; - } - - let gBrowser = browser.ownerDocument.defaultView.gBrowser; - let tab = gBrowser.getTabForBrowser(browser); - let tabId = TabManager.getId(tab); - let [needed, changeInfo] = sanitize(extension, {status}); - fire(tabId, changeInfo, TabManager.convert(extension, tab)); - }, - - onLocationChange(browser, webProgress, request, locationURI, flags) { - let gBrowser = browser.ownerDocument.defaultView.gBrowser; - let tab = gBrowser.getTabForBrowser(browser); - let tabId = TabManager.getId(tab); - let [needed, changeInfo] = sanitize(extension, {url: locationURI.spec}); - if (needed) { - fire(tabId, changeInfo, TabManager.convert(extension, tab)); - } - }, - }; - - AllWindowEvents.addListener("progress", progressListener); - AllWindowEvents.addListener("TabAttrModified", listener); - AllWindowEvents.addListener("TabPinned", listener); - AllWindowEvents.addListener("TabUnpinned", listener); - return () => { - AllWindowEvents.removeListener("progress", progressListener); - AllWindowEvents.addListener("TabAttrModified", listener); - AllWindowEvents.addListener("TabPinned", listener); - AllWindowEvents.addListener("TabUnpinned", listener); - }; - }).api(), - - onReplaced: ignoreEvent(), - - onRemoved: new EventManager(context, "tabs.onRemoved", fire => { - let tabListener = event => { - let tab = event.originalTarget; - let tabId = TabManager.getId(tab); - let windowId = WindowManager.getId(tab.ownerDocument.defaultView); - let removeInfo = {windowId, isWindowClosing: false}; - fire(tabId, removeInfo); - }; - - let windowListener = window => { - for (let tab of window.gBrowser.tabs) { - let tabId = TabManager.getId(tab); - let windowId = WindowManager.getId(window); - let removeInfo = {windowId, isWindowClosing: true}; - fire(tabId, removeInfo); - } - }; - - WindowListManager.addCloseListener(windowListener); - AllWindowEvents.addListener("TabClose", tabListener); - return () => { - WindowListManager.removeCloseListener(windowListener); - AllWindowEvents.removeListener("TabClose", tabListener); - }; - }).api(), - - create: function(createProperties, callback) { - if (!createProperties) { - createProperties = {}; - } - - let url = createProperties.url || NewTabURL.get(); - url = extension.baseURI.resolve(url); - - function createInWindow(window) { - let tab = window.gBrowser.addTab(url); - - let active = true; - if ("active" in createProperties) { - active = createProperties.active; - } else if ("selected" in createProperties) { - active = createProperties.selected; - } - if (active) { - window.gBrowser.selectedTab = tab; - } - - if ("index" in createProperties) { - window.gBrowser.moveTabTo(tab, createProperties.index); - } - - if (createProperties.pinned) { - window.gBrowser.pinTab(tab); - } - - if (callback) { - runSafe(context, callback, TabManager.convert(extension, tab)); - } - } - - let window = createProperties.windowId ? - WindowManager.getWindow(createProperties.windowId) : - WindowManager.topWindow; - if (!window.gBrowser) { - let obs = (finishedWindow, topic, data) => { - if (finishedWindow != window) { - return; - } - Services.obs.removeObserver(obs, "browser-delayed-startup-finished"); - createInWindow(window); - }; - Services.obs.addObserver(obs, "browser-delayed-startup-finished", false); - } else { - createInWindow(window); - } - }, - - remove: function(tabs, callback) { - if (!Array.isArray(tabs)) { - tabs = [tabs]; - } - - for (let tabId of tabs) { - let tab = TabManager.getTab(tabId); - tab.ownerDocument.defaultView.gBrowser.removeTab(tab); - } - - if (callback) { - runSafe(context, callback); - } - }, - - update: function(...args) { - let tabId, updateProperties, callback; - if (args.length == 1) { - updateProperties = args[0]; - } else { - [tabId, updateProperties, callback] = args; - } - - let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab; - let tabbrowser = tab.ownerDocument.gBrowser; - if ("url" in updateProperties) { - tab.linkedBrowser.loadURI(updateProperties.url); - } - if ("active" in updateProperties) { - if (updateProperties.active) { - tabbrowser.selectedTab = tab; - } else { - // Not sure what to do here? Which tab should we select? - } - } - if ("pinned" in updateProperties) { - if (updateProperties.pinned) { - tabbrowser.pinTab(tab); - } else { - tabbrowser.unpinTab(tab); - } - } - // FIXME: highlighted/selected, openerTabId - - if (callback) { - runSafe(context, callback, TabManager.convert(extension, tab)); - } - }, - - reload: function(tabId, reloadProperties, callback) { - let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab; - let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; - if (reloadProperties && reloadProperties.bypassCache) { - flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; - } - tab.linkedBrowser.reloadWithFlags(flags); - - if (callback) { - runSafe(context, callback); - } - }, - - get: function(tabId, callback) { - let tab = TabManager.getTab(tabId); - runSafe(context, callback, TabManager.convert(extension, tab)); - }, - - getAllInWindow: function(...args) { - let window, callback; - if (args.length == 1) { - callbacks = args[0]; - } else { - window = WindowManager.getWindow(args[0]); - callback = args[1]; - } - - if (!window) { - window = WindowManager.topWindow; - } - - return self.tabs.query({windowId: WindowManager.getId(window)}, callback); - }, - - query: function(queryInfo, callback) { - if (!queryInfo) { - queryInfo = {}; - } - - function matches(window, tab) { - let props = ["active", "pinned", "highlighted", "status", "title", "url", "index"]; - for (let prop of props) { - if (prop in queryInfo && queryInfo[prop] != tab[prop]) { - return false; - } - } - - let lastFocused = window == WindowManager.topWindow; - if ("lastFocusedWindow" in queryInfo && queryInfo.lastFocusedWindow != lastFocused) { - return false; - } - - let windowType = WindowManager.windowType(window); - if ("windowType" in queryInfo && queryInfo.windowType != windowType) { - return false; - } - - if ("windowId" in queryInfo) { - if (queryInfo.windowId == WindowManager.WINDOW_ID_CURRENT) { - if (context.contentWindow != window) { - return false; - } - } else { - if (queryInfo.windowId != tab.windowId) { - return false; - } - } - } - - if ("currentWindow" in queryInfo) { - let eq = window == context.contentWindow; - if (queryInfo.currentWindow != eq) { - return false; - } - } - - return true; - } - - let result = []; - let e = Services.wm.getEnumerator("navigator:browser"); - while (e.hasMoreElements()) { - let window = e.getNext(); - let tabs = TabManager.getTabs(extension, window); - for (let tab of tabs) { - if (matches(window, tab)) { - result.push(tab); - } - } - } - runSafe(context, callback, result); - }, - - _execute: function(tabId, details, kind, callback) { - let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab; - let mm = tab.linkedBrowser.messageManager; - - let options = {js: [], css: []}; - if (details.code) { - options[kind + 'Code'] = details.code; - } - if (details.file) { - options[kind].push(extension.baseURI.resolve(details.file)); - } - if (details.allFrames) { - options.all_frames = details.allFrames; - } - if (details.matchAboutBlank) { - options.match_about_blank = details.matchAboutBlank; - } - if (details.runAt) { - options.run_at = details.runAt; - } - mm.sendAsyncMessage("Extension:Execute", - {extensionId: extension.id, options}); - - // TODO: Call the callback with the result (which is what???). - }, - - executeScript: function(...args) { - if (args.length == 1) { - self.tabs._execute(undefined, args[0], 'js', undefined); - } else { - self.tabs._execute(args[0], args[1], 'js', args[2]); - } - }, - - insertCss: function(tabId, details, callback) { - if (args.length == 1) { - self.tabs._execute(undefined, args[0], 'css', undefined); - } else { - self.tabs._execute(args[0], args[1], 'css', args[2]); - } - }, - - connect: function(tabId, connectInfo) { - let tab = TabManager.getTab(tabId); - let mm = tab.linkedBrowser.messageManager; - - let name = connectInfo.name || ""; - let recipient = {extensionId: extension.id}; - if ("frameId" in connectInfo) { - recipient.frameId = connectInfo.frameId; - } - return context.messenger.connect(mm, name, recipient); - }, - - sendMessage: function(tabId, message, options, responseCallback) { - let tab = TabManager.getTab(tabId); - let mm = tab.linkedBrowser.messageManager; - - let recipient = {extensionId: extension.id}; - if (options && "frameId" in options) { - recipient.frameId = options.frameId; - } - return context.messenger.sendMessage(mm, message, recipient, responseCallback); - }, - }, - }; - return self; -}); diff --git a/browser/components/extensions/ext-utils.js b/browser/components/extensions/ext-utils.js deleted file mode 100644 index 83d58123bc4a..000000000000 --- a/browser/components/extensions/ext-utils.js +++ /dev/null @@ -1,324 +0,0 @@ -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - EventManager, -} = ExtensionUtils; - -// This file provides some useful code for the |tabs| and |windows| -// modules. All of the code is installed on |global|, which is a scope -// shared among the different ext-*.js scripts. - -// Manages mapping between XUL tabs and extension tab IDs. -global.TabManager = { - _tabs: new WeakMap(), - _nextId: 1, - - getId(tab) { - if (this._tabs.has(tab)) { - return this._tabs.get(tab); - } - let id = this._nextId++; - this._tabs.set(tab, id); - return id; - }, - - getBrowserId(browser) { - let gBrowser = browser.ownerDocument.defaultView.gBrowser; - // Some non-browser windows have gBrowser but not - // getTabForBrowser! - if (gBrowser && gBrowser.getTabForBrowser) { - let tab = gBrowser.getTabForBrowser(browser); - if (tab) { - return this.getId(tab); - } - } - return -1; - }, - - getTab(tabId) { - // FIXME: Speed this up without leaking memory somehow. - let e = Services.wm.getEnumerator("navigator:browser"); - while (e.hasMoreElements()) { - let window = e.getNext(); - if (!window.gBrowser) { - continue; - } - for (let tab of window.gBrowser.tabs) { - if (this.getId(tab) == tabId) { - return tab; - } - } - } - return null; - }, - - get activeTab() { - let window = WindowManager.topWindow; - if (window && window.gBrowser) { - return window.gBrowser.selectedTab; - } - return null; - }, - - getStatus(tab) { - return tab.getAttribute("busy") == "true" ? "loading" : "complete"; - }, - - convert(extension, tab) { - let window = tab.ownerDocument.defaultView; - let windowActive = window == WindowManager.topWindow; - let result = { - id: this.getId(tab), - index: tab._tPos, - windowId: WindowManager.getId(window), - selected: tab.selected, - highlighted: tab.selected, - active: tab.selected, - pinned: tab.pinned, - status: this.getStatus(tab), - incognito: PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser), - width: tab.linkedBrowser.clientWidth, - height: tab.linkedBrowser.clientHeight, - }; - - if (extension.hasPermission("tabs")) { - result.url = tab.linkedBrowser.currentURI.spec; - if (tab.linkedBrowser.contentTitle) { - result.title = tab.linkedBrowser.contentTitle; - } - let icon = window.gBrowser.getIcon(tab); - if (icon) { - result.favIconUrl = icon; - } - } - - return result; - }, - - getTabs(extension, window) { - if (!window.gBrowser) { - return []; - } - return [ for (tab of window.gBrowser.tabs) this.convert(extension, tab) ]; - }, -}; - -// Manages mapping between XUL windows and extension window IDs. -global.WindowManager = { - _windows: new WeakMap(), - _nextId: 0, - - WINDOW_ID_NONE: -1, - WINDOW_ID_CURRENT: -2, - - get topWindow() { - return Services.wm.getMostRecentWindow("navigator:browser"); - }, - - windowType(window) { - // TODO: Make this work. - return "normal"; - }, - - getId(window) { - if (this._windows.has(window)) { - return this._windows.get(window); - } - let id = this._nextId++; - this._windows.set(window, id); - return id; - }, - - getWindow(id) { - let e = Services.wm.getEnumerator("navigator:browser"); - while (e.hasMoreElements()) { - let window = e.getNext(); - if (this.getId(window) == id) { - return window; - } - } - return null; - }, - - convert(extension, window, getInfo) { - let result = { - id: this.getId(window), - focused: window == WindowManager.topWindow, - top: window.screenY, - left: window.screenX, - width: window.outerWidth, - height: window.outerHeight, - incognito: PrivateBrowsingUtils.isWindowPrivate(window), - - // We fudge on these next two. - type: this.windowType(window), - state: window.fullScreen ? "fullscreen" : "normal", - }; - - if (getInfo && getInfo.populate) { - results.tabs = TabManager.getTabs(extension, window); - } - - return result; - }, -}; - -// Manages listeners for window opening and closing. A window is -// considered open when the "load" event fires on it. A window is -// closed when a "domwindowclosed" notification fires for it. -global.WindowListManager = { - _openListeners: new Set(), - _closeListeners: new Set(), - - addOpenListener(listener, fireOnExisting = true) { - if (this._openListeners.length == 0 && this._closeListeners.length == 0) { - Services.ww.registerNotification(this); - } - this._openListeners.add(listener); - - let e = Services.wm.getEnumerator("navigator:browser"); - while (e.hasMoreElements()) { - let window = e.getNext(); - if (window.document.readyState != "complete") { - window.addEventListener("load", this); - } else if (fireOnExisting) { - listener(window); - } - } - }, - - removeOpenListener(listener) { - this._openListeners.delete(listener); - if (this._openListeners.length == 0 && this._closeListeners.length == 0) { - Services.ww.unregisterNotification(this); - } - }, - - addCloseListener(listener) { - if (this._openListeners.length == 0 && this._closeListeners.length == 0) { - Services.ww.registerNotification(this); - } - this._closeListeners.add(listener); - }, - - removeCloseListener(listener) { - this._closeListeners.delete(listener); - if (this._openListeners.length == 0 && this._closeListeners.length == 0) { - Services.ww.unregisterNotification(this); - } - }, - - handleEvent(event) { - let window = event.target.defaultView; - window.removeEventListener("load", this.loadListener); - if (window.document.documentElement.getAttribute("windowtype") != "navigator:browser") { - return; - } - - for (let listener of this._openListeners) { - listener(window); - } - }, - - queryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), - - observe(window, topic, data) { - if (topic == "domwindowclosed") { - if (window.document.documentElement.getAttribute("windowtype") != "navigator:browser") { - return; - } - - window.removeEventListener("load", this); - for (let listener of this._closeListeners) { - listener(window); - } - } else { - window.addEventListener("load", this); - } - }, -}; - -// Provides a facility to listen for DOM events across all XUL windows. -global.AllWindowEvents = { - _listeners: new Map(), - - // If |type| is a normal event type, invoke |listener| each time - // that event fires in any open window. If |type| is "progress", add - // a web progress listener that covers all open windows. - addListener(type, listener) { - if (type == "domwindowopened") { - return WindowListManager.addOpenListener(listener); - } else if (type == "domwindowclosed") { - return WindowListManager.addCloseListener(listener); - } - - let needOpenListener = this._listeners.size == 0; - - if (!this._listeners.has(type)) { - this._listeners.set(type, new Set()); - } - let list = this._listeners.get(type); - list.add(listener); - - if (needOpenListener) { - WindowListManager.addOpenListener(this.openListener); - } - }, - - removeListener(type, listener) { - if (type == "domwindowopened") { - return WindowListManager.removeOpenListener(listener); - } else if (type == "domwindowclosed") { - return WindowListManager.removeCloseListener(listener); - } - - let listeners = this._listeners.get(type); - listeners.delete(listener); - if (listeners.length == 0) { - this._listeners.delete(type); - if (this._listeners.size == 0) { - WindowListManager.removeOpenListener(this.openListener); - } - } - - let e = Services.wm.getEnumerator("navigator:browser"); - while (e.hasMoreElements()) { - let window = e.getNext(); - if (type == "progress") { - window.gBrowser.removeTabsProgressListener(listener); - } else { - window.removeEventListener(type, listener); - } - } - }, - - // Runs whenever the "load" event fires for a new window. - openListener(window) { - for (let [eventType, listeners] of AllWindowEvents._listeners) { - for (let listener of listeners) { - if (eventType == "progress") { - window.gBrowser.addTabsProgressListener(listener); - } else { - window.addEventListener(eventType, listener); - } - } - } - }, -}; - -// Subclass of EventManager where we just need to call -// add/removeEventListener on each XUL window. -global.WindowEventManager = function(context, name, event, listener) -{ - EventManager.call(this, context, name, fire => { - let listener2 = (...args) => listener(fire, ...args); - AllWindowEvents.addListener(event, listener2); - return () => { - AllWindowEvents.removeListener(event, listener2); - } - }); -} - -WindowEventManager.prototype = Object.create(EventManager.prototype); diff --git a/browser/components/extensions/ext-windows.js b/browser/components/extensions/ext-windows.js deleted file mode 100644 index f83bc4b81949..000000000000 --- a/browser/components/extensions/ext-windows.js +++ /dev/null @@ -1,151 +0,0 @@ -XPCOMUtils.defineLazyModuleGetter(this, "NewTabURL", - "resource:///modules/NewTabURL.jsm"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - EventManager, - ignoreEvent, - runSafe, -} = ExtensionUtils; - -extensions.registerAPI((extension, context) => { - return { - windows: { - WINDOW_ID_CURRENT: WindowManager.WINDOW_ID_CURRENT, - WINDOW_ID_NONE: WindowManager.WINDOW_ID_NONE, - - onCreated: - new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => { - fire(WindowManager.convert(extension, window)); - }).api(), - - onRemoved: - new WindowEventManager(context, "windows.onRemoved", "domwindowclosed", (fire, window) => { - fire(WindowManager.getId(window)); - }).api(), - - onFocusChanged: new EventManager(context, "windows.onFocusChanged", fire => { - // FIXME: This will send multiple messages for a single focus change. - let listener = event => { - let window = WindowManager.topWindow; - let windowId = window ? WindowManager.getId(window) : WindowManager.WINDOW_ID_NONE; - fire(windowId); - }; - AllWindowEvents.addListener("focus", listener); - AllWindowEvents.addListener("blur", listener); - return () => { - AllWindowEvents.removeListener("focus", listener); - AllWindowEvents.removeListener("blur", listener); - }; - }).api(), - - get: function(windowId, getInfo, callback) { - let window = WindowManager.getWindow(windowId); - runSafe(context, callback, WindowManager.convert(extension, window, getInfo)); - }, - - getCurrent: function(getInfo, callback) { - let window = context.contentWindow; - runSafe(context, callback, WindowManager.convert(extension, window, getInfo)); - }, - - getLastFocused: function(...args) { - let getInfo, callback; - if (args.length == 1) { - callback = args[0]; - } else { - [getInfo, callback] = args; - } - let window = WindowManager.topWindow; - runSafe(context, callback, WindowManager.convert(extension, window, getInfo)); - }, - - getAll: function(getAll, callback) { - let e = Services.wm.getEnumerator("navigator:browser"); - let windows = []; - while (e.hasMoreElements()) { - let window = e.getNext(); - windows.push(WindowManager.convert(extension, window, getInfo)); - } - runSafe(context, callback, windows); - }, - - create: function(createData, callback) { - function mkstr(s) { - let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); - result.data = s; - return result; - } - - let args = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray); - if ("url" in createData) { - if (Array.isArray(createData.url)) { - let array = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray); - for (let url of createData.url) { - array.AppendElement(mkstr(url)); - } - args.AppendElement(array); - } else { - args.AppendElement(mkstr(createData.url)); - } - } else { - args.AppendElement(mkstr(NewTabURL.get())); - } - - let extraFeatures = ""; - if ("incognito" in createData) { - if (createData.incognito) { - extraFeatures += ",private"; - } else { - extraFeatures += ",non-private"; - } - } - - let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank", - "chrome,dialog=no,all" + extraFeatures, args); - - if ("left" in createData || "top" in createData) { - let left = "left" in createData ? createData.left : window.screenX; - let top = "top" in createData ? createData.top : window.screenY; - window.moveTo(left, top); - } - if ("width" in createData || "height" in createData) { - let width = "width" in createData ? createData.width : window.outerWidth; - let height = "height" in createData ? createData.height : window.outerHeight; - window.resizeTo(width, height); - } - - // TODO: focused, type, state - - window.addEventListener("load", function listener() { - window.removeEventListener("load", listener); - if (callback) { - runSafe(context, callback, WindowManager.convert(extension, window)); - } - }); - }, - - update: function(windowId, updateInfo, callback) { - let window = WindowManager.getWindow(windowId); - if (updateInfo.focused) { - Services.focus.activeWindow = window; - } - // TODO: All the other properties... - runSafe(context, callback, WindowManager.convert(extension, window)); - }, - - remove: function(windowId, callback) { - let window = WindowManager.getWindow(windowId); - window.close(); - - let listener = () => { - AllWindowEvents.removeListener("domwindowclosed", listener); - if (callback) { - runSafe(context, callback); - } - }; - AllWindowEvents.addListener("domwindowclosed", listener); - }, - }, - }; -}); diff --git a/browser/components/extensions/extension.svg b/browser/components/extensions/extension.svg deleted file mode 100644 index a16455253834..000000000000 --- a/browser/components/extensions/extension.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - diff --git a/browser/components/extensions/jar.mn b/browser/components/extensions/jar.mn deleted file mode 100644 index ea0b27625cf0..000000000000 --- a/browser/components/extensions/jar.mn +++ /dev/null @@ -1,11 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -browser.jar: - content/browser/extension.svg (extension.svg) - content/browser/ext-utils.js (ext-utils.js) - content/browser/ext-contextMenus.js (ext-contextMenus.js) - content/browser/ext-browserAction.js (ext-browserAction.js) - content/browser/ext-tabs.js (ext-tabs.js) - content/browser/ext-windows.js (ext-windows.js) diff --git a/browser/components/extensions/moz.build b/browser/components/extensions/moz.build deleted file mode 100644 index 3bbe6729759c..000000000000 --- a/browser/components/extensions/moz.build +++ /dev/null @@ -1,7 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -JAR_MANIFESTS += ['jar.mn'] diff --git a/browser/components/extensions/prepare.py b/browser/components/extensions/prepare.py deleted file mode 100644 index 179adae45171..000000000000 --- a/browser/components/extensions/prepare.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import argparse -import json -import uuid -import sys -import os.path - -parser = argparse.ArgumentParser(description='Create install.rdf from manifest.json') -parser.add_argument('--locale') -parser.add_argument('--profile') -parser.add_argument('--uuid') -parser.add_argument('dir') -args = parser.parse_args() - -manifestFile = os.path.join(args.dir, 'manifest.json') -manifest = json.load(open(manifestFile)) - -locale = args.locale -if not locale: - locale = manifest.get('default_locale', 'en-US') - -def process_locale(s): - if s.startswith('__MSG_') and s.endswith('__'): - tag = s[6:-2] - path = os.path.join(args.dir, '_locales', locale, 'messages.json') - data = json.load(open(path)) - return data[tag]['message'] - else: - return s - -id = args.uuid -if not id: - id = '{' + str(uuid.uuid4()) + '}' - -name = process_locale(manifest['name']) -desc = process_locale(manifest['description']) -version = manifest['version'] - -installFile = open(os.path.join(args.dir, 'install.rdf'), 'w') -print >>installFile, '' -print >>installFile, '>installFile, ' xmlns:em="http://www.mozilla.org/2004/em-rdf#">' -print >>installFile -print >>installFile, ' ' -print >>installFile, ' {}'.format(id) -print >>installFile, ' 2' -print >>installFile, ' {}'.format(name) -print >>installFile, ' {}'.format(desc) -print >>installFile, ' {}'.format(version) -print >>installFile, ' true' - -print >>installFile, ' ' -print >>installFile, ' ' -print >>installFile, ' {ec8030f7-c20a-464f-9b0e-13a3a9e97384}' -print >>installFile, ' 4.0' -print >>installFile, ' 50.0' -print >>installFile, ' ' -print >>installFile, ' ' - -print >>installFile, ' ' -print >>installFile, '' -installFile.close() - -bootstrapPath = os.path.join(os.path.dirname(sys.argv[0]), 'bootstrap.js') -data = open(bootstrapPath).read() -boot = open(os.path.join(args.dir, 'bootstrap.js'), 'w') -boot.write(data) -boot.close() - -if args.profile: - os.system('mkdir -p {}/extensions'.format(args.profile)) - output = open(args.profile + '/extensions/' + id, 'w') - print >>output, os.path.realpath(args.dir) - output.close() -else: - dir = os.path.realpath(args.dir) - if dir[-1] == os.sep: - dir = dir[:-1] - os.system('cd "{}"; zip ../"{}".xpi -r *'.format(args.dir, os.path.basename(dir))) diff --git a/browser/components/moz.build b/browser/components/moz.build index 673692144b9b..8bcf35587a1e 100644 --- a/browser/components/moz.build +++ b/browser/components/moz.build @@ -9,7 +9,6 @@ DIRS += [ 'customizableui', 'dirprovider', 'downloads', - 'extensions', 'feeds', 'loop', 'migration', diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 9f7e0d5d9280..aad0240f2252 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -169,9 +169,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher", XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement", - "resource://gre/modules/ExtensionManagement.jsm"); - const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; const PREF_PLUGINS_UPDATEURL = "plugins.update.url"; @@ -604,12 +601,6 @@ BrowserGlue.prototype = { os.addObserver(this, "xpi-signature-changed", false); os.addObserver(this, "autocomplete-did-enter-text", false); - ExtensionManagement.registerScript("chrome://browser/content/ext-utils.js"); - ExtensionManagement.registerScript("chrome://browser/content/ext-browserAction.js"); - ExtensionManagement.registerScript("chrome://browser/content/ext-contextMenus.js"); - ExtensionManagement.registerScript("chrome://browser/content/ext-tabs.js"); - ExtensionManagement.registerScript("chrome://browser/content/ext-windows.js"); - this._flashHangCount = 0; }, diff --git a/browser/modules/E10SUtils.jsm b/browser/modules/E10SUtils.jsm index 1798082069ca..a27c46ff8453 100644 --- a/browser/modules/E10SUtils.jsm +++ b/browser/modules/E10SUtils.jsm @@ -59,15 +59,6 @@ this.E10SUtils = { mustLoadRemote = chromeReg.mustLoadURLRemotely(url); } - if (aURL.startsWith("moz-extension:")) { - canLoadRemote = false; - mustLoadRemote = false; - } - - if (aURL.startsWith("view-source:")) { - return this.canLoadURIInProcess(aURL.substr("view-source:".length), aProcess); - } - if (mustLoadRemote) return processIsRemote; diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index d25dd0f72ca0..856610f65477 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -1951,7 +1951,3 @@ chatbox { -moz-padding-end: 0 !important; -moz-margin-end: 0 !important; } - -.browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent { - padding: 0; -} diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index e7286466392d..0a23cfd5aae6 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -3692,7 +3692,3 @@ window > chatbox { padding-left: 0; padding-right: 0; } - -.browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent { - padding: 0; -} diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index f6be1ab2a65d..043c707f7137 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -2912,7 +2912,3 @@ chatbox { @media not all and (-moz-os-version: windows-xp) { %include browser-aero.css } - -.browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent { - padding: 0; -} diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm deleted file mode 100644 index c73b3eefd1cb..000000000000 --- a/toolkit/components/extensions/Extension.jsm +++ /dev/null @@ -1,578 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["Extension"]; - -/* - * This file is the main entry point for extensions. When an extension - * loads, its bootstrap.js file creates a Extension instance - * and calls .startup() on it. It calls .shutdown() when the extension - * unloads. Extension manages any extension-specific state in - * the chrome process. - */ - -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/devtools/event-emitter.js"); - -XPCOMUtils.defineLazyModuleGetter(this, "Locale", - "resource://gre/modules/Locale.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern", - "resource://gre/modules/MatchPattern.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); - -Cu.import("resource://gre/modules/ExtensionManagement.jsm"); - -// Register built-in parts of the API. Other parts may be registered -// in browser/, mobile/, or b2g/. -ExtensionManagement.registerScript("chrome://extensions/content/ext-alarms.js"); -ExtensionManagement.registerScript("chrome://extensions/content/ext-backgroundPage.js"); -ExtensionManagement.registerScript("chrome://extensions/content/ext-notifications.js"); -ExtensionManagement.registerScript("chrome://extensions/content/ext-i18n.js"); -ExtensionManagement.registerScript("chrome://extensions/content/ext-idle.js"); -ExtensionManagement.registerScript("chrome://extensions/content/ext-runtime.js"); -ExtensionManagement.registerScript("chrome://extensions/content/ext-extension.js"); -ExtensionManagement.registerScript("chrome://extensions/content/ext-webNavigation.js"); -ExtensionManagement.registerScript("chrome://extensions/content/ext-webRequest.js"); -ExtensionManagement.registerScript("chrome://extensions/content/ext-storage.js"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - MessageBroker, - Messenger, - injectAPI, -} = ExtensionUtils; - -const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - -let scriptScope = this; - -// This object loads the ext-*.js scripts that define the extension API. -let Management = { - initialized: false, - scopes: [], - apis: [], - emitter: new EventEmitter(), - - // Loads all the ext-*.js scripts currently registered. - lazyInit() { - if (this.initialized) { - return; - } - this.initialized = true; - - for (let script of ExtensionManagement.getScripts()) { - let scope = {extensions: this, global: scriptScope}; - Services.scriptloader.loadSubScript(script, scope, "UTF-8"); - - // Save the scope to avoid it being garbage collected. - this.scopes.push(scope); - } - }, - - // Called by an ext-*.js script to register an API. The |api| - // parameter should be an object of the form: - // { - // tabs: { - // create: ..., - // onCreated: ... - // } - // } - // This registers tabs.create and tabs.onCreated as part of the API. - registerAPI(api) { - this.apis.push({api}); - }, - - // Same as above, but only register the API is the add-on has the - // given permission. - registerPrivilegedAPI(permission, api) { - this.apis.push({api, permission}); - }, - - // Mash together into a single object all the APIs registered by the - // functions above. Return the merged object. - generateAPIs(extension, context) { - let obj = {}; - - // Recursively copy properties from source to dest. - function copy(dest, source) { - for (let prop in source) { - if (typeof(source[prop]) == "object") { - if (!(prop in dest)) { - dest[prop] = {}; - } - copy(dest[prop], source[prop]); - } else { - dest[prop] = source[prop]; - } - } - } - - for (let api of this.apis) { - if (api.permission) { - if (!extension.hasPermission(api.permission)) { - continue; - } - } - - api = api.api(extension, context); - copy(obj, api); - } - - return obj; - }, - - // The ext-*.js scripts can ask to be notified for certain hooks. - on(hook, callback) { - this.emitter.on(hook, callback); - }, - - // Ask to run all the callbacks that are registered for a given hook. - emit(hook, ...args) { - this.lazyInit(); - this.emitter.emit(hook, ...args); - }, -}; - -// A MessageBroker that's used to send and receive messages for -// extension pages (which run in the chrome process). -let globalBroker = new MessageBroker([Services.mm, Services.ppmm]); - -// An extension page is an execution context for any extension content -// that runs in the chrome process. It's used for background pages -// (type="background"), popups (type="popup"), and any extension -// content loaded into browser tabs (type="tab"). -// -// |params| is an object with the following properties: -// |type| is one of "background", "popup", or "tab". -// |contentWindow| is the DOM window the content runs in. -// |uri| is the URI of the content (optional). -// |docShell| is the docshell the content runs in (optional). -function ExtensionPage(extension, params) -{ - let {type, contentWindow, uri, docShell} = params; - this.extension = extension; - this.type = type; - this.contentWindow = contentWindow || null; - this.uri = uri || extension.baseURI; - this.onClose = new Set(); - - // This is the sender property passed to the Messenger for this - // page. It can be augmented by the "page-open" hook. - let sender = {id: extension.id}; - if (uri) { - sender.url = uri.spec; - } - let delegate = {}; - Management.emit("page-load", this, params, sender, delegate); - - let filter = {id: extension.id}; - this.messenger = new Messenger(this, globalBroker, sender, filter, delegate); - - this.extension.views.add(this); -} - -ExtensionPage.prototype = { - get cloneScope() { - return this.contentWindow; - }, - - callOnClose(obj) { - this.onClose.add(obj); - }, - - forgetOnClose(obj) { - this.onClose.delete(obj); - }, - - // Called when the extension shuts down. - shutdown() { - Management.emit("page-shutdown", this); - this.unload(); - }, - - // This method is called when an extension page navigates away or - // its tab is closed. - unload() { - Management.emit("page-unload", this); - - this.extension.views.delete(this); - - for (let obj of this.onClose) { - obj.close(); - } - }, -}; - -// Responsible for loading extension APIs into the right globals. -let GlobalManager = { - // Number of extensions currently enabled. - count: 0, - - // Map[docShell -> {extension, context}] where context is an ExtensionPage. - docShells: new Map(), - - // Map[extension ID -> Extension]. Determines which extension is - // responsible for content under a particular extension ID. - extensionMap: new Map(), - - init(extension) { - if (this.count == 0) { - Services.obs.addObserver(this, "content-document-global-created", false); - } - this.count++; - - this.extensionMap.set(extension.id, extension); - }, - - uninit(extension) { - this.count--; - if (this.count == 0) { - Services.obs.removeObserver(this, "content-document-global-created"); - } - - for (let [docShell, data] of this.docShells) { - if (extension == data.extension) { - this.docShells.delete(docShell); - } - } - - this.extensionMap.delete(extension.id); - }, - - injectInDocShell(docShell, extension, context) { - this.docShells.set(docShell, {extension, context}); - }, - - observe(contentWindow, topic, data) { - function inject(extension, context) { - let chromeObj = Cu.createObjectIn(contentWindow, {defineAs: "chrome"}); - let api = Management.generateAPIs(extension, context); - injectAPI(api, chromeObj); - } - - let docShell = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .sameTypeRootTreeItem - .QueryInterface(Ci.nsIDocShell); - - if (this.docShells.has(docShell)) { - let {extension, context} = this.docShells.get(docShell); - inject(extension, context); - return; - } - - // We don't inject into sub-frames of a UI page. - if (contentWindow != contentWindow.top) { - return; - } - - // Find the add-on associated with this document via the - // principal's originAttributes. This value is computed by - // extensionURIToAddonID, which ensures that we don't inject our - // API into webAccessibleResources. - let principal = contentWindow.document.nodePrincipal; - let id = principal.originAttributes.addonId; - if (!this.extensionMap.has(id)) { - return; - } - let extension = this.extensionMap.get(id); - let uri = contentWindow.document.documentURIObject; - let context = new ExtensionPage(extension, {type: "tab", contentWindow, uri, docShell}); - inject(extension, context); - - let eventHandler = docShell.chromeEventHandler; - let listener = event => { - eventHandler.removeEventListener("unload", listener); - context.unload(); - }; - eventHandler.addEventListener("unload", listener, true); - }, -}; - -// We create one instance of this class per extension. |addonData| -// comes directly from bootstrap.js when initializing. -function Extension(addonData) -{ - let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); - let uuid = uuidGenerator.generateUUID().number; - uuid = uuid.substring(1, uuid.length - 1); // Strip of { and } off the UUID. - this.uuid = uuid; - - this.addonData = addonData; - this.id = addonData.id; - this.baseURI = Services.io.newURI("moz-extension://" + uuid, null, null); - this.manifest = null; - this.localeMessages = null; - - this.views = new Set(); - - this.onStartup = null; - - this.hasShutdown = false; - this.onShutdown = new Set(); - - this.permissions = new Set(); - this.whiteListedHosts = null; - this.webAccessibleResources = new Set(); - - ExtensionManagement.startupExtension(this.uuid, this.addonData.resourceURI, this); -} - -Extension.prototype = { - // Representation of the extension to send to content - // processes. This should include anything the content process might - // need. - serialize() { - return { - id: this.id, - uuid: this.uuid, - manifest: this.manifest, - resourceURL: this.addonData.resourceURI.spec, - baseURL: this.baseURI.spec, - content_scripts: this.manifest.content_scripts || [], - webAccessibleResources: this.webAccessibleResources, - whiteListedHosts: this.whiteListedHosts.serialize(), - }; - }, - - // https://developer.chrome.com/extensions/i18n - localizeMessage(message, substitutions) { - if (message in this.localeMessages) { - let str = this.localeMessages[message].message; - - if (!substitutions) { - substitutions = []; - } - if (!Array.isArray(substitutions)) { - substitutions = [substitutions]; - } - - // https://developer.chrome.com/extensions/i18n-messages - // |str| may contain substrings of the form $1 or $PLACEHOLDER$. - // In the former case, we replace $n with substitutions[n - 1]. - // In the latter case, we consult the placeholders array. - // The placeholder may itself use $n to refer to substitutions. - let replacer = (matched, name) => { - if (name.length == 1 && name[0] >= '1' && name[0] <= '9') { - return substitutions[parseInt(name) - 1]; - } else { - let content = this.localeMessages[message].placeholders[name].content; - if (content[0] == '$') { - return replacer(matched, content[1]); - } else { - return content; - } - } - }; - return str.replace(/\$([A-Za-z_@]+)\$/, replacer) - .replace(/\$([0-9]+)/, replacer) - .replace(/\$\$/, "$"); - } - - // Check for certain pre-defined messages. - if (message == "@@extension_id") { - return this.id; - } else if (message == "@@ui_locale") { - return Locale.getLocale(); - } else if (message == "@@bidi_dir") { - return "ltr"; // FIXME - } - - Cu.reportError(`Unknown localization message ${message}`); - return "??"; - }, - - localize(str) { - if (!str) { - return str; - } - - if (str.startsWith("__MSG_") && str.endsWith("__")) { - let message = str.substring("__MSG_".length, str.length - "__".length); - return this.localizeMessage(message); - } - - return str; - }, - - readJSON(uri) { - return new Promise((resolve, reject) => { - NetUtil.asyncFetch({uri, loadUsingSystemPrincipal: true}, (inputStream, status) => { - if (!Components.isSuccessCode(status)) { - reject(status); - return; - } - let text = NetUtil.readInputStreamToString(inputStream, inputStream.available()); - try { - resolve(JSON.parse(text)); - } catch (e) { - reject(e); - } - }); - }); - }, - - readManifest() { - let manifestURI = Services.io.newURI("manifest.json", null, this.baseURI); - return this.readJSON(manifestURI); - }, - - readLocaleFile(locale) { - let dir = locale.replace("-", "_"); - let url = `_locales/${dir}/messages.json`; - let uri = Services.io.newURI(url, null, this.baseURI); - return this.readJSON(uri); - }, - - readLocaleMessages() { - let locales = []; - - // We need to base this off of this.addonData.resourceURI rather - // than baseURI since baseURI is a moz-extension URI, which always - // QIs to nsIFileURL. - let uri = Services.io.newURI("_locales", null, this.addonData.resourceURI); - if (uri instanceof Ci.nsIFileURL) { - let file = uri.file; - let enumerator; - try { - enumerator = file.directoryEntries; - } catch (e) { - return {}; - } - while (enumerator.hasMoreElements()) { - let file = enumerator.getNext().QueryInterface(Ci.nsIFile); - locales.push({ - name: file.leafName, - locales: [file.leafName.replace("_", "-")] - }); - } - } - - if (uri instanceof Ci.nsIJARURI && uri.JARFile instanceof Ci.nsIFileURL) { - let file = uri.JARFile.file; - let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader); - try { - zipReader.open(file); - let enumerator = zipReader.findEntries("_locales/*"); - while (enumerator.hasMore()) { - let name = enumerator.getNext(); - let match = name.match(new RegExp("_locales\/([^/]*)")); - if (match && match[1]) { - locales.push({ - name: match[1], - locales: [match[1].replace("_", "-")] - }); - } - } - } finally { - zipReader.close(); - } - } - - let locale = Locale.findClosestLocale(locales); - if (locale) { - return this.readLocaleFile(locale.name).catch(() => {}); - } - return {}; - }, - - runManifest(manifest) { - let permissions = manifest.permissions || []; - let webAccessibleResources = manifest.web_accessible_resources || []; - - let whitelist = []; - for (let perm of permissions) { - if (perm.match(/:\/\//)) { - whitelist.push(perm); - } else { - this.permissions.add(perm); - } - } - this.whiteListedHosts = new MatchPattern(whitelist); - - let resources = new Set(); - for (let url of webAccessibleResources) { - resources.add(url); - } - this.webAccessibleResources = resources; - - for (let directive in manifest) { - Management.emit("manifest_" + directive, directive, this, manifest); - } - - let data = Services.ppmm.initialProcessData; - if (!data["Extension:Extensions"]) { - data["Extension:Extensions"] = []; - } - let serial = this.serialize(); - data["Extension:Extensions"].push(serial); - Services.ppmm.broadcastAsyncMessage("Extension:Startup", serial); - }, - - callOnClose(obj) { - this.onShutdown.add(obj); - }, - - forgetOnClose(obj) { - this.onShutdown.delete(obj); - }, - - startup() { - GlobalManager.init(this); - - return Promise.all([this.readManifest(), this.readLocaleMessages()]).then(([manifest, messages]) => { - if (this.hasShutdown) { - return; - } - - this.manifest = manifest; - this.localeMessages = messages; - - Management.emit("startup", this); - - this.runManifest(manifest); - }).catch(e => { - dump(`Extension error: ${e} ${e.fileName}:${e.lineNumber}\n`); - Cu.reportError(e); - }); - }, - - shutdown() { - this.hasShutdown = true; - if (!this.manifest) { - return; - } - - GlobalManager.uninit(this); - - for (let view of this.views) { - view.shutdown(); - } - - for (let obj of this.onShutdown) { - obj.close(); - } - - Management.emit("shutdown", this); - - Services.ppmm.broadcastAsyncMessage("Extension:Shutdown", {id: this.id}); - - ExtensionManagement.shutdownExtension(this.uuid); - }, - - hasPermission(perm) { - return this.permissions.has(perm); - }, -}; - diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm deleted file mode 100644 index 81ea03e92128..000000000000 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ /dev/null @@ -1,521 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["ExtensionContent"]; - -/* - * This file handles the content process side of extensions. It mainly - * takes care of content script injection, content script APIs, and - * messaging. - */ - -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement", - "resource://gre/modules/ExtensionManagement.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern", - "resource://gre/modules/MatchPattern.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - runSafeWithoutClone, - MessageBroker, - Messenger, - ignoreEvent, - injectAPI, -} = ExtensionUtils; - -function isWhenBeforeOrSame(when1, when2) -{ - let table = {"document_start": 0, - "document_end": 1, - "document_idle": 2}; - return table[when1] <= table[when2]; -} - -// This is the fairly simple API that we inject into content -// scripts. -let api = context => { return { - runtime: { - connect: function(extensionId, connectInfo) { - let name = connectInfo && connectInfo.name || ""; - let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId}; - return context.messenger.connect(context.messageManager, name, recipient); - }, - - getManifest: function(context) { - return context.extension.getManifest(); - }, - - getURL: function(path) { - return context.extension.baseURI.resolve(url); - }, - - onConnect: context.messenger.onConnect("runtime.onConnect"), - - onMessage: context.messenger.onMessage("runtime.onMessage"), - - sendMessage: function(...args) { - let extensionId, message, options, responseCallback; - if (args.length == 1) { - message = args[0]; - } else if (args.length == 2) { - [message, responseCallback] = args; - } else { - [extensionId, message, options, responseCallback] = args; - } - - let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId}; - context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback); - }, - }, - - extension: { - getURL: function(path) { - return context.extension.baseURI.resolve(url); - }, - - inIncognitoContext: PrivateBrowsingUtils.isContentWindowPrivate(context.contentWindow), - }, -}}; - -// Represents a content script. -function Script(options) -{ - this.options = options; - this.run_at = this.options.run_at; - this.js = this.options.js || []; - this.css = this.options.css || []; - - this.matches_ = new MatchPattern(this.options.matches); - this.exclude_matches_ = new MatchPattern(this.options.exclude_matches || null); - - // TODO: Support glob patterns. -} - -Script.prototype = { - matches(window) { - let uri = window.document.documentURIObject; - if (!this.matches_.matches(uri)) { - return false; - } - - if (this.exclude_matches_.matches(uri)) { - return false; - } - - if (!this.options.all_frames && window.top != window) { - return false; - } - - // TODO: match_about_blank. - - return true; - }, - - tryInject(extension, window, sandbox, shouldRun) { - if (!this.matches(window)) { - return; - } - - if (shouldRun("document_start")) { - let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIDOMWindowUtils); - - for (let url of this.css) { - url = extension.baseURI.resolve(url); - runSafeWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET); - } - - if (this.options.cssCode) { - let url = "data:text/css;charset=utf-8," + encodeURIComponent(this.options.cssCode); - runSafeWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET); - } - } - - let scheduled = this.run_at || "document_idle"; - if (shouldRun(scheduled)) { - for (let url of this.js) { - url = extension.baseURI.resolve(url); - Services.scriptloader.loadSubScript(url, sandbox); - } - - if (this.options.jsCode) { - Cu.evalInSandbox(this.options.jsCode, sandbox, "latest"); - } - } - }, -}; - -function getWindowMessageManager(contentWindow) -{ - let ir = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDocShell) - .QueryInterface(Ci.nsIInterfaceRequestor); - try { - return ir.getInterface(Ci.nsIContentFrameMessageManager); - } catch (e) { - // Some windows don't support this interface (hidden window). - return null; - } -} - -// Scope in which extension content script code can run. It uses -// Cu.Sandbox to run the code. There is a separate scope for each -// frame. -function ExtensionContext(extensionId, contentWindow) -{ - this.extension = ExtensionManager.get(extensionId); - this.extensionId = extensionId; - this.contentWindow = contentWindow; - - let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - let outerWindowId = utils.outerWindowID; - let frameId = contentWindow == contentWindow.top ? 0 : outerWindowId; - this.frameId = frameId; - - let mm = getWindowMessageManager(contentWindow); - this.messageManager = mm; - - let prin = [contentWindow]; - if (Services.scriptSecurityManager.isSystemPrincipal(contentWindow.document.nodePrincipal)) { - // Make sure we don't hand out the system principal by accident. - prin = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal); - } - - this.sandbox = Cu.Sandbox(prin, {sandboxPrototype: contentWindow, wantXrays: true}); - - let delegate = { - getSender(context, target, sender) { - // Nothing to do here. - } - }; - - let url = contentWindow.location.href; - let broker = ExtensionContent.getBroker(mm); - this.messenger = new Messenger(this, broker, {id: extensionId, frameId, url}, - {id: extensionId, frameId}, delegate); - - let chromeObj = Cu.createObjectIn(this.sandbox, {defineAs: "chrome"}); - injectAPI(api(this), chromeObj); - - this.onClose = new Set(); -} - -ExtensionContext.prototype = { - get cloneScope() { - return this.sandbox; - }, - - execute(script, shouldRun) { - script.tryInject(this.extension, this.contentWindow, this.sandbox, shouldRun); - }, - - callOnClose(obj) { - this.onClose.add(obj); - }, - - forgetOnClose(obj) { - this.onClose.delete(obj); - }, - - close() { - for (let obj of this.onClose) { - obj.close(); - } - }, -}; - -// Responsible for creating ExtensionContexts and injecting content -// scripts into them when new documents are created. -let DocumentManager = { - extensionCount: 0, - - // WeakMap[window -> Map[extensionId -> ExtensionContext]] - windows: new WeakMap(), - - init() { - Services.obs.addObserver(this, "document-element-inserted", false); - Services.obs.addObserver(this, "dom-window-destroyed", false); - }, - - uninit() { - Services.obs.removeObserver(this, "document-element-inserted"); - Services.obs.removeObserver(this, "dom-window-destroyed"); - }, - - getWindowState(contentWindow) { - let readyState = contentWindow.document.readyState; - if (readyState == "loading") { - return "document_start"; - } else if (readyState == "interactive") { - return "document_end"; - } else { - return "document_idle"; - } - }, - - observe: function(subject, topic, data) { - if (topic == "document-element-inserted") { - let document = subject; - let window = document && document.defaultView; - if (!document || !document.location || !window) { - return; - } - - // Make sure we only load into frames that ExtensionContent.init - // was called on (i.e., not frames for social or sidebars). - let mm = getWindowMessageManager(window); - if (!mm || !ExtensionContent.globals.has(mm)) { - return; - } - - this.windows.delete(window); - - this.trigger("document_start", window); - window.addEventListener("DOMContentLoaded", this, true); - window.addEventListener("load", this, true); - } else if (topic == "dom-window-destroyed") { - let window = subject; - if (!this.windows.has(window)) { - return; - } - - let extensions = this.windows.get(window); - for (let [extensionId, context] of extensions) { - context.close(); - } - - this.windows.delete(window); - } - }, - - handleEvent: function(event) { - let window = event.target.defaultView; - window.removeEventListener(event.type, this, true); - - // Need to check if we're still on the right page? Greasemonkey does this. - - if (event.type == "DOMContentLoaded") { - this.trigger("document_end", window); - } else if (event.type == "load") { - this.trigger("document_idle", window); - } - }, - - executeScript(global, extensionId, script) { - let window = global.content; - let extensions = this.windows.get(window); - if (!extensions) { - return; - } - let context = extensions.get(extensionId); - if (!context) { - return; - } - - // TODO: Somehow make sure we have the right permissions for this origin! - // FIXME: Need to keep this around so that I will execute it later if we're not in the right state. - context.execute(script, scheduled => scheduled == state); - }, - - enumerateWindows: function*(docShell) { - let window = docShell.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow) - yield [window, this.getWindowState(window)]; - - for (let i = 0; i < docShell.childCount; i++) { - let child = docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell); - yield* this.enumerateWindows(child); - } - }, - - getContext(extensionId, window) { - if (!this.windows.has(window)) { - this.windows.set(window, new Map()); - } - let extensions = this.windows.get(window); - if (!extensions.has(extensionId)) { - let context = new ExtensionContext(extensionId, window); - extensions.set(extensionId, context); - } - return extensions.get(extensionId); - }, - - startupExtension(extensionId) { - if (this.extensionCount == 0) { - this.init(); - } - this.extensionCount++; - - let extension = ExtensionManager.get(extensionId); - for (let global of ExtensionContent.globals.keys()) { - for (let [window, state] of this.enumerateWindows(global.docShell)) { - for (let script of extension.scripts) { - if (script.matches(window)) { - let context = this.getContext(extensionId, window); - context.execute(script, scheduled => isWhenBeforeOrSame(scheduled, state)); - } - } - } - } - }, - - shutdownExtension(extensionId) { - for (let global of ExtensionContent.globals.keys()) { - for (let [window, state] of this.enumerateWindows(global.docShell)) { - let extensions = this.windows.get(window); - if (!extensions) { - continue; - } - let context = extensions.get(extensionId); - if (context) { - context.close(); - extensions.delete(extensionId); - } - } - } - - this.extensionCount--; - if (this.extensionCount == 0) { - this.uninit(); - } - }, - - trigger(when, window) { - let state = this.getWindowState(window); - for (let [extensionId, extension] of ExtensionManager.extensions) { - for (let script of extension.scripts) { - if (script.matches(window)) { - let context = this.getContext(extensionId, window); - context.execute(script, scheduled => isWhenBeforeOrSame(scheduled, state)); - } - } - } - }, -}; - -// Represents a browser extension in the content process. -function BrowserExtensionContent(data) -{ - this.id = data.id; - this.uuid = data.uuid; - this.data = data; - this.scripts = [ for (scriptData of data.content_scripts) new Script(scriptData) ]; - this.webAccessibleResources = data.webAccessibleResources; - this.whiteListedHosts = data.whiteListedHosts; - - this.manifest = data.manifest; - this.baseURI = Services.io.newURI(data.baseURL, null, null); - - let uri = Services.io.newURI(data.resourceURL, null, null); - - if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { - // Extension.jsm takes care of this in the parent. - ExtensionManagement.startupExtension(this.uuid, uri, this); - } -}; - -BrowserExtensionContent.prototype = { - shutdown() { - if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { - ExtensionManagement.shutdownExtension(this.uuid); - } - }, -}; - -let ExtensionManager = { - // Map[extensionId, BrowserExtensionContent] - extensions: new Map(), - - init() { - Services.cpmm.addMessageListener("Extension:Startup", this); - Services.cpmm.addMessageListener("Extension:Shutdown", this); - - if (Services.cpmm.initialProcessData && "Extension:Extensions" in Services.cpmm.initialProcessData) { - let extensions = Services.cpmm.initialProcessData["Extension:Extensions"]; - for (let data of extensions) { - this.extensions.set(data.id, new BrowserExtensionContent(data)); - DocumentManager.startupExtension(data.id); - } - } - }, - - get(extensionId) { - return this.extensions.get(extensionId); - }, - - receiveMessage({name, data}) { - let extension; - switch (name) { - case "Extension:Startup": - extension = new BrowserExtensionContent(data); - this.extensions.set(data.id, extension); - DocumentManager.startupExtension(data.id); - break; - - case "Extension:Shutdown": - extension = this.extensions.get(data.id); - extension.shutdown(); - DocumentManager.shutdownExtension(data.id); - this.extensions.delete(data.id); - break; - } - } -}; - -let ExtensionContent = { - globals: new Map(), - - init(global) { - let broker = new MessageBroker([global]); - this.globals.set(global, broker); - - global.addMessageListener("Extension:Execute", this); - - let windowId = global.content - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .outerWindowID; - global.sendAsyncMessage("Extension:TopWindowID", {windowId}); - }, - - uninit(global) { - this.globals.delete(global); - - let windowId = global.content - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .outerWindowID; - global.sendAsyncMessage("Extension:RemoveTopWindowID", {windowId}); - }, - - getBroker(messageManager) { - return this.globals.get(messageManager); - }, - - receiveMessage({target, name, data}) { - switch (name) { - case "Extension:Execute": - data.options.matches = ""; - let script = new Script(data.options); - let {extensionId} = data; - DocumentManager.executeScript(target, extensionId, script); - break; - } - }, -}; - -ExtensionManager.init(); diff --git a/toolkit/components/extensions/ExtensionManagement.jsm b/toolkit/components/extensions/ExtensionManagement.jsm deleted file mode 100644 index a79735f54400..000000000000 --- a/toolkit/components/extensions/ExtensionManagement.jsm +++ /dev/null @@ -1,201 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["ExtensionManagement"]; - -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -/* - * This file should be kept short and simple since it's loaded even - * when no extensions are running. - */ - -// Keep track of frame IDs for content windows. Mostly we can just use -// the outer window ID as the frame ID. However, the API specifies -// that top-level windows have a frame ID of 0. So we need to keep -// track of which windows are top-level. This code listens to messages -// from ExtensionContent to do that. -let Frames = { - // Window IDs of top-level content windows. - topWindowIds: new Set(), - - init() { - if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { - return; - } - - Services.mm.addMessageListener("Extension:TopWindowID", this); - Services.mm.addMessageListener("Extension:RemoveTopWindowID", this); - }, - - isTopWindowId(windowId) { - return this.topWindowIds.has(windowId); - }, - - // Convert an outer window ID to a frame ID. An outer window ID of 0 - // is invalid. - getId(windowId) { - if (this.isTopWindowId(windowId)) { - return 0; - } else if (windowId == 0) { - return -1; - } else { - return windowId; - } - }, - - // Convert an outer window ID for a parent window to a frame - // ID. Outer window IDs follow the same convention that - // |window.top.parent === window.top|. The API works differently, - // giving a frame ID of -1 for the the parent of a top-level - // window. This function handles the conversion. - getParentId(parentWindowId, windowId) { - if (parentWindowId == windowId) { - // We have a top-level window. - return -1; - } - - // Not a top-level window. Just return the ID as normal. - return this.getId(parentWindowId); - }, - - receiveMessage({name, data}) { - switch (name) { - case "Extension:TopWindowID": - // FIXME: Need to handle the case where the content process - // crashes. Right now we leak its top window IDs. - this.topWindowIds.add(data.windowId); - break; - - case "Extension:RemoveTopWindowID": - this.topWindowIds.delete(data.windowId); - break; - } - }, -}; -Frames.init(); - -// Manage the collection of ext-*.js scripts that define the extension API. -let Scripts = { - scripts: new Set(), - - register(script) { - this.scripts.add(script); - }, - - getScripts() { - return this.scripts; - }, -}; - -// This object manages various platform-level issues related to -// moz-extension:// URIs. It lives here so that it can be used in both -// the parent and child processes. -// -// moz-extension URIs have the form moz-extension://uuid/path. Each -// extension has its own UUID, unique to the machine it's installed -// on. This is easier and more secure than using the extension ID, -// since it makes it slightly harder to fingerprint for extensions if -// each user uses different URIs for the extension. -let Service = { - initialized: false, - - // Map[uuid -> extension]. - // extension can be an Extension (parent process) or BrowserExtensionContent (child process). - uuidMap: new Map(), - - init() { - let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService); - aps = aps.wrappedJSObject; - this.aps = aps; - aps.setExtensionURILoadCallback(this.extensionURILoadableByAnyone.bind(this)); - aps.setExtensionURIToAddonIdCallback(this.extensionURIToAddonID.bind(this)); - }, - - // Called when a new extension is loaded. - startupExtension(uuid, uri, extension) { - if (!this.initialized) { - this.initialized = true; - this.init(); - } - - // Create the moz-extension://uuid mapping. - let handler = Services.io.getProtocolHandler("moz-extension"); - handler.QueryInterface(Ci.nsISubstitutingProtocolHandler); - handler.setSubstitution(uuid, uri); - - this.uuidMap.set(uuid, extension); - this.aps.setAddonLoadURICallback(extension.id, this.checkAddonMayLoad.bind(this, extension)); - }, - - // Called when an extension is unloaded. - shutdownExtension(uuid) { - let extension = this.uuidMap.get(uuid); - this.uuidMap.delete(uuid); - this.aps.setAddonLoadURICallback(extension.id, null); - - let handler = Services.io.getProtocolHandler("moz-extension"); - handler.QueryInterface(Ci.nsISubstitutingProtocolHandler); - handler.setSubstitution(uuid, null); - }, - - // Return true if the given URI can be loaded from arbitrary web - // content. The manifest.json |web_accessible_resources| directive - // determines this. - extensionURILoadableByAnyone(uri) { - let uuid = uri.host; - let extension = this.uuidMap.get(uuid); - if (!extension) { - return false; - } - - let path = uri.path; - if (path.length > 0 && path[0] == '/') { - path = path.substr(1); - } - return extension.webAccessibleResources.has(path); - }, - - // Checks whether a given extension can load this URI (typically via - // an XML HTTP request). The manifest.json |permissions| directive - // determines this. - checkAddonMayLoad(extension, uri) { - return extension.whiteListedHosts.matchesIgnoringPath(uri); - }, - - // Finds the add-on ID associated with a given moz-extension:// URI. - // This is used to set the addonId on the originAttributes for the - // nsIPrincipal attached to the URI. - extensionURIToAddonID(uri) { - if (this.extensionURILoadableByAnyone(uri)) { - // We don't want webAccessibleResources to be associated with - // the add-on. That way they don't get any special privileges. - return null; - } - - let uuid = uri.host; - let extension = this.uuidMap.get(uuid); - return extension ? extension.id : undefined; - }, -}; - -let ExtensionManagement = { - startupExtension: Service.startupExtension.bind(Service), - shutdownExtension: Service.shutdownExtension.bind(Service), - - registerScript: Scripts.register.bind(Scripts), - getScripts: Scripts.getScripts.bind(Scripts), - - getFrameId: Frames.getId.bind(Frames), - getParentFrameId: Frames.getParentId.bind(Frames), -}; - diff --git a/toolkit/components/extensions/ExtensionStorage.jsm b/toolkit/components/extensions/ExtensionStorage.jsm deleted file mode 100644 index 125e03ea4943..000000000000 --- a/toolkit/components/extensions/ExtensionStorage.jsm +++ /dev/null @@ -1,149 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["ExtensionStorage"]; - -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/osfile.jsm") -Cu.import("resource://gre/modules/AsyncShutdown.jsm"); - -let Path = OS.Path; -let profileDir = OS.Constants.Path.profileDir; - -let ExtensionStorage = { - cache: new Map(), - listeners: new Map(), - - extensionDir: Path.join(profileDir, "browser-extension-data"), - - getExtensionDir(extensionId) { - return Path.join(this.extensionDir, extensionId); - }, - - getStorageFile(extensionId) { - return Path.join(this.extensionDir, extensionId, "storage.js"); - }, - - read(extensionId) { - if (this.cache.has(extensionId)) { - return this.cache.get(extensionId); - } - - let path = this.getStorageFile(extensionId); - let decoder = new TextDecoder(); - let promise = OS.File.read(path); - promise = promise.then(array => { - return JSON.parse(decoder.decode(array)); - }).catch(() => { - Cu.reportError("Unable to parse JSON data for extension storage."); - return {}; - }); - this.cache.set(extensionId, promise); - return promise; - }, - - write(extensionId) { - let promise = this.read(extensionId).then(extData => { - let encoder = new TextEncoder(); - let array = encoder.encode(JSON.stringify(extData)); - let path = this.getStorageFile(extensionId); - OS.File.makeDir(this.getExtensionDir(extensionId), {ignoreExisting: true, from: profileDir}); - let promise = OS.File.writeAtomic(path, array); - return promise; - }).catch(() => { - // Make sure this promise is never rejected. - Cu.reportError("Unable to write JSON data for extension storage."); - }); - - AsyncShutdown.profileBeforeChange.addBlocker( - "ExtensionStorage: Finish writing extension data", - promise); - - return promise.then(() => { - AsyncShutdown.profileBeforeChange.removeBlocker(promise); - }); - }, - - set(extensionId, items) { - return this.read(extensionId).then(extData => { - let changes = {}; - for (let prop in items) { - changes[prop] = {oldValue: extData[prop], newValue: items[prop]}; - extData[prop] = items[prop]; - } - - let listeners = this.listeners.get(extensionId); - if (listeners) { - for (let listener of listeners) { - listener(changes); - } - } - - return this.write(extensionId); - }); - }, - - remove(extensionId, items) { - return this.read(extensionId).then(extData => { - let changes = {}; - for (let prop in items) { - changes[prop] = {oldValue: extData[prop]}; - delete extData[prop]; - } - - let listeners = this.listeners.get(extensionId); - if (listeners) { - for (let listener of listeners) { - listener(changes); - } - } - - return this.write(extensionId); - }); - }, - - get(extensionId, keys) { - return this.read(extensionId).then(extData => { - let result = {}; - if (keys === null) { - Object.assign(result, extData); - } else if (typeof(keys) == "object") { - for (let prop in keys) { - if (prop in extData) { - result[prop] = extData[prop]; - } else { - result[prop] = keys[prop]; - } - } - } else if (typeof(keys) == "string") { - result[prop] = extData[prop] || undefined; - } else { - for (let prop of keys) { - result[prop] = extData[prop] || undefined; - } - } - - return result; - }); - }, - - addOnChangedListener(extensionId, listener) { - let listeners = this.listeners.get(extensionId) || new Set(); - listeners.add(listener); - this.listeners.set(extensionId, listeners); - }, - - removeOnChangedListener(extensionId, listener) { - let listeners = this.listeners.get(extensionId); - listeners.delete(listener); - }, -}; diff --git a/toolkit/components/extensions/ExtensionUtils.jsm b/toolkit/components/extensions/ExtensionUtils.jsm deleted file mode 100644 index a92185ab8015..000000000000 --- a/toolkit/components/extensions/ExtensionUtils.jsm +++ /dev/null @@ -1,540 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const EXPORTED_SYMBOLS = ["ExtensionUtils"]; - -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cu = Components.utils; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -// Run a function and report exceptions. -function runSafeWithoutClone(f, ...args) -{ - try { - return f(...args); - } catch (e) { - dump(`Extension error: ${e} ${e.fileName} ${e.lineNumber}\n${e.stack}\n${Error().stack}`); - Cu.reportError(e); - } -} - -// Run a function, cloning arguments into context.cloneScope, and -// report exceptions. |f| is expected to be in context.cloneScope. -function runSafe(context, f, ...args) -{ - try { - args = Cu.cloneInto(args, context.cloneScope); - } catch (e) { - dump(`runSafe failure\n${context.cloneScope}\n${Error().stack}`); - } - return runSafeWithoutClone(f, ...args); -} - -// Similar to a WeakMap, but returns a particular default value for -// |get| if a key is not present. -function DefaultWeakMap(defaultValue) -{ - this.defaultValue = defaultValue; - this.weakmap = new WeakMap(); -} - -DefaultWeakMap.prototype = { - get(key) { - if (this.weakmap.has(key)) { - return this.weakmap.get(key); - } - return this.defaultValue; - }, - - set(key, value) { - if (key) { - this.weakmap.set(key, value); - } else { - this.defaultValue = value; - } - }, -}; - -// This is a generic class for managing event listeners. Example usage: -// -// new EventManager(context, "api.subAPI", fire => { -// let listener = (...) => { -// // Fire any listeners registered with addListener. -// fire(arg1, arg2); -// }; -// // Register the listener. -// SomehowRegisterListener(listener); -// return () => { -// // Return a way to unregister the listener. -// SomehowUnregisterListener(listener); -// }; -// }).api() -// -// The result is an object with addListener, removeListener, and -// hasListener methods. |context| is an add-on scope (either an -// ExtensionPage in the chrome process or ExtensionContext in a -// content process). |name| is for debugging. |register| is a function -// to register the listener. |register| is only called once, event if -// multiple listeners are registered. |register| should return an -// unregister function that will unregister the listener. -function EventManager(context, name, register) -{ - this.context = context; - this.name = name; - this.register = register; - this.unregister = null; - this.callbacks = new Set(); - this.registered = false; -} - -EventManager.prototype = { - addListener(callback) { - if (!this.registered) { - this.context.callOnClose(this); - - let fireFunc = this.fire.bind(this); - let fireWithoutClone = this.fireWithoutClone.bind(this); - fireFunc.withoutClone = fireWithoutClone; - this.unregister = this.register(fireFunc); - } - this.callbacks.add(callback); - }, - - removeListener(callback) { - if (!this.registered) { - return; - } - - this.callbacks.delete(callback); - if (this.callbacks.length == 0) { - this.unregister(); - - this.context.forgetOnClose(this); - } - }, - - hasListener(callback) { - return this.callbacks.has(callback); - }, - - fire(...args) { - for (let callback of this.callbacks) { - runSafe(this.context, callback, ...args); - } - }, - - fireWithoutClone(...args) { - for (let callback of this.callbacks) { - runSafeWithoutClone(callback, ...args); - } - }, - - close() { - this.unregister(); - }, - - api() { - return { - addListener: callback => this.addListener(callback), - removeListener: callback => this.removeListener(callback), - hasListener: callback => this.hasListener(callback), - }; - }, -}; - -// Similar to EventManager, but it doesn't try to consolidate event -// notifications. Each addListener call causes us to register once. It -// allows extra arguments to be passed to addListener. -function SingletonEventManager(context, name, register) -{ - this.context = context; - this.name = name; - this.register = register; - this.unregister = new Map(); - context.callOnClose(this); -} - -SingletonEventManager.prototype = { - addListener(callback, ...args) { - let unregister = this.register(callback, ...args); - this.unregister.set(callback, unregister); - }, - - removeListener(callback) { - if (!this.unregister.has(callback)) { - return; - } - - let unregister = this.unregister.get(callback); - this.unregister.delete(callback); - this.unregister(); - }, - - hasListener(callback) { - return this.unregister.has(callback); - }, - - close() { - for (let unregister of this.unregister.values()) { - unregister(); - } - }, - - api() { - return { - addListener: (...args) => this.addListener(...args), - removeListener: (...args) => this.removeListener(...args), - hasListener: (...args) => this.hasListener(...args), - }; - }, -}; - -// Simple API for event listeners where events never fire. -function ignoreEvent() -{ - return { - addListener: function(context, callback) {}, - removeListener: function(context, callback) {}, - hasListener: function(context, callback) {}, - }; -} - -// Copy an API object from |source| into the scope |dest|. -function injectAPI(source, dest) -{ - for (let prop in source) { - // Skip names prefixed with '_'. - if (prop[0] == '_') { - continue; - } - - let value = source[prop]; - if (typeof(value) == "function") { - Cu.exportFunction(value, dest, {defineAs: prop}); - } else if (typeof(value) == "object") { - let obj = Cu.createObjectIn(dest, {defineAs: prop}); - injectAPI(value, obj); - } else { - dest[prop] = value; - } - } -} - -/* - * Messaging primitives. - */ - -let nextBrokerId = 1; - -let MESSAGES = [ - "Extension:Message", - "Extension:Connect", -]; - -// Receives messages from multiple message managers and directs them -// to a set of listeners. On the child side: one broker per frame -// script. On the parent side: one broker total, covering both the -// global MM and the ppmm. Message must be tagged with a recipient, -// which is an object with properties. Listeners can filter for -// messages that have a certain value for a particular property in the -// recipient. (If a message doesn't specify the given property, it's -// considered a match.) -function MessageBroker(messageManagers) -{ - this.messageManagers = messageManagers; - for (let mm of this.messageManagers) { - for (let message of MESSAGES) { - mm.addMessageListener(message, this); - } - } - - this.listeners = {message: [], connect: []}; -} - -MessageBroker.prototype = { - uninit() { - for (let mm of this.messageManagers) { - for (let message of MESSAGES) { - mm.removeMessageListener(message, this); - } - } - - this.listeners = null; - }, - - makeId() { - return nextBrokerId++; - }, - - addListener(type, listener, filter) { - this.listeners[type].push({filter, listener}); - }, - - removeListener(type, listener) { - let index = -1; - for (let i = 0; i < this.listeners[type].length; i++) { - if (this.listeners[type][i].listener == listener) { - this.listeners[type].splice(i, 1); - return; - } - } - }, - - runListeners(type, target, data) { - let listeners = []; - for (let {listener, filter} of this.listeners[type]) { - let pass = true; - for (let prop in filter) { - if (prop in data.recipient && filter[prop] != data.recipient[prop]) { - pass = false; - break; - } - } - - // Save up the list of listeners to call in case they modify the - // set of listeners. - if (pass) { - listeners.push(listener); - } - } - - for (let listener of listeners) { - listener(type, target, data.message, data.sender, data.recipient); - } - }, - - receiveMessage({name, data, target}) { - switch (name) { - case "Extension:Message": - this.runListeners("message", target, data); - break; - - case "Extension:Connect": - this.runListeners("connect", target, data); - break; - } - }, - - sendMessage(messageManager, type, message, sender, recipient) { - let data = {message, sender, recipient}; - let names = {message: "Extension:Message", connect: "Extension:Connect"}; - messageManager.sendAsyncMessage(names[type], data); - }, -}; - -// Abstraction for a Port object in the extension API. Each port has a unique ID. -function Port(context, messageManager, name, id, sender) -{ - this.context = context; - this.messageManager = messageManager; - this.name = name; - this.id = id; - this.listenerName = `Extension:Port-${this.id}`; - this.disconnectName = `Extension:Disconnect-${this.id}`; - this.sender = sender; - this.disconnected = false; -} - -Port.prototype = { - api() { - let portObj = Cu.createObjectIn(this.context.cloneScope); - - // We want a close() notification when the window is destroyed. - this.context.callOnClose(this); - - let publicAPI = { - name: this.name, - disconnect: () => { - this.disconnect(); - }, - postMessage: json => { - if (this.disconnected) { - throw "Attempt to postMessage on disconnected port"; - } - this.messageManager.sendAsyncMessage(this.listenerName, json); - }, - onDisconnect: new EventManager(this.context, "Port.onDisconnect", fire => { - let listener = () => { - if (!this.disconnected) { - fire(); - } - }; - - this.messageManager.addMessageListener(this.disconnectName, listener, true); - return () => { - this.messageManager.removeMessageListener(this.disconnectName, listener); - }; - }).api(), - onMessage: new EventManager(this.context, "Port.onMessage", fire => { - let listener = ({data}) => { - if (!this.disconnected) { - fire(data); - } - }; - - this.messageManager.addMessageListener(this.listenerName, listener); - return () => { - this.messageManager.removeMessageListener(this.listenerName, listener); - }; - }).api(), - }; - - if (this.sender) { - publicAPI.sender = this.sender; - } - - injectAPI(publicAPI, portObj); - return portObj; - }, - - disconnect() { - this.context.forgetOnClose(this); - this.disconnect = true; - this.messageManager.sendAsyncMessage(this.disconnectName); - }, - - close() { - this.disconnect(); - }, -}; - -function getMessageManager(target) -{ - if (target instanceof Ci.nsIDOMXULElement) { - return target.messageManager; - } else { - return target; - } -} - -// Each extension scope gets its own Messenger object. It handles the -// basics of sendMessage, onMessage, connect, and onConnect. -// -// |context| is the extension scope. -// |broker| is a MessageBroker used to receive and send messages. -// |sender| is an object describing the sender (usually giving its extensionId, tabId, etc.) -// |filter| is a recipient filter to apply to incoming messages from the broker. -// |delegate| is an object that must implement a few methods: -// getSender(context, messageManagerTarget, sender): returns a MessageSender -// See https://developer.chrome.com/extensions/runtime#type-MessageSender. -function Messenger(context, broker, sender, filter, delegate) -{ - this.context = context; - this.broker = broker; - this.sender = sender; - this.filter = filter; - this.delegate = delegate; -} - -Messenger.prototype = { - sendMessage(messageManager, msg, recipient, responseCallback) { - let id = this.broker.makeId(); - let replyName = `Extension:Reply-${id}`; - recipient.messageId = id; - this.broker.sendMessage(messageManager, "message", msg, this.sender, recipient); - - let onClose; - let listener = ({data: response}) => { - messageManager.removeMessageListener(replyName, listener); - this.context.forgetOnClose(onClose); - - if (response.gotData) { - // TODO: Handle failure to connect to the extension? - runSafe(this.context, responseCallback, response.data); - } - }; - onClose = { - close() { - messageManager.removeMessageListener(replyName, listener); - } - }; - if (responseCallback) { - messageManager.addMessageListener(replyName, listener); - this.context.callOnClose(onClose); - } - }, - - onMessage(name) { - return new EventManager(this.context, name, fire => { - let listener = (type, target, message, sender, recipient) => { - message = Cu.cloneInto(message, this.context.cloneScope); - if (this.delegate) { - this.delegate.getSender(this.context, target, sender); - } - sender = Cu.cloneInto(sender, this.context.cloneScope); - - let mm = getMessageManager(target); - let replyName = `Extension:Reply-${recipient.messageId}`; - - let valid = true, sent = false; - let sendResponse = data => { - if (!valid) { - return; - } - sent = true; - mm.sendAsyncMessage(replyName, {data, gotData: true}); - }; - sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope); - - let result = fire.withoutClone(message, sender, sendResponse); - if (result !== true) { - valid = false; - } - if (!sent) { - mm.sendAsyncMessage(replyName, {gotData: false}); - } - }; - - this.broker.addListener("message", listener, this.filter); - return () => { - this.broker.removeListener("message", listener); - }; - }).api(); - }, - - connect(messageManager, name, recipient) { - let portId = this.broker.makeId(); - let port = new Port(this.context, messageManager, name, portId, null); - let msg = {name, portId}; - this.broker.sendMessage(messageManager, "connect", msg, this.sender, recipient); - return port.api(); - }, - - onConnect(name) { - return new EventManager(this.context, name, fire => { - let listener = (type, target, message, sender, recipient) => { - let {name, portId} = message; - let mm = getMessageManager(target); - if (this.delegate) { - this.delegate.getSender(this.context, target, sender); - } - let port = new Port(this.context, mm, name, portId, sender); - fire.withoutClone(port.api()); - }; - - this.broker.addListener("connect", listener, this.filter); - return () => { - this.broker.removeListener("connect", listener); - }; - }).api(); - }, -}; - -let ExtensionUtils = { - runSafe, - DefaultWeakMap, - EventManager, - SingletonEventManager, - ignoreEvent, - injectAPI, - MessageBroker, - Messenger, -}; - diff --git a/toolkit/components/extensions/ext-alarms.js b/toolkit/components/extensions/ext-alarms.js deleted file mode 100644 index b534c674638c..000000000000 --- a/toolkit/components/extensions/ext-alarms.js +++ /dev/null @@ -1,168 +0,0 @@ -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - EventManager, - ignoreEvent, -} = ExtensionUtils; - -// WeakMap[Extension -> Set[Alarm]] -let alarmsMap = new WeakMap(); - -// WeakMap[Extension -> callback] -let alarmCallbacksMap = new WeakMap(); - -// Manages an alarm created by the extension (alarms API). -function Alarm(extension, name, alarmInfo) -{ - this.extension = extension; - this.name = name; - this.when = alarmInfo.when; - this.delayInMinutes = alarmInfo.delayInMinutes; - this.periodInMinutes = alarmInfo.periodInMinutes; - this.canceled = false; - - let delay, scheduledTime; - if (this.when) { - scheduledTime = this.when; - delay = this.when - Date.now(); - } else { - delay = this.delayInMinutes * 60 * 1000; - scheduledTime = Date.now() + delay; - } - - this.scheduledTime = scheduledTime; - - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT); - this.timer = timer; -} - -Alarm.prototype = { - clear() { - this.timer.cancel(); - alarmsMap.get(this.extension).delete(this); - this.canceled = true; - }, - - observe(subject, topic, data) { - if (alarmCallbacksMap.has(this.extension)) { - alarmCallbacksMap.get(this.extension)(this); - } - if (this.canceled) { - return; - } - - if (!this.periodInMinutes) { - this.clear(); - return; - } - - let delay = this.periodInMinutes * 60 * 1000; - this.scheduledTime = Date.now() + delay; - this.timer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT); - }, - - get data() { - return { - name: this.name, - scheduledTime: this.scheduledTime, - periodInMinutes: this.periodInMinutes, - }; - }, -}; - -extensions.on("startup", (type, extension) => { - alarmsMap.set(extension, new Set()); -}); - -extensions.on("shutdown", (type, extension) => { - for (let alarm of alarmsMap.get(extension)) { - alarm.clear(); - } - alarmsMap.delete(extension); -}); - -extensions.registerAPI((extension, context) => { - return { - alarms: { - create: function(...args) { - let name = "", alarmInfo; - if (args.length == 1) { - alarmInfo = args[0]; - } else { - [name, alarmInfo] = args; - } - - let alarm = new Alarm(extension, name, alarmInfo); - alarmsMap.get(extension).add(alarm); - }, - - get: function(args) { - let name = "", callback; - if (args.length == 1) { - callback = args[0]; - } else { - [name, callback] = args; - } - - for (let alarm of alarmsMap.get(extension)) { - if (alarm.name == name) { - runSafe(context, callback, alarm.data); - break; - } - } - }, - - getAll: function(callback) { - let alarms = alarmsMap.get(extension); - result = [ for (alarm of alarms) alarm.data ]; - runSafe(context, callback, result); - }, - - clear: function(...args) { - let name = "", callback; - if (args.length == 1) { - callback = args[0]; - } else { - [name, callback] = args; - } - - let alarms = alarmsMap.get(extension); - let cleared = false; - for (let alarm of alarms) { - if (alarm.name == name) { - alarm.clear(); - cleared = true; - break; - } - } - - if (callback) { - runSafe(context, callback, cleared); - } - }, - - clearAll: function(callback) { - let alarms = alarmsMap.get(extension); - let cleared = false; - for (let alarm of alarms) { - alarm.clear(); - cleared = true; - } - if (callback) { - runSafe(context, callback, cleared); - } - }, - - onAlarm: new EventManager(context, "alarms.onAlarm", fire => { - let callback = alarm => { - fire(alarm.data); - }; - - alarmCallbacksMap.set(extension, callback); - return () => { - alarmCallbacksMap.delete(extension); - }; - }).api(), - }, - }; -}); diff --git a/toolkit/components/extensions/ext-backgroundPage.js b/toolkit/components/extensions/ext-backgroundPage.js deleted file mode 100644 index 4cdf28054205..000000000000 --- a/toolkit/components/extensions/ext-backgroundPage.js +++ /dev/null @@ -1,92 +0,0 @@ -// WeakMap[Extension -> BackgroundPage] -let backgroundPagesMap = new WeakMap(); - -// Responsible for the background_page section of the manifest. -function BackgroundPage(options, extension) -{ - this.extension = extension; - this.scripts = options.scripts || []; - this.page = options.page || null; - this.contentWindow = null; - this.webNav = null; - this.context = null; -} - -BackgroundPage.prototype = { - build() { - let webNav = Services.appShell.createWindowlessBrowser(false); - this.webNav = webNav; - - let principal = Services.scriptSecurityManager.createCodebasePrincipal(this.extension.baseURI, - {addonId: this.extension.id}); - - let interfaceRequestor = webNav.QueryInterface(Ci.nsIInterfaceRequestor); - let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell); - - this.context = new ExtensionPage(this.extension, {type: "background", docShell}); - GlobalManager.injectInDocShell(docShell, this.extension, this.context); - - docShell.createAboutBlankContentViewer(principal); - - let window = webNav.document.defaultView; - this.contentWindow = window; - this.context.contentWindow = window; - - let url; - if (this.page) { - url = this.extension.baseURI.resolve(this.page); - } else { - url = this.extension.baseURI.resolve("_blank.html"); - } - webNav.loadURI(url, 0, null, null, null); - - // TODO: Right now we run onStartup after the background page - // finishes. See if this is what Chrome does. - window.windowRoot.addEventListener("load", () => { - if (this.scripts) { - let doc = window.document; - for (let script of this.scripts) { - let url = this.extension.baseURI.resolve(script); - let tag = doc.createElement("script"); - tag.setAttribute("src", url); - tag.async = false; - doc.body.appendChild(tag); - } - } - - if (this.extension.onStartup) { - this.extension.onStartup(); - } - }, true); - }, - - shutdown() { - // Navigate away from the background page to invalidate any - // setTimeouts or other callbacks. - this.webNav.loadURI("about:blank", 0, null, null, null); - this.webNav = null; - }, -}; - -extensions.on("manifest_background", (type, directive, extension, manifest) => { - let bgPage = new BackgroundPage(manifest.background, extension); - bgPage.build(); - backgroundPagesMap.set(extension, bgPage); -}); - -extensions.on("shutdown", (type, extension) => { - if (backgroundPagesMap.has(extension)) { - backgroundPagesMap.get(extension).shutdown(); - backgroundPagesMap.delete(extension); - } -}); - -extensions.registerAPI((extension, context) => { - return { - extension: { - getBackgroundPage: function() { - return backgroundPagesMap.get(extension).contentWindow; - }, - }, - }; -}); diff --git a/toolkit/components/extensions/ext-extension.js b/toolkit/components/extensions/ext-extension.js deleted file mode 100644 index deaebebc4fed..000000000000 --- a/toolkit/components/extensions/ext-extension.js +++ /dev/null @@ -1,10 +0,0 @@ -extensions.registerAPI((extension, context) => { - return { - extension: { - getURL: function(url) { - return extension.baseURI.resolve(url); - }, - }, - }; -}); - diff --git a/toolkit/components/extensions/ext-i18n.js b/toolkit/components/extensions/ext-i18n.js deleted file mode 100644 index 769331e8ad7e..000000000000 --- a/toolkit/components/extensions/ext-i18n.js +++ /dev/null @@ -1,9 +0,0 @@ -extensions.registerAPI((extension, context) => { - return { - i18n: { - getMessage: function(messageName, substitutions) { - return extension.localizeMessage(messageName, substitutions); - }, - }, - }; -}); diff --git a/toolkit/components/extensions/ext-idle.js b/toolkit/components/extensions/ext-idle.js deleted file mode 100644 index 96bc3ead80bd..000000000000 --- a/toolkit/components/extensions/ext-idle.js +++ /dev/null @@ -1,9 +0,0 @@ -extensions.registerPrivilegedAPI("idle", (extension, context) => { - return { - idle: { - queryState: function(detectionIntervalInSeconds, callback) { - runSafe(context, callback, "active"); - }, - }, - }; -}); diff --git a/toolkit/components/extensions/ext-notifications.js b/toolkit/components/extensions/ext-notifications.js deleted file mode 100644 index 0138154408cf..000000000000 --- a/toolkit/components/extensions/ext-notifications.js +++ /dev/null @@ -1,140 +0,0 @@ -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - EventManager, - ignoreEvent, -} = ExtensionUtils; - -// WeakMap[Extension -> Set[Notification]] -let notificationsMap = new WeakMap(); - -// WeakMap[Extension -> callback] -let notificationCallbacksMap = new WeakMap(); - -// Manages a notification popup (notifications API) created by the extension. -function Notification(extension, id, options) -{ - this.extension = extension; - this.id = id; - this.options = options; - - let imageURL; - if (options.iconUrl) { - imageURL = this.extension.baseURI.resolve(options.iconUrl); - } - - try { - let svc = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); - svc.showAlertNotification(imageURL, - options.title, - options.message, - false, // textClickable - this.id, - this, - this.id); - } catch (e) { - // This will fail if alerts aren't available on the system. - } -} - -Notification.prototype = { - clear() { - try { - let svc = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); - svc.closeAlert(this.id); - } catch (e) { - // This will fail if the OS doesn't support this function. - } - notificationsMap.get(this.extension).delete(this); - }, - - observe(subject, topic, data) { - if (topic != "alertfinished") { - return; - } - - if (notificationCallbacksMap.has(this.extension)) { - notificationCallbackMap.get(this.extension)(this); - } - - notificationsMap.get(this.extension).delete(this); - }, -}; - -extensions.on("startup", (type, extension) => { - notificationsMap.set(extension, new Set()); -}); - -extensions.on("shutdown", (type, extension) => { - for (let notification of notificationsMap.get(extension)) { - notification.clear(); - } - notificationsMap.delete(extension); -}); - -let nextId = 0; - -extensions.registerPrivilegedAPI("notifications", (extension, context) => { - return { - notifications: { - create: function(...args) { - let notificationId, options, callback; - if (args.length == 1) { - options = args[0]; - } else { - [notificationId, options, callback] = args; - } - - if (!notificationId) { - notificationId = nextId++; - } - - // FIXME: Lots of options still aren't supported, especially - // buttons. - let notification = new Notification(extension, notificationId, options); - notificationsMap.get(extension).add(notification); - - if (callback) { - runSafe(context, callback, notificationId); - } - }, - - clear: function(notificationId, callback) { - let notifications = notificationsMap.get(extension); - let cleared = false; - for (let notification of notifications) { - if (notification.id == notificationId) { - notification.clear(); - cleared = true; - break; - } - } - - if (callback) { - runSafe(context, callback, cleared); - } - }, - - getAll: function(callback) { - let notifications = notificationsMap.get(extension); - notifications = [ for (notification of notifications) notification.id ]; - runSafe(context, callback, notifications); - }, - - onClosed: new EventManager(context, "notifications.onClosed", fire => { - let listener = notification => { - // FIXME: Support the byUser argument. - fire(notification.id, true); - }; - - notificationCallbackMap.set(extension, listener); - return () => { - notificationCallbackMap.delete(extension); - }; - }).api(), - - // FIXME - onButtonClicked: ignoreEvent(), - onClicked: ignoreEvent(), - }, - }; -}); diff --git a/toolkit/components/extensions/ext-runtime.js b/toolkit/components/extensions/ext-runtime.js deleted file mode 100644 index e9c633b26c00..000000000000 --- a/toolkit/components/extensions/ext-runtime.js +++ /dev/null @@ -1,47 +0,0 @@ -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - EventManager, - ignoreEvent, -} = ExtensionUtils; - -extensions.registerAPI((extension, context) => { - return { - runtime: { - onStartup: new EventManager(context, "runtime.onStartup", fire => { - extension.onStartup = fire; - return () => { - extension.onStartup = null; - }; - }).api(), - - onInstalled: ignoreEvent(), - - onMessage: context.messenger.onMessage("runtime.onMessage"), - - onConnect: context.messenger.onConnect("runtime.onConnect"), - - sendMessage: function(...args) { - let extensionId, message, options, responseCallback; - if (args.length == 1) { - message = args[0]; - } else if (args.length == 2) { - [message, responseCallback] = args; - } else { - [extensionId, message, options, responseCallback] = args; - } - let recipient = {extensionId: extensionId ? extensionId : extension.id}; - return context.messenger.sendMessage(Services.cpmm, message, recipient, responseCallback); - }, - - getManifest() { - return Cu.cloneInto(extension.manifest, context.cloneScope); - }, - - id: extension.id, - - getURL: function(url) { - return extension.baseURI.resolve(url); - }, - }, - }; -}); diff --git a/toolkit/components/extensions/ext-storage.js b/toolkit/components/extensions/ext-storage.js deleted file mode 100644 index c6c1530460ad..000000000000 --- a/toolkit/components/extensions/ext-storage.js +++ /dev/null @@ -1,48 +0,0 @@ -XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage", - "resource://gre/modules/ExtensionStorage.jsm"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - EventManager, - ignoreEvent, - runSafe, -} = ExtensionUtils; - -extensions.registerPrivilegedAPI("storage", (extension, context) => { - return { - storage: { - local: { - get: function(keys, callback) { - ExtensionStorage.get(extension.id, keys).then(result => { - runSafe(context, callback, result); - }); - }, - set: function(items, callback) { - ExtensionStorage.set(extension.id, items).then(() => { - if (callback) { - runSafe(context, callback); - } - }); - }, - remove: function(items, callback) { - ExtensionStorage.remove(extension.id, items).then(() => { - if (callback) { - runSafe(context, callback); - } - }); - }, - }, - - onChanged: new EventManager(context, "storage.local.onChanged", fire => { - let listener = changes => { - fire(changes, "local"); - }; - - ExtensionStorage.addOnChangedListener(extension.id, listener); - return () => { - ExtensionStorage.removeOnChangedListener(extension.id, listener); - }; - }).api(), - }, - }; -}); diff --git a/toolkit/components/extensions/ext-webNavigation.js b/toolkit/components/extensions/ext-webNavigation.js deleted file mode 100644 index 32d63f1c8be9..000000000000 --- a/toolkit/components/extensions/ext-webNavigation.js +++ /dev/null @@ -1,70 +0,0 @@ -XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement", - "resource://gre/modules/ExtensionManagement.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern", - "resource://gre/modules/MatchPattern.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "WebNavigation", - "resource://gre/modules/WebNavigation.jsm"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - SingletonEventManager, - ignoreEvent, - runSafe, -} = ExtensionUtils; - -// Similar to WebRequestEventManager but for WebNavigation. -function WebNavigationEventManager(context, eventName) -{ - let name = `webNavigation.${eventName}`; - let register = callback => { - let listener = data => { - if (!data.browser) { - return; - } - - let tabId = TabManager.getBrowserId(data.browser); - if (tabId == -1) { - return; - } - - let data2 = { - url: data.url, - timeStamp: Date.now(), - frameId: ExtensionManagement.getFrameId(data.windowId), - parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId), - }; - - // Fills in tabId typically. - let result = {}; - extensions.emit("fill-browser-data", data.browser, data2, result); - if (result.cancel) { - return; - } - - return runSafe(context, callback, data2); - }; - - WebNavigation[eventName].addListener(listener); - return () => { - WebNavigation[eventName].removeListener(listener); - }; - }; - - return SingletonEventManager.call(this, context, name, register); -} - -WebNavigationEventManager.prototype = Object.create(SingletonEventManager.prototype); - -extensions.registerPrivilegedAPI("webNavigation", (extension, context) => { - return { - webNavigation: { - onBeforeNavigate: new WebNavigationEventManager(context, "onBeforeNavigate").api(), - onCommitted: new WebNavigationEventManager(context, "onCommitted").api(), - onDOMContentLoaded: new WebNavigationEventManager(context, "onDOMContentLoaded").api(), - onCompleted: new WebNavigationEventManager(context, "onCompleted").api(), - onErrorOccurred: new WebNavigationEventManager(context, "onErrorOccurred").api(), - onReferenceFragmentUpdated: new WebNavigationEventManager(context, "onReferenceFragmentUpdated").api(), - onCreatedNavigationTarget: ignoreEvent(), - }, - }; -}); diff --git a/toolkit/components/extensions/ext-webRequest.js b/toolkit/components/extensions/ext-webRequest.js deleted file mode 100644 index b6169c381c92..000000000000 --- a/toolkit/components/extensions/ext-webRequest.js +++ /dev/null @@ -1,104 +0,0 @@ -XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern", - "resource://gre/modules/MatchPattern.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "WebRequest", - "resource://gre/modules/WebRequest.jsm"); - -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); -let { - SingletonEventManager, - runSafe, -} = ExtensionUtils; - -// EventManager-like class specifically for WebRequest. Inherits from -// SingletonEventManager. Takes care of converting |details| parameter -// when invoking listeners. -function WebRequestEventManager(context, eventName) -{ - let name = `webRequest.${eventName}`; - let register = (callback, filter, info) => { - let listener = data => { - if (!data.browser) { - return; - } - - let tabId = TabManager.getBrowserId(data.browser); - if (tabId == -1) { - return; - } - - let data2 = { - url: data.url, - method: data.method, - type: data.type, - timeStamp: Date.now(), - frameId: ExtensionManagement.getFrameId(data.windowId), - parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId), - }; - - // Fills in tabId typically. - let result = {}; - extensions.emit("fill-browser-data", data.browser, data2, result); - if (result.cancel) { - return; - } - - let optional = ["requestHeaders", "responseHeaders", "statusCode"]; - for (let opt of optional) { - if (opt in data) { - data2[opt] = data[opt]; - } - } - - return runSafe(context, callback, data2); - }; - - let filter2 = {}; - filter2.urls = new MatchPattern(filter.urls); - if (filter.types) { - filter2.types = filter.types; - } - if (filter.tabId) { - filter2.tabId = filter.tabId; - } - if (filter.windowId) { - filter2.windowId = filter.windowId; - } - - let info2 = []; - if (info) { - for (let desc of info) { - if (desc == "blocking" && !context.extension.hasPermission("webRequestBlocking")) { - Cu.reportError("Using webRequest.addListener with the blocking option " + - "requires the 'webRequestBlocking' permission."); - } else { - info2.push(desc); - } - } - } - - WebRequest[eventName].addListener(listener, filter2, info2); - return () => { - WebRequest[eventName].removeListener(listener); - }; - }; - - return SingletonEventManager.call(this, context, name, register); -} - -WebRequestEventManager.prototype = Object.create(SingletonEventManager.prototype); - -extensions.registerPrivilegedAPI("webRequest", (extension, context) => { - return { - webRequest: { - onBeforeRequest: new WebRequestEventManager(context, "onBeforeRequest").api(), - onBeforeSendHeaders: new WebRequestEventManager(context, "onBeforeSendHeaders").api(), - onSendHeaders: new WebRequestEventManager(context, "onSendHeaders").api(), - onHeadersReceived: new WebRequestEventManager(context, "onHeadersReceived").api(), - onResponseStarted: new WebRequestEventManager(context, "onResponseStarted").api(), - onCompleted: new WebRequestEventManager(context, "onCompleted").api(), - handlerBehaviorChanged: function() { - // TODO: Flush all caches. - }, - }, - }; -}); diff --git a/toolkit/components/extensions/jar.mn b/toolkit/components/extensions/jar.mn deleted file mode 100644 index 8f48a5653733..000000000000 --- a/toolkit/components/extensions/jar.mn +++ /dev/null @@ -1,16 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -toolkit.jar: -% content extensions %content/extensions/ - content/extensions/ext-alarms.js (ext-alarms.js) - content/extensions/ext-backgroundPage.js (ext-backgroundPage.js) - content/extensions/ext-notifications.js (ext-notifications.js) - content/extensions/ext-i18n.js (ext-i18n.js) - content/extensions/ext-idle.js (ext-idle.js) - content/extensions/ext-webRequest.js (ext-webRequest.js) - content/extensions/ext-webNavigation.js (ext-webNavigation.js) - content/extensions/ext-runtime.js (ext-runtime.js) - content/extensions/ext-extension.js (ext-extension.js) - content/extensions/ext-storage.js (ext-storage.js) diff --git a/toolkit/components/extensions/moz.build b/toolkit/components/extensions/moz.build deleted file mode 100644 index d6f48aa67686..000000000000 --- a/toolkit/components/extensions/moz.build +++ /dev/null @@ -1,15 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXTRA_JS_MODULES += [ - 'Extension.jsm', - 'ExtensionContent.jsm', - 'ExtensionManagement.jsm', - 'ExtensionStorage.jsm', - 'ExtensionUtils.jsm', -] - -JAR_MANIFESTS += ['jar.mn'] diff --git a/toolkit/components/moz.build b/toolkit/components/moz.build index 479c90c58372..493e396881ff 100644 --- a/toolkit/components/moz.build +++ b/toolkit/components/moz.build @@ -22,7 +22,6 @@ DIRS += [ 'crashmonitor', 'diskspacewatcher', 'downloads', - 'extensions', 'exthelper', 'filepicker', 'filewatcher', diff --git a/toolkit/components/utils/simpleServices.js b/toolkit/components/utils/simpleServices.js index 608c62475803..69b727d9eced 100644 --- a/toolkit/components/utils/simpleServices.js +++ b/toolkit/components/utils/simpleServices.js @@ -104,11 +104,7 @@ AddonPolicyService.prototype = { * directly. */ setAddonLoadURICallback(aAddonId, aCallback) { - if (aCallback) { - this.mayLoadURICallbacks[aAddonId] = aCallback; - } else { - delete this.mayLoadURICallbacks[aAddonId]; - } + this.mayLoadURICallbacks[aAddonId] = aCallback; }, /* diff --git a/toolkit/modules/Locale.jsm b/toolkit/modules/Locale.jsm deleted file mode 100644 index 3b76a0071da0..000000000000 --- a/toolkit/modules/Locale.jsm +++ /dev/null @@ -1,93 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -this.EXPORTED_SYMBOLS = ["Locale"]; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); - -const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; -const PREF_SELECTED_LOCALE = "general.useragent.locale"; - -let Locale = { - /** - * Gets the currently selected locale for display. - * @return the selected locale or "en-US" if none is selected - */ - getLocale() { - if (Preferences.get(PREF_MATCH_OS_LOCALE, false)) - return Services.locale.getLocaleComponentForUserAgent(); - try { - let locale = Preferences.get(PREF_SELECTED_LOCALE, null, Ci.nsIPrefLocalizedString); - if (locale) - return locale; - } - catch (e) {} - return Preferences.get(PREF_SELECTED_LOCALE, "en-US"); - }, - - /** - * Selects the closest matching locale from a list of locales. - * - * @param aLocales - * An array of locales - * @return the best match for the currently selected locale - */ - findClosestLocale(aLocales) { - let appLocale = this.getLocale(); - - // Holds the best matching localized resource - var bestmatch = null; - // The number of locale parts it matched with - var bestmatchcount = 0; - // The number of locale parts in the match - var bestpartcount = 0; - - var matchLocales = [appLocale.toLowerCase()]; - /* If the current locale is English then it will find a match if there is - a valid match for en-US so no point searching that locale too. */ - if (matchLocales[0].substring(0, 3) != "en-") - matchLocales.push("en-us"); - - for (let locale of matchLocales) { - var lparts = locale.split("-"); - for (let localized of aLocales) { - for (let found of localized.locales) { - found = found.toLowerCase(); - // Exact match is returned immediately - if (locale == found) - return localized; - - var fparts = found.split("-"); - /* If we have found a possible match and this one isn't any longer - then we dont need to check further. */ - if (bestmatch && fparts.length < bestmatchcount) - continue; - - // Count the number of parts that match - var maxmatchcount = Math.min(fparts.length, lparts.length); - var matchcount = 0; - while (matchcount < maxmatchcount && - fparts[matchcount] == lparts[matchcount]) - matchcount++; - - /* If we matched more than the last best match or matched the same and - this locale is less specific than the last best match. */ - if (matchcount > bestmatchcount || - (matchcount == bestmatchcount && fparts.length < bestpartcount)) { - bestmatch = localized; - bestmatchcount = matchcount; - bestpartcount = fparts.length; - } - } - } - // If we found a valid match for this locale return it - if (bestmatch) - return bestmatch; - } - return null; - }, -}; diff --git a/toolkit/modules/addons/MatchPattern.jsm b/toolkit/modules/addons/MatchPattern.jsm index 86f089f66f00..91d4bf6b30ca 100644 --- a/toolkit/modules/addons/MatchPattern.jsm +++ b/toolkit/modules/addons/MatchPattern.jsm @@ -63,7 +63,7 @@ function SingleMatchPattern(pat) } SingleMatchPattern.prototype = { - matches(uri, ignorePath = false) { + matches(uri) { if (this.scheme.indexOf(uri.scheme) == -1) { return false; } @@ -83,7 +83,7 @@ SingleMatchPattern.prototype = { } } - if (!ignorePath && !this.path.test(uri.path)) { + if (!this.path.test(uri.path)) { return false; } @@ -114,15 +114,6 @@ MatchPattern.prototype = { return false; }, - matchesIgnoringPath(uri) { - for (let matcher of this.matchers) { - if (matcher.matches(uri, true)) { - return true; - } - } - return false; - }, - serialize() { return this.pat; }, diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index 2d8e15397992..2a0cfb3f3f5b 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -36,7 +36,6 @@ EXTRA_JS_MODULES += [ 'InlineSpellChecker.jsm', 'InlineSpellCheckerContent.jsm', 'LoadContextInfo.jsm', - 'Locale.jsm', 'Log.jsm', 'NewTabUtils.jsm', 'ObjectUtils.jsm', diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index cb59cee344bb..d363960abcac 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -22,8 +22,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "ChromeManifestParser", "resource://gre/modules/ChromeManifestParser.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Locale", - "resource://gre/modules/Locale.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ZipUtils", @@ -69,6 +67,8 @@ const PREF_INSTALL_CACHE = "extensions.installCache"; const PREF_XPI_STATE = "extensions.xpiState"; const PREF_BOOTSTRAP_ADDONS = "extensions.bootstrappedAddons"; const PREF_PENDING_OPERATIONS = "extensions.pendingOperations"; +const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; +const PREF_SELECTED_LOCALE = "general.useragent.locale"; const PREF_EM_DSS_ENABLED = "extensions.dss.enabled"; const PREF_DSS_SWITCHPENDING = "extensions.dss.switchPending"; const PREF_DSS_SKIN_TO_SELECT = "extensions.lastSelectedSkin"; @@ -514,6 +514,84 @@ SafeInstallOperation.prototype = { } }; +/** + * Gets the currently selected locale for display. + * @return the selected locale or "en-US" if none is selected + */ +function getLocale() { + if (Preferences.get(PREF_MATCH_OS_LOCALE, false)) + return Services.locale.getLocaleComponentForUserAgent(); + try { + let locale = Preferences.get(PREF_SELECTED_LOCALE, null, Ci.nsIPrefLocalizedString); + if (locale) + return locale; + } + catch (e) {} + return Preferences.get(PREF_SELECTED_LOCALE, "en-US"); +} + +/** + * Selects the closest matching locale from a list of locales. + * + * @param aLocales + * An array of locales + * @return the best match for the currently selected locale + */ +function findClosestLocale(aLocales) { + let appLocale = getLocale(); + + // Holds the best matching localized resource + var bestmatch = null; + // The number of locale parts it matched with + var bestmatchcount = 0; + // The number of locale parts in the match + var bestpartcount = 0; + + var matchLocales = [appLocale.toLowerCase()]; + /* If the current locale is English then it will find a match if there is + a valid match for en-US so no point searching that locale too. */ + if (matchLocales[0].substring(0, 3) != "en-") + matchLocales.push("en-us"); + + for each (var locale in matchLocales) { + var lparts = locale.split("-"); + for each (var localized in aLocales) { + for each (let found in localized.locales) { + found = found.toLowerCase(); + // Exact match is returned immediately + if (locale == found) + return localized; + + var fparts = found.split("-"); + /* If we have found a possible match and this one isn't any longer + then we dont need to check further. */ + if (bestmatch && fparts.length < bestmatchcount) + continue; + + // Count the number of parts that match + var maxmatchcount = Math.min(fparts.length, lparts.length); + var matchcount = 0; + while (matchcount < maxmatchcount && + fparts[matchcount] == lparts[matchcount]) + matchcount++; + + /* If we matched more than the last best match or matched the same and + this locale is less specific than the last best match. */ + if (matchcount > bestmatchcount || + (matchcount == bestmatchcount && fparts.length < bestpartcount)) { + bestmatch = localized; + bestmatchcount = matchcount; + bestpartcount = fparts.length; + } + } + } + // If we found a valid match for this locale return it + if (bestmatch) + return bestmatch; + } + return null; +} + /** * Sets the userDisabled and softDisabled properties of an add-on based on what * values those properties had for a previous instance of the add-on. The @@ -6415,7 +6493,7 @@ AddonInternal.prototype = { get selectedLocale() { if (this._selectedLocale) return this._selectedLocale; - let locale = Locale.findClosestLocale(this.locales); + let locale = findClosestLocale(this.locales); this._selectedLocale = locale ? locale : this.defaultLocale; return this._selectedLocale; }, From 623d6495a4edc44b11bd8e345ea869600407a984 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 3 Aug 2015 09:38:27 +0200 Subject: [PATCH 107/113] Backed out changeset b821b18a1bfb (bug 1189550) for breaking GU Tests --- dom/base/nsFrameMessageManager.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index dd673f8ffd64..9d33b8d3ffeb 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -1432,14 +1432,6 @@ nsFrameMessageManager::GetInitialProcessData(JSContext* aCx, JS::MutableHandleVa init.setObject(*obj); } - if (!mChrome && XRE_IsParentProcess()) { - // This is the cpmm in the parent process. We should use the same object as the ppmm. - nsCOMPtr ppmm = - do_GetService("@mozilla.org/parentprocessmessagemanager;1"); - ppmm->GetInitialProcessData(aCx, &init); - mInitialProcessData = init; - } - if (!JS_WrapValue(aCx, &init)) { return NS_ERROR_OUT_OF_MEMORY; } @@ -2172,6 +2164,7 @@ NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult) NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED); global.forget(aResult); return NS_OK; + } static PLDHashOperator From c9982faad7a55682e748c36d0c06eb261e48255a Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 3 Aug 2015 14:34:57 +0200 Subject: [PATCH 108/113] Backed out changeset b8eacfe11fc5 (bug 1186748) --- .../tooltool-manifests/linux32/valgrind.manifest | 16 ++++++++++++++++ .../tooltool-manifests/linux64/valgrind.manifest | 16 ++++++++++++++++ build/valgrind/valgrind.sh | 2 +- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 browser/config/tooltool-manifests/linux32/valgrind.manifest create mode 100644 browser/config/tooltool-manifests/linux64/valgrind.manifest diff --git a/browser/config/tooltool-manifests/linux32/valgrind.manifest b/browser/config/tooltool-manifests/linux32/valgrind.manifest new file mode 100644 index 000000000000..80c50dfaea99 --- /dev/null +++ b/browser/config/tooltool-manifests/linux32/valgrind.manifest @@ -0,0 +1,16 @@ +[ +{ +"size": 80458572, +"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad", +"algorithm": "sha512", +"filename": "gcc.tar.xz", +"unpack": true +}, +{ +"size": 167175, +"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"algorithm": "sha512", +"filename": "sccache.tar.bz2", +"unpack": true +} +] diff --git a/browser/config/tooltool-manifests/linux64/valgrind.manifest b/browser/config/tooltool-manifests/linux64/valgrind.manifest new file mode 100644 index 000000000000..80c50dfaea99 --- /dev/null +++ b/browser/config/tooltool-manifests/linux64/valgrind.manifest @@ -0,0 +1,16 @@ +[ +{ +"size": 80458572, +"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad", +"algorithm": "sha512", +"filename": "gcc.tar.xz", +"unpack": true +}, +{ +"size": 167175, +"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"algorithm": "sha512", +"filename": "sccache.tar.bz2", +"unpack": true +} +] diff --git a/build/valgrind/valgrind.sh b/build/valgrind/valgrind.sh index c46f18876d01..9857792176a7 100755 --- a/build/valgrind/valgrind.sh +++ b/build/valgrind/valgrind.sh @@ -25,7 +25,7 @@ else _arch=32 fi -TOOLTOOL_MANIFEST=browser/config/tooltool-manifests/linux${_arch}/releng.manifest +TOOLTOOL_MANIFEST=browser/config/tooltool-manifests/linux${_arch}/valgrind.manifest TOOLTOOL_SERVER=https://api.pub.build.mozilla.org/tooltool/ (cd $srcdir; python /builds/tooltool.py --url $TOOLTOOL_SERVER --overwrite -m $TOOLTOOL_MANIFEST fetch ${TOOLTOOL_CACHE:+ -c ${TOOLTOOL_CACHE}}) || exit 2 From 9b61dabe975f58e679b2546fd39d494469de1e58 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 3 Aug 2015 14:35:03 +0200 Subject: [PATCH 109/113] Backed out changeset 2238409de271 (bug 1188780) --- .../linux32/releng.manifest | 5 +-- .../linux64/releng.manifest | 5 +-- .../linux32/releng.manifest | 5 +-- .../tooltool-manifests/linux64/clang.manifest | 5 +-- .../linux64/releng.manifest | 5 +-- .../tooltool-manifests/linux64/tsan.manifest | 5 +-- build/unix/build-gtk3/build-gtk3.sh | 41 +------------------ build/unix/mozconfig.gtk | 34 ++++++++++++++- .../devtools/rootAnalysis/build/gcc.manifest | 5 +-- 9 files changed, 47 insertions(+), 63 deletions(-) diff --git a/b2g/config/tooltool-manifests/linux32/releng.manifest b/b2g/config/tooltool-manifests/linux32/releng.manifest index c4ca7bcd97aa..79bbfa10a656 100644 --- a/b2g/config/tooltool-manifests/linux32/releng.manifest +++ b/b2g/config/tooltool-manifests/linux32/releng.manifest @@ -7,11 +7,10 @@ "unpack": true }, { -"size": 11179576, -"digest": "91567ce8e2bb8ab0ebc60c31e90731d88a1ea889fb71bcf55c735746a60fa7610b7e040ea3d8f727b6f692ae3ee703d6f3b30cdbd76fdf5617f77d9c38aa20ed", +"size": 4079256, +"digest": "bb5238558bcf6db2ca395513c8dccaa15dd61b3c375598eb6a685356b0c1a2d9840e3bf81bc00242b872fd798541f53d723777c754412abf0e772b7cc284937c", "algorithm": "sha512", "filename": "gtk3.tar.xz", -"setup": "setup.sh", "unpack": true }, { diff --git a/b2g/config/tooltool-manifests/linux64/releng.manifest b/b2g/config/tooltool-manifests/linux64/releng.manifest index 064a3f5aa234..d336d9b35e8d 100644 --- a/b2g/config/tooltool-manifests/linux64/releng.manifest +++ b/b2g/config/tooltool-manifests/linux64/releng.manifest @@ -7,11 +7,10 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 4431740, +"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3", "algorithm": "sha512", "filename": "gtk3.tar.xz", -"setup": "setup.sh", "unpack": true }, { diff --git a/browser/config/tooltool-manifests/linux32/releng.manifest b/browser/config/tooltool-manifests/linux32/releng.manifest index 9bf4aad7f53e..9bc6b0e01e29 100644 --- a/browser/config/tooltool-manifests/linux32/releng.manifest +++ b/browser/config/tooltool-manifests/linux32/releng.manifest @@ -7,11 +7,10 @@ "unpack": true }, { -"size": 11179576, -"digest": "91567ce8e2bb8ab0ebc60c31e90731d88a1ea889fb71bcf55c735746a60fa7610b7e040ea3d8f727b6f692ae3ee703d6f3b30cdbd76fdf5617f77d9c38aa20ed", +"size": 4079256, +"digest": "bb5238558bcf6db2ca395513c8dccaa15dd61b3c375598eb6a685356b0c1a2d9840e3bf81bc00242b872fd798541f53d723777c754412abf0e772b7cc284937c", "algorithm": "sha512", "filename": "gtk3.tar.xz", -"setup": "setup.sh", "unpack": true }, { diff --git a/browser/config/tooltool-manifests/linux64/clang.manifest b/browser/config/tooltool-manifests/linux64/clang.manifest index f804ab75c4cf..ba2d63fe998d 100644 --- a/browser/config/tooltool-manifests/linux64/clang.manifest +++ b/browser/config/tooltool-manifests/linux64/clang.manifest @@ -10,11 +10,10 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 4431740, +"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3", "algorithm": "sha512", "filename": "gtk3.tar.xz", -"setup": "setup.sh", "unpack": true } ] diff --git a/browser/config/tooltool-manifests/linux64/releng.manifest b/browser/config/tooltool-manifests/linux64/releng.manifest index 83180f42fe66..0001721cb8e5 100644 --- a/browser/config/tooltool-manifests/linux64/releng.manifest +++ b/browser/config/tooltool-manifests/linux64/releng.manifest @@ -7,11 +7,10 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 4431740, +"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3", "algorithm": "sha512", "filename": "gtk3.tar.xz", -"setup": "setup.sh", "unpack": true }, { diff --git a/browser/config/tooltool-manifests/linux64/tsan.manifest b/browser/config/tooltool-manifests/linux64/tsan.manifest index 1dfe9c0ba969..80609e02c1a0 100644 --- a/browser/config/tooltool-manifests/linux64/tsan.manifest +++ b/browser/config/tooltool-manifests/linux64/tsan.manifest @@ -10,11 +10,10 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 4431740, +"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3", "algorithm": "sha512", "filename": "gtk3.tar.xz", -"setup": "setup.sh", "unpack": true } ] diff --git a/build/unix/build-gtk3/build-gtk3.sh b/build/unix/build-gtk3/build-gtk3.sh index 9e8d5c0c8f31..24a5ae2933c3 100644 --- a/build/unix/build-gtk3/build-gtk3.sh +++ b/build/unix/build-gtk3/build-gtk3.sh @@ -57,7 +57,7 @@ build() { cd build/$name eval ../../$name-$version/configure --disable-static $* $configure_args make $make_flags - make install DESTDIR=$root_dir/gtk3 + make install-strip DESTDIR=$root_dir/gtk3 find $root_dir/gtk3 -name \*.la -delete cd ../.. } @@ -103,44 +103,5 @@ build gtk+ rm -rf $root_dir/gtk3/usr/local/share/gtk-doc rm -rf $root_dir/gtk3/usr/local/share/locale -# mock build environment doesn't have fonts in /usr/share/fonts, but -# has some in /usr/share/X11/fonts. Add this directory to the -# fontconfig configuration without changing the gtk3 tooltool package. -cat << EOF > $root_dir/gtk3/usr/local/etc/fonts/local.conf - - - - /usr/share/X11/fonts - -EOF - -cat < $root_dir/gtk3/setup.sh -#!/bin/sh - -cd \$(dirname \$0) - -# pango expects absolute paths in pango.modules, and TOOLTOOL_DIR may vary... -LD_LIBRARY_PATH=./usr/local/lib \ -PANGO_SYSCONFDIR=./usr/local/etc \ -PANGO_LIBDIR=./usr/local/lib \ -./usr/local/bin/pango-querymodules > ./usr/local/etc/pango/pango.modules - -# same with gdb-pixbuf and loaders.cache -LD_LIBRARY_PATH=./usr/local/lib \ -GDK_PIXBUF_MODULE_FILE=./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \ -GDK_PIXBUF_MODULEDIR=./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \ -./usr/local/bin/gdk-pixbuf-query-loaders > ./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache - -# The fontconfig version in the tooltool package has known uses of -# uninitialized memory when creating its cache, and while most users -# will already have an existing cache, running Firefox on automation -# will create it. Combined with valgrind, this generates irrelevant -# errors. -# So create the fontconfig cache beforehand. -./usr/local/bin/fc-cache -EOF - -chmod +x $root_dir/gtk3/setup.sh - cd $cwd tar -C $root_dir -Jcf gtk3.tar.xz gtk3 diff --git a/build/unix/mozconfig.gtk b/build/unix/mozconfig.gtk index 5849a9c4371c..8050b05e1cbe 100644 --- a/build/unix/mozconfig.gtk +++ b/build/unix/mozconfig.gtk @@ -11,6 +11,7 @@ if [ -d "$TOOLTOOL_DIR/gtk3" ]; then export PATH="$TOOLTOOL_DIR/gtk3/usr/local/bin:${PATH}" # Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages. LDFLAGS="-L$TOOLTOOL_DIR/gtk3/usr/local/lib ${LDFLAGS}" + mk_add_options "export LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib" ac_add_options --enable-default-toolkit=cairo-gtk3 # Set things up to use Gtk+3 from the tooltool package @@ -21,8 +22,37 @@ if [ -d "$TOOLTOOL_DIR/gtk3" ]; then mk_add_options "export GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders" mk_add_options "export LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib" - # Until a tooltool with bug 1188571 landed is available everywhere - $TOOLTOOL_DIR/gtk3/setup.sh + # pango expects absolute paths in pango.modules, and TOOLTOOL_DIR may vary... + LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib \ + PANGO_SYSCONFDIR=$TOOLTOOL_DIR/gtk3/usr/local/etc \ + PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib \ + $TOOLTOOL_DIR/gtk3/usr/local/bin/pango-querymodules > $TOOLTOOL_DIR/gtk3/usr/local/etc/pango/pango.modules + + # same with gdb-pixbuf and loaders.cache + LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib \ + GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \ + GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \ + $TOOLTOOL_DIR/gtk3/usr/local/bin/gdk-pixbuf-query-loaders > $TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache + + # The fontconfig version in the tooltool package has known uses of + # uninitialized memory when creating its cache, and while most users + # will already have an existing cache, running Firefox on automation + # will create it. Combined with valgrind, this generates irrelevant + # errors. + # So create the fontconfig cache beforehand. + $TOOLTOOL_DIR/gtk3/usr/local/bin/fc-cache + + # mock build environment doesn't have fonts in /usr/share/fonts, but + # has some in /usr/share/X11/fonts. Add this directory to the + # fontconfig configuration without changing the gtk3 tooltool package. + cat << EOF > $TOOLTOOL_DIR/gtk3/usr/local/etc/fonts/local.conf + + + + /usr/share/X11/fonts + +EOF + else ac_add_options --enable-default-toolkit=cairo-gtk2 fi diff --git a/js/src/devtools/rootAnalysis/build/gcc.manifest b/js/src/devtools/rootAnalysis/build/gcc.manifest index 83274df282e9..7dcd080f3092 100644 --- a/js/src/devtools/rootAnalysis/build/gcc.manifest +++ b/js/src/devtools/rootAnalysis/build/gcc.manifest @@ -10,11 +10,10 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 4431740, +"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3", "algorithm": "sha512", "filename": "gtk3.tar.xz", -"setup": "setup.sh", "unpack": true } ] From a66a62bc8b3a0bbd43bf0132db59f87e3e646d8c Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 3 Aug 2015 14:35:09 +0200 Subject: [PATCH 110/113] Backed out changeset 695b89efecfa (bug 1187664) --- build/valgrind/mach_commands.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/build/valgrind/mach_commands.py b/build/valgrind/mach_commands.py index 81920821fae3..a2af75d11a8d 100644 --- a/build/valgrind/mach_commands.py +++ b/build/valgrind/mach_commands.py @@ -111,13 +111,6 @@ class MachCommands(MachCommandBase): '--show-possibly-lost=no', '--track-origins=yes', '--trace-children=yes', - # The gstreamer plugin scanner can run as part of executing - # firefox, but is an external program. In some weird cases, - # valgrind finds errors while executing __libc_freeres when - # it runs, but those are not relevant, as it's related to - # executing third party code. So don't trace - # gst-plugin-scanner. - '--trace-children-skip=*/gst-plugin-scanner', '-v', # Enable verbosity to get the list of used suppressions ] From cd121161d60ba7c5d78774ff11ca23722352c9cd Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 3 Aug 2015 14:35:14 +0200 Subject: [PATCH 111/113] Backed out changeset 207c45dd3c2e (bug 1187664) --- build/unix/mozconfig.gtk | 8 -------- 1 file changed, 8 deletions(-) diff --git a/build/unix/mozconfig.gtk b/build/unix/mozconfig.gtk index 8050b05e1cbe..c49f0e4a4db8 100644 --- a/build/unix/mozconfig.gtk +++ b/build/unix/mozconfig.gtk @@ -34,14 +34,6 @@ if [ -d "$TOOLTOOL_DIR/gtk3" ]; then GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \ $TOOLTOOL_DIR/gtk3/usr/local/bin/gdk-pixbuf-query-loaders > $TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache - # The fontconfig version in the tooltool package has known uses of - # uninitialized memory when creating its cache, and while most users - # will already have an existing cache, running Firefox on automation - # will create it. Combined with valgrind, this generates irrelevant - # errors. - # So create the fontconfig cache beforehand. - $TOOLTOOL_DIR/gtk3/usr/local/bin/fc-cache - # mock build environment doesn't have fonts in /usr/share/fonts, but # has some in /usr/share/X11/fonts. Add this directory to the # fontconfig configuration without changing the gtk3 tooltool package. From ecaa52f350a2358e850150149a6eb6370003e591 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 3 Aug 2015 14:36:14 +0200 Subject: [PATCH 112/113] Backed out changeset d093c57c6835 (bug 1186748) for causing Bug 1190180 --- .../linux32/releng.manifest | 7 --- .../linux64/releng.manifest | 7 --- browser/config/mozconfigs/linux64/hazards | 1 - .../tooltool-manifests/linux64/clang.manifest | 7 --- build/unix/mozconfig.gtk | 48 +++++++++---------- .../devtools/rootAnalysis/build/gcc.manifest | 7 --- 6 files changed, 23 insertions(+), 54 deletions(-) diff --git a/b2g/config/tooltool-manifests/linux32/releng.manifest b/b2g/config/tooltool-manifests/linux32/releng.manifest index 79bbfa10a656..604e98b09cd7 100644 --- a/b2g/config/tooltool-manifests/linux32/releng.manifest +++ b/b2g/config/tooltool-manifests/linux32/releng.manifest @@ -7,13 +7,6 @@ "unpack": true }, { -"size": 4079256, -"digest": "bb5238558bcf6db2ca395513c8dccaa15dd61b3c375598eb6a685356b0c1a2d9840e3bf81bc00242b872fd798541f53d723777c754412abf0e772b7cc284937c", -"algorithm": "sha512", -"filename": "gtk3.tar.xz", -"unpack": true -}, -{ "size": 167175, "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", "algorithm": "sha512", diff --git a/b2g/config/tooltool-manifests/linux64/releng.manifest b/b2g/config/tooltool-manifests/linux64/releng.manifest index d336d9b35e8d..604e98b09cd7 100644 --- a/b2g/config/tooltool-manifests/linux64/releng.manifest +++ b/b2g/config/tooltool-manifests/linux64/releng.manifest @@ -7,13 +7,6 @@ "unpack": true }, { -"size": 4431740, -"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3", -"algorithm": "sha512", -"filename": "gtk3.tar.xz", -"unpack": true -}, -{ "size": 167175, "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", "algorithm": "sha512", diff --git a/browser/config/mozconfigs/linux64/hazards b/browser/config/mozconfigs/linux64/hazards index ebe46ccb82ef..6c5e19373284 100644 --- a/browser/config/mozconfigs/linux64/hazards +++ b/browser/config/mozconfigs/linux64/hazards @@ -26,7 +26,6 @@ CFLAGS="$CFLAGS -Wno-attributes" CPPFLAGS="$CPPFLAGS -Wno-attributes" CXXFLAGS="$CXXFLAGS -Wno-attributes" -TOOLTOOL_DIR="$(dirname $topsrcdir)" export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig . $topsrcdir/build/unix/mozconfig.gtk diff --git a/browser/config/tooltool-manifests/linux64/clang.manifest b/browser/config/tooltool-manifests/linux64/clang.manifest index ba2d63fe998d..216b4eae7037 100644 --- a/browser/config/tooltool-manifests/linux64/clang.manifest +++ b/browser/config/tooltool-manifests/linux64/clang.manifest @@ -8,12 +8,5 @@ "algorithm": "sha512", "filename": "clang.tar.bz2", "unpack": true -}, -{ -"size": 4431740, -"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3", -"algorithm": "sha512", -"filename": "gtk3.tar.xz", -"unpack": true } ] diff --git a/build/unix/mozconfig.gtk b/build/unix/mozconfig.gtk index c49f0e4a4db8..fd92daf3fa4d 100644 --- a/build/unix/mozconfig.gtk +++ b/build/unix/mozconfig.gtk @@ -1,43 +1,41 @@ -TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} - -# $TOOLTOOL_DIR/gtk3 comes from tooltool, when the tooltool manifest contains it. -if [ -d "$TOOLTOOL_DIR/gtk3" ]; then +# $topsrcdir/gtk3 comes from tooltool, when the tooltool manifest contains it. +if [ -d "$topsrcdir/gtk3" ]; then if [ -z "$PKG_CONFIG_LIBDIR" ]; then echo PKG_CONFIG_LIBDIR must be set >&2 exit 1 fi - export PKG_CONFIG_SYSROOT_DIR="$TOOLTOOL_DIR/gtk3" - export PKG_CONFIG_PATH="$TOOLTOOL_DIR/gtk3/usr/local/lib/pkgconfig" - export PATH="$TOOLTOOL_DIR/gtk3/usr/local/bin:${PATH}" + export PKG_CONFIG_SYSROOT_DIR="$topsrcdir/gtk3" + export PKG_CONFIG_PATH="$topsrcdir/gtk3/usr/local/lib/pkgconfig" + export PATH="$topsrcdir/gtk3/usr/local/bin:${PATH}" # Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages. - LDFLAGS="-L$TOOLTOOL_DIR/gtk3/usr/local/lib ${LDFLAGS}" - mk_add_options "export LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib" + LDFLAGS="-L$topsrcdir/gtk3/usr/local/lib ${LDFLAGS}" + mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib" ac_add_options --enable-default-toolkit=cairo-gtk3 # Set things up to use Gtk+3 from the tooltool package - mk_add_options "export FONTCONFIG_PATH=$TOOLTOOL_DIR/gtk3/usr/local/etc/fonts" - mk_add_options "export PANGO_SYSCONFDIR=$TOOLTOOL_DIR/gtk3/usr/local/etc" - mk_add_options "export PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib" - mk_add_options "export GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" - mk_add_options "export GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders" - mk_add_options "export LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib" + mk_add_options "export FONTCONFIG_PATH=$topsrcdir/gtk3/usr/local/etc/fonts" + mk_add_options "export PANGO_SYSCONFDIR=$topsrcdir/gtk3/usr/local/etc" + mk_add_options "export PANGO_LIBDIR=$topsrcdir/gtk3/usr/local/lib" + mk_add_options "export GDK_PIXBUF_MODULE_FILE=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" + mk_add_options "export GDK_PIXBUF_MODULEDIR=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders" + mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib" - # pango expects absolute paths in pango.modules, and TOOLTOOL_DIR may vary... - LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib \ - PANGO_SYSCONFDIR=$TOOLTOOL_DIR/gtk3/usr/local/etc \ - PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib \ - $TOOLTOOL_DIR/gtk3/usr/local/bin/pango-querymodules > $TOOLTOOL_DIR/gtk3/usr/local/etc/pango/pango.modules + # pango expects absolute paths in pango.modules, and topsrcdir may vary... + LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib \ + PANGO_SYSCONFDIR=$topsrcdir/gtk3/usr/local/etc \ + PANGO_LIBDIR=$topsrcdir/gtk3/usr/local/lib \ + $topsrcdir/gtk3/usr/local/bin/pango-querymodules > $topsrcdir/gtk3/usr/local/etc/pango/pango.modules # same with gdb-pixbuf and loaders.cache - LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib \ - GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \ - GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \ - $TOOLTOOL_DIR/gtk3/usr/local/bin/gdk-pixbuf-query-loaders > $TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache + LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib \ + GDK_PIXBUF_MODULE_FILE=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \ + GDK_PIXBUF_MODULEDIR=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \ + $topsrcdir/gtk3/usr/local/bin/gdk-pixbuf-query-loaders > $topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache # mock build environment doesn't have fonts in /usr/share/fonts, but # has some in /usr/share/X11/fonts. Add this directory to the # fontconfig configuration without changing the gtk3 tooltool package. - cat << EOF > $TOOLTOOL_DIR/gtk3/usr/local/etc/fonts/local.conf + cat << EOF > $topsrcdir/gtk3/usr/local/etc/fonts/local.conf diff --git a/js/src/devtools/rootAnalysis/build/gcc.manifest b/js/src/devtools/rootAnalysis/build/gcc.manifest index 7dcd080f3092..6686b39d0f0b 100644 --- a/js/src/devtools/rootAnalysis/build/gcc.manifest +++ b/js/src/devtools/rootAnalysis/build/gcc.manifest @@ -8,12 +8,5 @@ "algorithm": "sha512", "filename": "gcc.tar.xz", "unpack": true -}, -{ -"size": 4431740, -"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3", -"algorithm": "sha512", -"filename": "gtk3.tar.xz", -"unpack": true } ] From dd768f51f0b2578512adec93d1379f89bfccd0d6 Mon Sep 17 00:00:00 2001 From: Carsten Book Date: Mon, 3 Aug 2015 05:41:00 +0200 Subject: [PATCH 113/113] Bug 1190353 - "Touch Clobber to fix Bug 1190180". r=tomcat a=tomcat --- CLOBBER | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLOBBER b/CLOBBER index efbfda8b77dd..830244b75ce5 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 1186748 needed a CLOBBER again +Bug 1190180 - need clobber for backouts