From e36586f9011137e9719421965e1bf79f5101b0ba Mon Sep 17 00:00:00 2001 From: Fernando Jimenez Date: Fri, 22 May 2015 09:32:25 +0200 Subject: [PATCH 001/107] Bug 1161684 - Allow JAR channels to be intercepted by service workers. r=jdm --- dom/workers/ServiceWorkerManager.cpp | 97 +++++++++-------- modules/libjar/InterceptedJARChannel.cpp | 126 ++++++++++++++++++++++ modules/libjar/InterceptedJARChannel.h | 62 +++++++++++ modules/libjar/moz.build | 2 + modules/libjar/nsJARChannel.cpp | 127 +++++++++++++++++++++-- modules/libjar/nsJARChannel.h | 25 +++++ netwerk/base/moz.build | 1 + 7 files changed, 387 insertions(+), 53 deletions(-) create mode 100644 modules/libjar/InterceptedJARChannel.cpp create mode 100644 modules/libjar/InterceptedJARChannel.h diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 4e5ad8ce478..77a117b9387 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -14,6 +14,7 @@ #include "nsIHttpChannel.h" #include "nsIHttpChannelInternal.h" #include "nsIHttpHeaderVisitor.h" +#include "nsIJARChannel.h" #include "nsINetworkInterceptController.h" #include "nsIMutableArray.h" #include "nsIUploadChannel2.h" @@ -2594,60 +2595,70 @@ public: rv = uri->GetSpec(mSpec); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr httpChannel = do_QueryInterface(channel); - NS_ENSURE_TRUE(httpChannel, NS_ERROR_NOT_AVAILABLE); - - rv = httpChannel->GetRequestMethod(mMethod); - NS_ENSURE_SUCCESS(rv, rv); - uint32_t loadFlags; rv = channel->GetLoadFlags(&loadFlags); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr internalChannel = do_QueryInterface(httpChannel); - NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE); - - uint32_t mode; - internalChannel->GetCorsMode(&mode); - switch (mode) { - case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN: - mRequestMode = RequestMode::Same_origin; - break; - case nsIHttpChannelInternal::CORS_MODE_NO_CORS: - mRequestMode = RequestMode::No_cors; - break; - case nsIHttpChannelInternal::CORS_MODE_CORS: - case nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT: - mRequestMode = RequestMode::Cors; - break; - default: - MOZ_CRASH("Unexpected CORS mode"); - } - - if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { - mRequestCredentials = RequestCredentials::Omit; - } else { - bool includeCrossOrigin; - internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin); - if (includeCrossOrigin) { - mRequestCredentials = RequestCredentials::Include; - } - } - - rv = httpChannel->VisitRequestHeaders(this); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr loadInfo; rv = channel->GetLoadInfo(getter_AddRefs(loadInfo)); NS_ENSURE_SUCCESS(rv, rv); mContentPolicyType = loadInfo->GetContentPolicyType(); - nsCOMPtr uploadChannel = do_QueryInterface(httpChannel); - if (uploadChannel) { - MOZ_ASSERT(!mUploadStream); - rv = uploadChannel->CloneUploadStream(getter_AddRefs(mUploadStream)); + nsCOMPtr httpChannel = do_QueryInterface(channel); + if (httpChannel) { + rv = httpChannel->GetRequestMethod(mMethod); NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr internalChannel = do_QueryInterface(httpChannel); + NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE); + + uint32_t mode; + internalChannel->GetCorsMode(&mode); + switch (mode) { + case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN: + mRequestMode = RequestMode::Same_origin; + break; + case nsIHttpChannelInternal::CORS_MODE_NO_CORS: + mRequestMode = RequestMode::No_cors; + break; + case nsIHttpChannelInternal::CORS_MODE_CORS: + case nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT: + mRequestMode = RequestMode::Cors; + break; + default: + MOZ_CRASH("Unexpected CORS mode"); + } + + if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { + mRequestCredentials = RequestCredentials::Omit; + } else { + bool includeCrossOrigin; + internalChannel->GetCorsIncludeCredentials(&includeCrossOrigin); + if (includeCrossOrigin) { + mRequestCredentials = RequestCredentials::Include; + } + } + + rv = httpChannel->VisitRequestHeaders(this); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr uploadChannel = do_QueryInterface(httpChannel); + if (uploadChannel) { + MOZ_ASSERT(!mUploadStream); + rv = uploadChannel->CloneUploadStream(getter_AddRefs(mUploadStream)); + NS_ENSURE_SUCCESS(rv, rv); + } + } else { + nsCOMPtr jarChannel = do_QueryInterface(channel); + // If it is not an HTTP channel it must be a JAR one. + NS_ENSURE_TRUE(jarChannel, NS_ERROR_NOT_AVAILABLE); + + mMethod = "GET"; + + if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { + mRequestCredentials = RequestCredentials::Omit; + } } return NS_OK; diff --git a/modules/libjar/InterceptedJARChannel.cpp b/modules/libjar/InterceptedJARChannel.cpp new file mode 100644 index 00000000000..d706df4bffe --- /dev/null +++ b/modules/libjar/InterceptedJARChannel.cpp @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set expandtab ts=2 sw=2 sts=2 cin: */ +/* 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 "InterceptedJARChannel.h" +#include "nsIPipe.h" + +using namespace mozilla::net; + +NS_IMPL_ISUPPORTS(InterceptedJARChannel, nsIInterceptedChannel) + +InterceptedJARChannel::InterceptedJARChannel(nsJARChannel* aChannel, + nsINetworkInterceptController* aController, + bool aIsNavigation) +: mController(aController) +, mChannel(aChannel) +, mIsNavigation(aIsNavigation) +{ +} + +NS_IMETHODIMP +InterceptedJARChannel::GetResponseBody(nsIOutputStream** aStream) +{ + NS_IF_ADDREF(*aStream = mResponseBody); + return NS_OK; +} + +NS_IMETHODIMP +InterceptedJARChannel::GetIsNavigation(bool* aIsNavigation) +{ + *aIsNavigation = mIsNavigation; + return NS_OK; +} + +NS_IMETHODIMP +InterceptedJARChannel::GetChannel(nsIChannel** aChannel) +{ + NS_IF_ADDREF(*aChannel = mChannel); + return NS_OK; +} + +NS_IMETHODIMP +InterceptedJARChannel::ResetInterception() +{ + if (!mChannel) { + return NS_ERROR_NOT_AVAILABLE; + } + + mResponseBody = nullptr; + mSynthesizedInput = nullptr; + + mChannel->ResetInterception(); + mChannel = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +InterceptedJARChannel::SynthesizeStatus(uint16_t aStatus, + const nsACString& aReason) +{ + return NS_OK; +} + +NS_IMETHODIMP +InterceptedJARChannel::SynthesizeHeader(const nsACString& aName, + const nsACString& aValue) +{ + return NS_OK; +} + +NS_IMETHODIMP +InterceptedJARChannel::FinishSynthesizedResponse() +{ + if (NS_WARN_IF(!mChannel)) { + return NS_ERROR_NOT_AVAILABLE; + } + + mChannel->OverrideWithSynthesizedResponse(mSynthesizedInput); + + mResponseBody = nullptr; + mChannel = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +InterceptedJARChannel::Cancel() +{ + if (!mChannel) { + return NS_ERROR_FAILURE; + } + + nsresult rv = mChannel->Cancel(NS_BINDING_ABORTED); + NS_ENSURE_SUCCESS(rv, rv); + mResponseBody = nullptr; + mChannel = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +InterceptedJARChannel::SetSecurityInfo(nsISupports* aSecurityInfo) +{ + if (!mChannel) { + return NS_ERROR_FAILURE; + } + + return mChannel->OverrideSecurityInfo(aSecurityInfo); +} + +void +InterceptedJARChannel::NotifyController() +{ + nsresult rv = NS_NewPipe(getter_AddRefs(mSynthesizedInput), + getter_AddRefs(mResponseBody), + 0, UINT32_MAX, true, true); + NS_ENSURE_SUCCESS_VOID(rv); + + rv = mController->ChannelIntercepted(this); + if (NS_WARN_IF(NS_FAILED(rv))) { + rv = ResetInterception(); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), + "Failed to resume intercepted network request"); + } + mController = nullptr; +} diff --git a/modules/libjar/InterceptedJARChannel.h b/modules/libjar/InterceptedJARChannel.h new file mode 100644 index 00000000000..1f5fc130a13 --- /dev/null +++ b/modules/libjar/InterceptedJARChannel.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set expandtab ts=2 sw=2 sts=2 cin: */ +/* 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 InterceptedJARChannel_h +#define InterceptedJARChannel_h + +#include "nsJAR.h" +#include "nsJARChannel.h" +#include "nsIInputStream.h" +#include "nsIInputStreamPump.h" +#include "nsINetworkInterceptController.h" +#include "nsIOutputStream.h" +#include "nsRefPtr.h" + +#include "mozilla/Maybe.h" + +class nsIStreamListener; +class nsJARChannel; + +namespace mozilla { +namespace net { + +// An object representing a channel that has been intercepted. This avoids +// complicating the actual channel implementation with the details of +// synthesizing responses. +class InterceptedJARChannel : public nsIInterceptedChannel +{ + // The interception controller to notify about the successful channel + // interception. + nsCOMPtr mController; + + // The actual channel being intercepted. + nsRefPtr mChannel; + + // Reader-side of the synthesized response body. + nsCOMPtr mSynthesizedInput; + + // The stream to write the body of the synthesized response. + nsCOMPtr mResponseBody; + + // Wether this intercepted channel was performing a navigation. + bool mIsNavigation; + + virtual ~InterceptedJARChannel() {}; +public: + InterceptedJARChannel(nsJARChannel* aChannel, + nsINetworkInterceptController* aController, + bool aIsNavigation); + + NS_DECL_ISUPPORTS + NS_DECL_NSIINTERCEPTEDCHANNEL + + void NotifyController(); +}; + +} // namespace net +} // namespace mozilla + +#endif // InterceptedJARChannel_h diff --git a/modules/libjar/moz.build b/modules/libjar/moz.build index d02c680829c..858bbe30a7a 100644 --- a/modules/libjar/moz.build +++ b/modules/libjar/moz.build @@ -23,12 +23,14 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'jar' EXPORTS += [ + 'InterceptedJARChannel.h', 'nsJARURI.h', 'nsZipArchive.h', 'zipstruct.h', ] UNIFIED_SOURCES += [ + 'InterceptedJARChannel.cpp', 'nsJARProtocolHandler.cpp', 'nsJARURI.cpp', ] diff --git a/modules/libjar/nsJARChannel.cpp b/modules/libjar/nsJARChannel.cpp index 93cc1431535..4160ea7182c 100644 --- a/modules/libjar/nsJARChannel.cpp +++ b/modules/libjar/nsJARChannel.cpp @@ -24,6 +24,9 @@ #include "mozilla/net/RemoteOpenFileChild.h" #include "nsITabChild.h" #include "private/pprio.h" +#include "nsINetworkInterceptController.h" +#include "InterceptedJARChannel.h" +#include "nsInputStreamPump.h" using namespace mozilla; using namespace mozilla::net; @@ -199,6 +202,7 @@ nsJARChannel::nsJARChannel() , mIsUnsafe(true) , mOpeningRemote(false) , mEnsureChildFd(false) + , mSynthesizedStreamLength(0) { if (!gJarProtocolLog) gJarProtocolLog = PR_NewLogModule("nsJarProtocol"); @@ -234,7 +238,7 @@ NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel, nsIThreadRetargetableStreamListener, nsIJARChannel) -nsresult +nsresult nsJARChannel::Init(nsIURI *uri) { nsresult rv; @@ -530,6 +534,8 @@ nsJARChannel::GetStatus(nsresult *status) { if (mPump && NS_SUCCEEDED(mStatus)) mPump->GetStatus(status); + else if (mSynthesizedResponsePump && NS_SUCCEEDED(mStatus)) + mSynthesizedResponsePump->GetStatus(status); else *status = mStatus; return NS_OK; @@ -541,6 +547,8 @@ nsJARChannel::Cancel(nsresult status) mStatus = status; if (mPump) return mPump->Cancel(status); + if (mSynthesizedResponsePump) + return mSynthesizedResponsePump->Cancel(status); NS_ASSERTION(!mIsPending, "need to implement cancel when downloading"); return NS_OK; @@ -551,6 +559,8 @@ nsJARChannel::Suspend() { if (mPump) return mPump->Suspend(); + if (mSynthesizedResponsePump) + return mSynthesizedResponsePump->Suspend(); NS_ASSERTION(!mIsPending, "need to implement suspend when downloading"); return NS_OK; @@ -561,6 +571,8 @@ nsJARChannel::Resume() { if (mPump) return mPump->Resume(); + if (mSynthesizedResponsePump) + return mSynthesizedResponsePump->Resume(); NS_ASSERTION(!mIsPending, "need to implement resume when downloading"); return NS_OK; @@ -670,7 +682,20 @@ nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) return NS_OK; } -NS_IMETHODIMP +nsresult +nsJARChannel::OverrideSecurityInfo(nsISupports* aSecurityInfo) +{ + MOZ_RELEASE_ASSERT(!mSecurityInfo, + "This can only be called when we don't have a security info object already"); + MOZ_RELEASE_ASSERT(aSecurityInfo, + "This can only be called with a valid security info object"); + MOZ_RELEASE_ASSERT(ShouldIntercept(), + "This can only be called on channels that can be intercepted"); + mSecurityInfo = aSecurityInfo; + return NS_OK; +} + +NS_IMETHODIMP nsJARChannel::GetSecurityInfo(nsISupports **aSecurityInfo) { NS_PRECONDITION(aSecurityInfo, "Null out param"); @@ -836,6 +861,66 @@ nsJARChannel::Open(nsIInputStream **stream) return NS_OK; } +bool +nsJARChannel::ShouldIntercept() +{ + LOG(("nsJARChannel::ShouldIntercept [this=%x]\n", this)); + // We only intercept app:// requests + if (!mAppURI) { + return false; + } + + nsCOMPtr controller; + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, + NS_GET_IID(nsINetworkInterceptController), + getter_AddRefs(controller)); + bool shouldIntercept = false; + if (controller) { + bool isNavigation = mLoadFlags & LOAD_DOCUMENT_URI; + nsresult rv = controller->ShouldPrepareForIntercept(mAppURI, + isNavigation, + &shouldIntercept); + NS_ENSURE_SUCCESS(rv, false); + } + + return shouldIntercept; +} + +void nsJARChannel::ResetInterception() +{ + LOG(("nsJARChannel::ResetInterception [this=%x]\n", this)); + + // Continue with the origin request. + nsresult rv = ContinueAsyncOpen(); + NS_ENSURE_SUCCESS_VOID(rv); +} + +void +nsJARChannel::OverrideWithSynthesizedResponse(nsIInputStream* aSynthesizedInput) +{ + // In our current implementation, the FetchEvent handler will copy the + // response stream completely into the pipe backing the input stream so we + // can treat the available as the length of the stream. + uint64_t available; + nsresult rv = aSynthesizedInput->Available(&available); + if (NS_WARN_IF(NS_FAILED(rv))) { + mSynthesizedStreamLength = -1; + } else { + mSynthesizedStreamLength = int64_t(available); + } + + rv = nsInputStreamPump::Create(getter_AddRefs(mSynthesizedResponsePump), + aSynthesizedInput, + int64_t(-1), int64_t(-1), 0, 0, true); + if (NS_WARN_IF(NS_FAILED(rv))) { + aSynthesizedInput->Close(); + return; + } + + rv = mSynthesizedResponsePump->AsyncRead(this, nullptr); + NS_ENSURE_SUCCESS_VOID(rv); +} + NS_IMETHODIMP nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) { @@ -851,18 +936,40 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) // Initialize mProgressSink NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink); - nsresult rv = LookupFile(true); - if (NS_FAILED(rv)) - return rv; - - // These variables must only be set if we're going to trigger an - // OnStartRequest, either from AsyncRead or OnDownloadComplete. - // - // That means: Do not add early return statements beyond this point! mListener = listener; mListenerContext = ctx; mIsPending = true; + // Check if this channel should intercept the network request and prepare + // for a possible synthesized response instead. + if (ShouldIntercept()) { + nsCOMPtr controller; + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, + NS_GET_IID(nsINetworkInterceptController), + getter_AddRefs(controller)); + + bool isNavigation = mLoadFlags & LOAD_DOCUMENT_URI; + nsRefPtr intercepted = + new InterceptedJARChannel(this, controller, isNavigation); + intercepted->NotifyController(); + return NS_OK; + } + + return ContinueAsyncOpen(); +} + +nsresult +nsJARChannel::ContinueAsyncOpen() +{ + LOG(("nsJARChannel::ContinueAsyncOpen [this=%x]\n", this)); + nsresult rv = LookupFile(true); + if (NS_FAILED(rv)) { + mIsPending = false; + mListenerContext = nullptr; + mListener = nullptr; + return rv; + } + nsCOMPtr channel; if (!mJarFile) { diff --git a/modules/libjar/nsJARChannel.h b/modules/libjar/nsJARChannel.h index 108e0339311..d7f5cbefd1e 100644 --- a/modules/libjar/nsJARChannel.h +++ b/modules/libjar/nsJARChannel.h @@ -10,6 +10,7 @@ #include "nsIJARChannel.h" #include "nsIJARURI.h" #include "nsIInputStreamPump.h" +#include "InterceptedJARChannel.h" #include "nsIInterfaceRequestor.h" #include "nsIProgressEventSink.h" #include "nsIStreamListener.h" @@ -27,6 +28,13 @@ #include "mozilla/Logging.h" class nsJARInputThunk; +class nsInputStreamPump; + +namespace mozilla { +namespace net { + class InterceptedJARChannel; +} // namespace net +} // namespace mozilla //----------------------------------------------------------------------------- @@ -69,6 +77,18 @@ private: mozilla::net::MemoryDownloader::Data aData) override; + // Returns true if this channel should intercept the network request and + // prepare for a possible synthesized response instead. + bool ShouldIntercept(); + nsresult ContinueAsyncOpen(); + // Discard the prior interception and continue with the original network + // request. + void ResetInterception(); + // Override this channel's pending response with a synthesized one. The + // content will be asynchronously read from the pump. + void OverrideWithSynthesizedResponse(nsIInputStream* aSynthesizedInput); + nsresult OverrideSecurityInfo(nsISupports* aSecurityInfo); + nsCString mSpec; bool mOpened; @@ -107,6 +127,11 @@ private: nsCOMPtr mJarBaseURI; nsCString mJarEntry; nsCString mInnerJarEntry; + + nsRefPtr mSynthesizedResponsePump; + int64_t mSynthesizedStreamLength; + + friend class mozilla::net::InterceptedJARChannel; }; #endif // nsJARChannel_h__ diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index 0960556dba8..542af4b43bf 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -144,6 +144,7 @@ EXPORTS += [ 'nsASocketHandler.h', 'nsAsyncRedirectVerifyHelper.h', 'nsFileStreams.h', + 'nsInputStreamPump.h', 'nsMIMEInputStream.h', 'nsNetUtil.h', 'nsReadLine.h', From 72fb1c17c3c9dbf9fb1037e89c680d9d977a2cbe Mon Sep 17 00:00:00 2001 From: Fernando Jimenez Date: Fri, 22 May 2015 09:32:25 +0200 Subject: [PATCH 002/107] Bug 1161684 - Allow JAR channels to be intercepted by service workers. Tests. r=jdm --- .../serviceworkers/app-protocol/README.txt | 2 + .../app-protocol/application.zip | Bin 0 -> 2331 bytes .../app-protocol/controlled.html | 18 +++ .../test/serviceworkers/app-protocol/foo.txt | 1 + .../serviceworkers/app-protocol/index.html | 31 ++++ .../app-protocol/manifest.webapp | 5 + .../test/serviceworkers/app-protocol/sw.js | 8 + .../test/serviceworkers/app-protocol/test.js | 29 ++++ .../serviceworkers/app-protocol/update.webapp | 6 + .../app-protocol/update.webapp^headers^ | 1 + dom/workers/test/serviceworkers/mochitest.ini | 2 + .../serviceworkers/test_app_protocol.html | 146 ++++++++++++++++++ 12 files changed, 249 insertions(+) create mode 100644 dom/workers/test/serviceworkers/app-protocol/README.txt create mode 100644 dom/workers/test/serviceworkers/app-protocol/application.zip create mode 100644 dom/workers/test/serviceworkers/app-protocol/controlled.html create mode 100644 dom/workers/test/serviceworkers/app-protocol/foo.txt create mode 100644 dom/workers/test/serviceworkers/app-protocol/index.html create mode 100644 dom/workers/test/serviceworkers/app-protocol/manifest.webapp create mode 100644 dom/workers/test/serviceworkers/app-protocol/sw.js create mode 100644 dom/workers/test/serviceworkers/app-protocol/test.js create mode 100644 dom/workers/test/serviceworkers/app-protocol/update.webapp create mode 100644 dom/workers/test/serviceworkers/app-protocol/update.webapp^headers^ create mode 100644 dom/workers/test/serviceworkers/test_app_protocol.html diff --git a/dom/workers/test/serviceworkers/app-protocol/README.txt b/dom/workers/test/serviceworkers/app-protocol/README.txt new file mode 100644 index 00000000000..fcf383b3003 --- /dev/null +++ b/dom/workers/test/serviceworkers/app-protocol/README.txt @@ -0,0 +1,2 @@ +application.zip contains foo.txt, index.html, sw.js and manifest.webapp. +Any change to one of these three files should be added to application.zip as well. diff --git a/dom/workers/test/serviceworkers/app-protocol/application.zip b/dom/workers/test/serviceworkers/app-protocol/application.zip new file mode 100644 index 0000000000000000000000000000000000000000..0e914a94cf6639d4f26e47a5a26e21c0422eb82e GIT binary patch literal 2331 zcmaJ?c~nzp7EcH<0TOnB0hKrn${G*`WNl$0yFeNZn;Mo7*}`IoEV2s3DmWRILRnKW zpdge}Fl1y?1S|=J9T@~smJykPG8L?F2r@6Im{3pOcfRw^`{Uj7yZ5(ToGV0983YGT zD3Rj;5_zOS@*upu-3d2)3@x5U@J4_JPWcg31VqB`U}ey`frrRWBZR|j;_Y=r_Hse;-_rtEJ)xlk2W#90GH)%H2v& zj2zY&bnfeard|FD?APG6$V!U zI|j!9Aj^P@+l7wJ6^AYdKqo~-(x_1s3ONW9LW`gX@k@C65vEW3?cgWmx`lTcA+D@! z?4RWEv<@4!*p=Bn>A6T&{)JJ`GJB1m;<`I%Z>`)7?rq7>Tw8ps`XDhSaWW;6=>Nal z4Sn&(pd{O2$D{X%8w#xkmU5lG6B_B$w6qrD@kn&rcm-dw)u!}*bY9X?b!kHQ$v92kRdolrLma_H!7}^?X#+m&DNLYM?V{*o$#7z;Flk+@U*Fmu=EsD*!afiBXlC zBTki2WucKlD>hVscEj zF(2=;y}4}OKhjW6aUJyV@W_f{K4+@^S*Qp}KQD_*%hw#uv zL4V2gD-eQLG1n#{?TA$ z9;YHm@mT|neWxlKjykJ4iAqM9(mOottj?Kdl-M3m= zGmXA{fSsC5_-EyKZa{L={FSgN>7}CCA^bb0|G=eV%)v=rv$pW*sh0!Tnk=EZH8tf6!lG#83MCZh)@$%>1B6f0X@8s_%lrijTR9Q8G4=^L&L<8 z0|TO?_Xb7n0v?C!b>+Je8u2))zBXb+FNSIrZzP~La>nKq>D}oPbhhBH^vV$$6pJ;%n#d?=)%#+0B=*?RMbR6OSAdwr zO7}<%3V@7>!-U0rf_Cepx=a~%GU>MR^p*EnaT=;&OJ^6gTA+546$^r*AJ< zshJ3!DSg*o%)MA6LmJSGr8ay!alxr3D1pGX>oxJ>QV~-AeFcjhD}}5@v$09buIZ*S z{go!#-=!0-wEHU0oRYL||8MT)x#Q84w(OHVV#lvwbpJ*r3+HR~ShcFx;zKJQRLiQ#pgdB3KRkn5TBH#I4Ey8m!m1G$iq?Z_hAw2h;f!xq0W^w!-$VNkD81&BP-cdm}RlJ>A)zxmy$ ziT`}GGe3L#D$l}sj7_&c?2&}HdZ#O;HIc+j?9X?Y&*SJ3e}CYU$zbO6hD~u`v|D$z zs0}M;^rfmCeOc$~LUJu@?U1yA?~m!c?e^3mR5#nho?t2ShW4B;)1Phkc@@{^KIWtMRUqCU82v8OhrO+Q|4G%z`s{4} zhh)qsDF`$IToAwG0$md-tx{9Ph2!NKI$F}ma@GZJI;k%m?=lZ#ZaA}Dqubfzo zR-ncoRv3JDg^3mt2DNC|!o&*61JHM4`^!j#nJnse@ovro1w<1X4*HbPdqIfWQM?

Q@ literal 0 HcmV?d00001 diff --git a/dom/workers/test/serviceworkers/app-protocol/controlled.html b/dom/workers/test/serviceworkers/app-protocol/controlled.html new file mode 100644 index 00000000000..cf11816aeae --- /dev/null +++ b/dom/workers/test/serviceworkers/app-protocol/controlled.html @@ -0,0 +1,18 @@ + + + + Test app for bug 1161684 + + + + + + diff --git a/dom/workers/test/serviceworkers/app-protocol/foo.txt b/dom/workers/test/serviceworkers/app-protocol/foo.txt new file mode 100644 index 00000000000..8d1556472f3 --- /dev/null +++ b/dom/workers/test/serviceworkers/app-protocol/foo.txt @@ -0,0 +1 @@ +networkresponse \ No newline at end of file diff --git a/dom/workers/test/serviceworkers/app-protocol/index.html b/dom/workers/test/serviceworkers/app-protocol/index.html new file mode 100644 index 00000000000..38726531c5b --- /dev/null +++ b/dom/workers/test/serviceworkers/app-protocol/index.html @@ -0,0 +1,31 @@ + + + + Test app for bug 1161684 + + + + + + diff --git a/dom/workers/test/serviceworkers/app-protocol/manifest.webapp b/dom/workers/test/serviceworkers/app-protocol/manifest.webapp new file mode 100644 index 00000000000..e47d156f44a --- /dev/null +++ b/dom/workers/test/serviceworkers/app-protocol/manifest.webapp @@ -0,0 +1,5 @@ +{ + "name": "App", + "launch_path": "/index.html", + "description": "Test app for bug 1161684" +} diff --git a/dom/workers/test/serviceworkers/app-protocol/sw.js b/dom/workers/test/serviceworkers/app-protocol/sw.js new file mode 100644 index 00000000000..38975595b07 --- /dev/null +++ b/dom/workers/test/serviceworkers/app-protocol/sw.js @@ -0,0 +1,8 @@ +self.addEventListener('fetch', (event) => { + if (event.request.url.indexOf('foo.txt') >= 0) { + var body = 'swresponse'; + event.respondWith(new Response(body, { + headers: {'Content-Type': 'text/plain'} + })); + } +}); diff --git a/dom/workers/test/serviceworkers/app-protocol/test.js b/dom/workers/test/serviceworkers/app-protocol/test.js new file mode 100644 index 00000000000..e08d6156040 --- /dev/null +++ b/dom/workers/test/serviceworkers/app-protocol/test.js @@ -0,0 +1,29 @@ +function ok(aCondition, aMessage) { + if (aCondition) { + alert('OK: ' + aMessage); + } else { + alert('KO: ' + aMessage); + } +} + +function ready() { + alert('READY'); +} + +function done() { + alert('DONE'); +} + +function testFetchAppResource(aExpectedResponse) { + return fetch('foo.txt').then(res => { + ok(true, 'fetch should resolve'); + if (res.type == 'error') { + ok(false, 'fetch failed'); + } + ok(res.status == 200, 'status should be 200'); + ok(res.statusText == 'OK', 'statusText should be OK'); + return res.text().then(body => { + ok(body == aExpectedResponse, 'body should match'); + }); + }); +} diff --git a/dom/workers/test/serviceworkers/app-protocol/update.webapp b/dom/workers/test/serviceworkers/app-protocol/update.webapp new file mode 100644 index 00000000000..e4649d81c85 --- /dev/null +++ b/dom/workers/test/serviceworkers/app-protocol/update.webapp @@ -0,0 +1,6 @@ +{ + "name": "App", + "launch_path": "/index.html", + "description": "Test app for bug 1161684", + "package_path": "application.zip" +} diff --git a/dom/workers/test/serviceworkers/app-protocol/update.webapp^headers^ b/dom/workers/test/serviceworkers/app-protocol/update.webapp^headers^ new file mode 100644 index 00000000000..90818c6398e --- /dev/null +++ b/dom/workers/test/serviceworkers/app-protocol/update.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/manifest+json diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini index 283f294cef3..10a81a3de0b 100644 --- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -97,6 +97,7 @@ support-files = claim_worker_2.js claim_clients/client.html claim_fetch_worker.js + app-protocol/* [test_unregister.html] [test_installation_simple.html] @@ -134,4 +135,5 @@ support-files = [test_sanitize.html] [test_sanitize_domain.html] [test_service_worker_allowed.html] +[test_app_protocol.html] [test_claim_fetch.html] diff --git a/dom/workers/test/serviceworkers/test_app_protocol.html b/dom/workers/test/serviceworkers/test_app_protocol.html new file mode 100644 index 00000000000..daa3af05e8c --- /dev/null +++ b/dom/workers/test/serviceworkers/test_app_protocol.html @@ -0,0 +1,146 @@ + + + + + Bug 1161684 - Allow JAR channels to be intercepted by service workers + + + + +

+ +

+
+
+ + From cfea3153937641a167bc6f1dbe23285a60bb2ec0 Mon Sep 17 00:00:00 2001 From: Fernando Jimenez Date: Sun, 24 May 2015 21:28:15 +0200 Subject: [PATCH 003/107] Bug 1162281 - Invalid system message handler in an App Manifest can break the entire system. r=fabrice --- dom/apps/Webapps.jsm | 46 ++-- dom/messages/SystemMessageInternal.js | 71 +++--- .../interfaces/nsISystemMessagesInternal.idl | 6 +- dom/messages/test/chrome.ini | 12 +- dom/messages/test/invalid_manifest.webapp | 8 + .../test/invalid_manifest.webapp^headers^ | 1 + dom/messages/test/manifest.webapp | 9 + dom/messages/test/manifest.webapp^headers^ | 1 + .../test/test_sysmsg_registration.html | 235 ++++++++++++++++++ 9 files changed, 337 insertions(+), 52 deletions(-) create mode 100644 dom/messages/test/invalid_manifest.webapp create mode 100644 dom/messages/test/invalid_manifest.webapp^headers^ create mode 100644 dom/messages/test/manifest.webapp create mode 100644 dom/messages/test/manifest.webapp^headers^ create mode 100644 dom/messages/test/test_sysmsg_registration.html diff --git a/dom/apps/Webapps.jsm b/dom/apps/Webapps.jsm index d94fc9b6dab..05d3a9e2fe5 100644 --- a/dom/apps/Webapps.jsm +++ b/dom/apps/Webapps.jsm @@ -860,6 +860,10 @@ this.DOMApplicationRegistry = { if (!root.messages || !Array.isArray(root.messages) || root.messages.length == 0) { + dump("Could not register invalid system message entry\n"); + try { + dump(JSON.stringify(root.messages) + "\n"); + } catch(e) {} return; } @@ -869,28 +873,32 @@ this.DOMApplicationRegistry = { root.messages.forEach(function registerPages(aMessage) { let handlerPageURI = launchPathURI; let messageName; - if (typeof(aMessage) === "object" && Object.keys(aMessage).length === 1) { - messageName = Object.keys(aMessage)[0]; - let handlerPath = aMessage[messageName]; - // Resolve the handler path from origin. If |handler_path| is absent, - // simply skip. - let fullHandlerPath; + if (typeof(aMessage) !== "object" || Object.keys(aMessage).length !== 1) { + dump("Could not register invalid system message entry\n"); try { - if (handlerPath && handlerPath.trim()) { - fullHandlerPath = manifest.resolveURL(handlerPath); - } else { - throw new Error("Empty or blank handler path."); - } - } catch(e) { - debug("system message handler path (" + handlerPath + ") is " + - "invalid, skipping. Error is: " + e); - return; - } - handlerPageURI = Services.io.newURI(fullHandlerPath, null, null); - } else { - messageName = aMessage; + dump(JSON.stringify(aMessage) + "\n"); + } catch(e) {} + return; } + messageName = Object.keys(aMessage)[0]; + let handlerPath = aMessage[messageName]; + // Resolve the handler path from origin. If |handler_path| is absent, + // simply skip. + let fullHandlerPath; + try { + if (handlerPath && handlerPath.trim()) { + fullHandlerPath = manifest.resolveURL(handlerPath); + } else { + throw new Error("Empty or blank handler path."); + } + } catch(e) { + debug("system message handler path (" + handlerPath + ") is " + + "invalid, skipping. Error is: " + e); + return; + } + handlerPageURI = Services.io.newURI(fullHandlerPath, null, null); + if (SystemMessagePermissionsChecker .isSystemMessagePermittedToRegister(messageName, aApp.manifestURL, diff --git a/dom/messages/SystemMessageInternal.js b/dom/messages/SystemMessageInternal.js index 3e030070f73..072bcb17dbf 100644 --- a/dom/messages/SystemMessageInternal.js +++ b/dom/messages/SystemMessageInternal.js @@ -273,7 +273,7 @@ SystemMessageInternal.prototype = { type: aType, msg: aMessage, extra: aExtra }); - return; + return Promise.resolve(); } // Give this message an ID so that we can identify the message and @@ -285,37 +285,50 @@ SystemMessageInternal.prototype = { let shouldDispatchFunc = this._getMessageConfigurator(aType).shouldDispatch; - // Find pages that registered an handler for this type. - this._pages.forEach(function(aPage) { - if (aPage.type !== aType) { - return; - } + if (!this._pages.length) { + return Promise.resolve(); + } - let doDispatch = () => { - let result = this._sendMessageCommon(aType, - aMessage, - messageID, - aPage.pageURL, - aPage.manifestURL, - aExtra); - debug("Returned status of sending message: " + result); - }; - - if ('function' !== typeof shouldDispatchFunc) { - // If the configurator has no 'shouldDispatch' defined, - // always dispatch this message. - doDispatch(); - return; - } - - shouldDispatchFunc(aPage.manifestURL, aPage.pageURL, aType, aMessage, aExtra) - .then(aShouldDispatch => { - if (aShouldDispatch) { - doDispatch(); + // Find pages that registered a handler for this type. + let promises = []; + for (let i = 0; i < this._pages.length; i++) { + let promise = ((page) => { + return new Promise((resolve, reject) => { + if (page.type !== aType) { + resolve(); + return; } - }); - }, this); + let doDispatch = () => { + let result = this._sendMessageCommon(aType, + aMessage, + messageID, + page.pageURL, + page.manifestURL, + aExtra); + debug("Returned status of sending message: " + result); + resolve(); + }; + + if ('function' !== typeof shouldDispatchFunc) { + // If the configurator has no 'shouldDispatch' defined, + // always dispatch this message. + doDispatch(); + return; + } + + shouldDispatchFunc(page.manifestURL, page.pageURL, aType, aMessage, aExtra) + .then(aShouldDispatch => { + if (aShouldDispatch) { + doDispatch(); + } + }); + }); + })(this._pages[i]); + promises.push(promise); + } + + return Promise.all(promises); }, registerPage: function(aType, aPageURI, aManifestURI) { diff --git a/dom/messages/interfaces/nsISystemMessagesInternal.idl b/dom/messages/interfaces/nsISystemMessagesInternal.idl index ad6f95121d1..92e562cebe9 100644 --- a/dom/messages/interfaces/nsISystemMessagesInternal.idl +++ b/dom/messages/interfaces/nsISystemMessagesInternal.idl @@ -10,7 +10,7 @@ interface nsIMessageSender; // Implemented by the contract id @mozilla.org/system-message-internal;1 -[scriptable, uuid(54c8e274-decb-4258-9a24-4ebfcbf3d00a)] +[scriptable, uuid(59b6beda-f911-4d47-a296-8c81e6abcfb9)] interface nsISystemMessagesInternal : nsISupports { /* @@ -36,8 +36,10 @@ interface nsISystemMessagesInternal : nsISupports * @param message The message payload. * @param extra Extra opaque information that will be passed around in the observer * notification to open the page. + * returns a Promise */ - void broadcastMessage(in DOMString type, in jsval message, [optional] in jsval extra); + nsISupports broadcastMessage(in DOMString type, in jsval message, + [optional] in jsval extra); /* * Registration of a page that wants to be notified of a message type. diff --git a/dom/messages/test/chrome.ini b/dom/messages/test/chrome.ini index 77ad7c46861..cf8cbd4ad9b 100644 --- a/dom/messages/test/chrome.ini +++ b/dom/messages/test/chrome.ini @@ -1,3 +1,11 @@ -[test_hasPendingMessage.html] +[DEFAULT] skip-if = (buildapp != "browser") || e10s -support-files = file_hasPendingMessage.html +support-files = + file_hasPendingMessage.html + invalid_manifest.webapp + invalid_manifest.webapp^headers^ + manifest.webapp + manifest.webapp^headers^ + +[test_hasPendingMessage.html] +[test_sysmsg_registration.html] diff --git a/dom/messages/test/invalid_manifest.webapp b/dom/messages/test/invalid_manifest.webapp new file mode 100644 index 00000000000..ead4f9f64b0 --- /dev/null +++ b/dom/messages/test/invalid_manifest.webapp @@ -0,0 +1,8 @@ +{ + "name": "Random app", + "launch_path": "/index.html", + "messages": [{ + "dummy-system-message": "/index.html", + "dummy-system-message2": "/index.html" + }] +} diff --git a/dom/messages/test/invalid_manifest.webapp^headers^ b/dom/messages/test/invalid_manifest.webapp^headers^ new file mode 100644 index 00000000000..90818c6398e --- /dev/null +++ b/dom/messages/test/invalid_manifest.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/manifest+json diff --git a/dom/messages/test/manifest.webapp b/dom/messages/test/manifest.webapp new file mode 100644 index 00000000000..45b15930891 --- /dev/null +++ b/dom/messages/test/manifest.webapp @@ -0,0 +1,9 @@ +{ + "name": "Random app", + "launch_path": "/index.html", + "messages": [{ + "dummy-system-message": "/index.html" + }, { + "dummy-system-message2": "/index.html" + }] +} diff --git a/dom/messages/test/manifest.webapp^headers^ b/dom/messages/test/manifest.webapp^headers^ new file mode 100644 index 00000000000..90818c6398e --- /dev/null +++ b/dom/messages/test/manifest.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/manifest+json diff --git a/dom/messages/test/test_sysmsg_registration.html b/dom/messages/test/test_sysmsg_registration.html new file mode 100644 index 00000000000..adacbfa8ce4 --- /dev/null +++ b/dom/messages/test/test_sysmsg_registration.html @@ -0,0 +1,235 @@ + + + + System messages registration tests + + + + + +Mozilla Bug {1162281} +

+ +
+
+
+ + From 5171b1d412729907e7d620eed668cbcbb7eaa458 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Sun, 24 May 2015 13:59:43 -0700 Subject: [PATCH 004/107] Back out ffb94ee54752 (bug 1162281) for build and test bustage CLOSED TREE --- dom/apps/Webapps.jsm | 44 ++-- dom/messages/SystemMessageInternal.js | 63 ++--- .../interfaces/nsISystemMessagesInternal.idl | 6 +- dom/messages/test/chrome.ini | 12 +- dom/messages/test/invalid_manifest.webapp | 8 - .../test/invalid_manifest.webapp^headers^ | 1 - dom/messages/test/manifest.webapp | 9 - dom/messages/test/manifest.webapp^headers^ | 1 - .../test/test_sysmsg_registration.html | 235 ------------------ 9 files changed, 47 insertions(+), 332 deletions(-) delete mode 100644 dom/messages/test/invalid_manifest.webapp delete mode 100644 dom/messages/test/invalid_manifest.webapp^headers^ delete mode 100644 dom/messages/test/manifest.webapp delete mode 100644 dom/messages/test/manifest.webapp^headers^ delete mode 100644 dom/messages/test/test_sysmsg_registration.html diff --git a/dom/apps/Webapps.jsm b/dom/apps/Webapps.jsm index 05d3a9e2fe5..d94fc9b6dab 100644 --- a/dom/apps/Webapps.jsm +++ b/dom/apps/Webapps.jsm @@ -860,10 +860,6 @@ this.DOMApplicationRegistry = { if (!root.messages || !Array.isArray(root.messages) || root.messages.length == 0) { - dump("Could not register invalid system message entry\n"); - try { - dump(JSON.stringify(root.messages) + "\n"); - } catch(e) {} return; } @@ -873,31 +869,27 @@ this.DOMApplicationRegistry = { root.messages.forEach(function registerPages(aMessage) { let handlerPageURI = launchPathURI; let messageName; - if (typeof(aMessage) !== "object" || Object.keys(aMessage).length !== 1) { - dump("Could not register invalid system message entry\n"); + if (typeof(aMessage) === "object" && Object.keys(aMessage).length === 1) { + messageName = Object.keys(aMessage)[0]; + let handlerPath = aMessage[messageName]; + // Resolve the handler path from origin. If |handler_path| is absent, + // simply skip. + let fullHandlerPath; try { - dump(JSON.stringify(aMessage) + "\n"); - } catch(e) {} - return; - } - - messageName = Object.keys(aMessage)[0]; - let handlerPath = aMessage[messageName]; - // Resolve the handler path from origin. If |handler_path| is absent, - // simply skip. - let fullHandlerPath; - try { - if (handlerPath && handlerPath.trim()) { - fullHandlerPath = manifest.resolveURL(handlerPath); - } else { - throw new Error("Empty or blank handler path."); + if (handlerPath && handlerPath.trim()) { + fullHandlerPath = manifest.resolveURL(handlerPath); + } else { + throw new Error("Empty or blank handler path."); + } + } catch(e) { + debug("system message handler path (" + handlerPath + ") is " + + "invalid, skipping. Error is: " + e); + return; } - } catch(e) { - debug("system message handler path (" + handlerPath + ") is " + - "invalid, skipping. Error is: " + e); - return; + handlerPageURI = Services.io.newURI(fullHandlerPath, null, null); + } else { + messageName = aMessage; } - handlerPageURI = Services.io.newURI(fullHandlerPath, null, null); if (SystemMessagePermissionsChecker .isSystemMessagePermittedToRegister(messageName, diff --git a/dom/messages/SystemMessageInternal.js b/dom/messages/SystemMessageInternal.js index 072bcb17dbf..3e030070f73 100644 --- a/dom/messages/SystemMessageInternal.js +++ b/dom/messages/SystemMessageInternal.js @@ -273,7 +273,7 @@ SystemMessageInternal.prototype = { type: aType, msg: aMessage, extra: aExtra }); - return Promise.resolve(); + return; } // Give this message an ID so that we can identify the message and @@ -285,50 +285,37 @@ SystemMessageInternal.prototype = { let shouldDispatchFunc = this._getMessageConfigurator(aType).shouldDispatch; - if (!this._pages.length) { - return Promise.resolve(); - } + // Find pages that registered an handler for this type. + this._pages.forEach(function(aPage) { + if (aPage.type !== aType) { + return; + } - // Find pages that registered a handler for this type. - let promises = []; - for (let i = 0; i < this._pages.length; i++) { - let promise = ((page) => { - return new Promise((resolve, reject) => { - if (page.type !== aType) { - resolve(); - return; - } + let doDispatch = () => { + let result = this._sendMessageCommon(aType, + aMessage, + messageID, + aPage.pageURL, + aPage.manifestURL, + aExtra); + debug("Returned status of sending message: " + result); + }; - let doDispatch = () => { - let result = this._sendMessageCommon(aType, - aMessage, - messageID, - page.pageURL, - page.manifestURL, - aExtra); - debug("Returned status of sending message: " + result); - resolve(); - }; + if ('function' !== typeof shouldDispatchFunc) { + // If the configurator has no 'shouldDispatch' defined, + // always dispatch this message. + doDispatch(); + return; + } - if ('function' !== typeof shouldDispatchFunc) { - // If the configurator has no 'shouldDispatch' defined, - // always dispatch this message. + shouldDispatchFunc(aPage.manifestURL, aPage.pageURL, aType, aMessage, aExtra) + .then(aShouldDispatch => { + if (aShouldDispatch) { doDispatch(); - return; } - - shouldDispatchFunc(page.manifestURL, page.pageURL, aType, aMessage, aExtra) - .then(aShouldDispatch => { - if (aShouldDispatch) { - doDispatch(); - } - }); }); - })(this._pages[i]); - promises.push(promise); - } - return Promise.all(promises); + }, this); }, registerPage: function(aType, aPageURI, aManifestURI) { diff --git a/dom/messages/interfaces/nsISystemMessagesInternal.idl b/dom/messages/interfaces/nsISystemMessagesInternal.idl index 92e562cebe9..ad6f95121d1 100644 --- a/dom/messages/interfaces/nsISystemMessagesInternal.idl +++ b/dom/messages/interfaces/nsISystemMessagesInternal.idl @@ -10,7 +10,7 @@ interface nsIMessageSender; // Implemented by the contract id @mozilla.org/system-message-internal;1 -[scriptable, uuid(59b6beda-f911-4d47-a296-8c81e6abcfb9)] +[scriptable, uuid(54c8e274-decb-4258-9a24-4ebfcbf3d00a)] interface nsISystemMessagesInternal : nsISupports { /* @@ -36,10 +36,8 @@ interface nsISystemMessagesInternal : nsISupports * @param message The message payload. * @param extra Extra opaque information that will be passed around in the observer * notification to open the page. - * returns a Promise */ - nsISupports broadcastMessage(in DOMString type, in jsval message, - [optional] in jsval extra); + void broadcastMessage(in DOMString type, in jsval message, [optional] in jsval extra); /* * Registration of a page that wants to be notified of a message type. diff --git a/dom/messages/test/chrome.ini b/dom/messages/test/chrome.ini index cf8cbd4ad9b..77ad7c46861 100644 --- a/dom/messages/test/chrome.ini +++ b/dom/messages/test/chrome.ini @@ -1,11 +1,3 @@ -[DEFAULT] -skip-if = (buildapp != "browser") || e10s -support-files = - file_hasPendingMessage.html - invalid_manifest.webapp - invalid_manifest.webapp^headers^ - manifest.webapp - manifest.webapp^headers^ - [test_hasPendingMessage.html] -[test_sysmsg_registration.html] +skip-if = (buildapp != "browser") || e10s +support-files = file_hasPendingMessage.html diff --git a/dom/messages/test/invalid_manifest.webapp b/dom/messages/test/invalid_manifest.webapp deleted file mode 100644 index ead4f9f64b0..00000000000 --- a/dom/messages/test/invalid_manifest.webapp +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "Random app", - "launch_path": "/index.html", - "messages": [{ - "dummy-system-message": "/index.html", - "dummy-system-message2": "/index.html" - }] -} diff --git a/dom/messages/test/invalid_manifest.webapp^headers^ b/dom/messages/test/invalid_manifest.webapp^headers^ deleted file mode 100644 index 90818c6398e..00000000000 --- a/dom/messages/test/invalid_manifest.webapp^headers^ +++ /dev/null @@ -1 +0,0 @@ -Content-Type: application/manifest+json diff --git a/dom/messages/test/manifest.webapp b/dom/messages/test/manifest.webapp deleted file mode 100644 index 45b15930891..00000000000 --- a/dom/messages/test/manifest.webapp +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "Random app", - "launch_path": "/index.html", - "messages": [{ - "dummy-system-message": "/index.html" - }, { - "dummy-system-message2": "/index.html" - }] -} diff --git a/dom/messages/test/manifest.webapp^headers^ b/dom/messages/test/manifest.webapp^headers^ deleted file mode 100644 index 90818c6398e..00000000000 --- a/dom/messages/test/manifest.webapp^headers^ +++ /dev/null @@ -1 +0,0 @@ -Content-Type: application/manifest+json diff --git a/dom/messages/test/test_sysmsg_registration.html b/dom/messages/test/test_sysmsg_registration.html deleted file mode 100644 index adacbfa8ce4..00000000000 --- a/dom/messages/test/test_sysmsg_registration.html +++ /dev/null @@ -1,235 +0,0 @@ - - - - System messages registration tests - - - - - -Mozilla Bug {1162281} -

- -
-
-
- - From 38e011321b08390e724eb94f8df47aac3f10d3e6 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Tue, 19 May 2015 17:32:57 +1200 Subject: [PATCH 005/107] bug 1167045 don't reuse output sample when !mMFTProvidesOutputSamples r=mattwoodrow --- dom/media/platforms/wmf/MFTDecoder.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dom/media/platforms/wmf/MFTDecoder.cpp b/dom/media/platforms/wmf/MFTDecoder.cpp index 5fac9fdba1f..7a77c980b38 100644 --- a/dom/media/platforms/wmf/MFTDecoder.cpp +++ b/dom/media/platforms/wmf/MFTDecoder.cpp @@ -201,15 +201,15 @@ MFTDecoder::Output(RefPtr* aOutput) MFT_OUTPUT_DATA_BUFFER output = {0}; - bool providedSample = false; RefPtr sample; - if (*aOutput) { - output.pSample = *aOutput; - providedSample = true; - } else if (!mMFTProvidesOutputSamples) { - hr = CreateOutputSample(&sample); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - output.pSample = sample; + if (!mMFTProvidesOutputSamples) { + if (*aOutput) { + output.pSample = *aOutput; + } else { + hr = CreateOutputSample(&sample); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + output.pSample = sample; + } } DWORD status = 0; @@ -248,7 +248,7 @@ MFTDecoder::Output(RefPtr* aOutput) } *aOutput = output.pSample; // AddRefs - if (mMFTProvidesOutputSamples && !providedSample) { + if (mMFTProvidesOutputSamples) { // If the MFT is providing samples, we must release the sample here. // Typically only the H.264 MFT provides samples when using DXVA, // and it always re-uses the same sample, so if we don't release it From 3b1dcc246db604ca6d281ce998d59c84c5b5fc44 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 22 May 2015 15:23:00 +1200 Subject: [PATCH 006/107] bug 1162364 detect and abort MF_E_TRANSFORM_STREAM_CHANGE infinite loops r=cpearce --- dom/media/platforms/wmf/WMFAudioMFTManager.cpp | 6 ++++++ dom/media/platforms/wmf/WMFVideoMFTManager.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp index 64bfab608cf..9968a68fe69 100644 --- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp @@ -205,6 +205,7 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset, aOutData = nullptr; RefPtr sample; HRESULT hr; + int typeChangeCount = 0; while (true) { hr = mDecoder->Output(&sample); if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { @@ -213,6 +214,11 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset, if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { hr = UpdateOutputType(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + // Catch infinite loops, but some decoders perform at least 2 stream + // changes on consecutive calls, so be permissive. + // 100 is arbitrarily > 2. + NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE); + ++typeChangeCount; continue; } break; diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp index c345fa8bb4d..d50e0d7a749 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -482,6 +482,7 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset, RefPtr sample; HRESULT hr; aOutData = nullptr; + int typeChangeCount = 0; // Loop until we decode a sample, or an unexpected error that we can't // handle occurs. @@ -497,7 +498,12 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset, MOZ_ASSERT(!sample); hr = ConfigureVideoFrameGeometry(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + // Catch infinite loops, but some decoders perform at least 2 stream + // changes on consecutive calls, so be permissive. + // 100 is arbitrarily > 2. + NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE); // Loop back and try decoding again... + ++typeChangeCount; continue; } if (SUCCEEDED(hr)) { From 2a2deee1a1634e8e7866e7a744136b4d7d038bf2 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Mon, 25 May 2015 08:52:30 +1200 Subject: [PATCH 007/107] bug 1166107 release internal drain monitor before calling Flush() r=gerald The DrainComplete() caught with mWaitForInternalDrain still won't necessarily be from the internal Drain(), but all we need is that one DrainComplete() is caught for the internal Drain() because one more will be generated if there is a Drain() in progress. What protecting mWaitForInternalDrain access with the monitor provides here is that the compiler won't use its address for storage of other data meaningless in the context of mWaitForInternalDrain and so, for example, two DrainComplete() calls won't unintentionally think that they are both for one internal drain. And TSan warnings. --- dom/media/platforms/SharedDecoderManager.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dom/media/platforms/SharedDecoderManager.cpp b/dom/media/platforms/SharedDecoderManager.cpp index 6203973a699..55c50680533 100644 --- a/dom/media/platforms/SharedDecoderManager.cpp +++ b/dom/media/platforms/SharedDecoderManager.cpp @@ -163,16 +163,15 @@ void SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy) { if (aProxy && mActiveProxy == aProxy) { - MonitorAutoLock mon(mMonitor); - mWaitForInternalDrain = true; - nsresult rv; { - // We don't want to hold the lock while calling Drain() has some + MonitorAutoLock mon(mMonitor); + mWaitForInternalDrain = true; + // We don't want to hold the lock while calling Drain() as some // platform implementations call DrainComplete() immediately. - MonitorAutoUnlock mon(mMonitor); - rv = mActiveProxy->Drain(); } + nsresult rv = mActiveProxy->Drain(); if (NS_SUCCEEDED(rv)) { + MonitorAutoLock mon(mMonitor); while (mWaitForInternalDrain) { mon.Wait(); } From e40d0dfd2836a9fd2d6ee494915773bda4dfc8bc Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 22 May 2015 11:10:00 +1200 Subject: [PATCH 008/107] bug 1166107 documentation of mWaitForInternalDrain thread access r=gerald --- dom/media/platforms/SharedDecoderManager.cpp | 2 +- dom/media/platforms/SharedDecoderManager.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dom/media/platforms/SharedDecoderManager.cpp b/dom/media/platforms/SharedDecoderManager.cpp index 55c50680533..05c896a14b7 100644 --- a/dom/media/platforms/SharedDecoderManager.cpp +++ b/dom/media/platforms/SharedDecoderManager.cpp @@ -74,7 +74,7 @@ SharedDecoderManager::SharedDecoderManager() , mActiveProxy(nullptr) , mActiveCallback(nullptr) , mWaitForInternalDrain(false) - , mMonitor("SharedDecoderProxy") + , mMonitor("SharedDecoderManager") , mDecoderReleasedResources(false) { MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread. diff --git a/dom/media/platforms/SharedDecoderManager.h b/dom/media/platforms/SharedDecoderManager.h index c6b33d8fec5..7c283085662 100644 --- a/dom/media/platforms/SharedDecoderManager.h +++ b/dom/media/platforms/SharedDecoderManager.h @@ -56,6 +56,7 @@ private: SharedDecoderProxy* mActiveProxy; MediaDataDecoderCallback* mActiveCallback; nsAutoPtr mCallback; + // access protected by mMonitor bool mWaitForInternalDrain; Monitor mMonitor; bool mDecoderReleasedResources; From 71402a0b354e9a99e823dbb18531ca8cfe8d058a Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 May 2015 16:48:26 -0400 Subject: [PATCH 009/107] Bug 1160642. Add reftest-async-zoom to apply an async zoom before taking snapshot. r=dbaron --- dom/base/nsDOMWindowUtils.cpp | 27 ++++++++++++++ dom/interfaces/base/nsIDOMWindowUtils.idl | 10 ++++- gfx/layers/apz/src/AsyncPanZoomController.cpp | 6 ++- gfx/layers/apz/src/AsyncPanZoomController.h | 9 +++++ gfx/layers/ipc/LayerTransactionParent.cpp | 16 ++++++++ gfx/layers/ipc/LayerTransactionParent.h | 2 + gfx/layers/ipc/PLayerTransaction.ipdl | 5 +++ .../reftest-sanity/async-zoom-1-ref.html | 6 +++ .../reftests/reftest-sanity/async-zoom-1.html | 6 +++ .../reftest-sanity/async-zoom-2-ref.html | 6 +++ .../reftests/reftest-sanity/async-zoom-2.html | 6 +++ layout/reftests/reftest-sanity/reftest.list | 3 ++ layout/tools/reftest/README.txt | 24 ++++++++---- layout/tools/reftest/reftest-content.js | 37 +++++++++++++++++-- 14 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 layout/reftests/reftest-sanity/async-zoom-1-ref.html create mode 100644 layout/reftests/reftest-sanity/async-zoom-1.html create mode 100644 layout/reftests/reftest-sanity/async-zoom-2-ref.html create mode 100644 layout/reftests/reftest-sanity/async-zoom-2.html diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 73d86550e14..782de64457f 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2455,6 +2455,33 @@ nsDOMWindowUtils::SetAsyncScrollOffset(nsIDOMNode* aNode, return NS_OK; } +NS_IMETHODIMP +nsDOMWindowUtils::SetAsyncZoom(nsIDOMNode* aRootElement, float aValue) +{ + nsCOMPtr element = do_QueryInterface(aRootElement); + if (!element) { + return NS_ERROR_INVALID_ARG; + } + FrameMetrics::ViewID viewId; + if (!nsLayoutUtils::FindIDFor(element, &viewId)) { + return NS_ERROR_UNEXPECTED; + } + nsIWidget* widget = GetWidget(); + if (!widget) { + return NS_ERROR_FAILURE; + } + LayerManager* manager = widget->GetLayerManager(); + if (!manager) { + return NS_ERROR_FAILURE; + } + ShadowLayerForwarder* forwarder = manager->AsShadowForwarder(); + if (!forwarder || !forwarder->HasShadowManager()) { + return NS_ERROR_UNEXPECTED; + } + forwarder->GetShadowManager()->SendSetAsyncZoom(viewId, aValue); + return NS_OK; +} + NS_IMETHODIMP nsDOMWindowUtils::ComputeAnimationDistance(nsIDOMElement* aElement, const nsAString& aProperty, diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 72602da9f86..20e765585c3 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -49,7 +49,7 @@ interface nsIJSRAIIHelper; interface nsIContentPermissionRequest; interface nsIObserver; -[scriptable, uuid(0ce789cc-3fb6-48b8-a58e-32deefc337b4)] +[scriptable, uuid(7719798a-62fa-4dff-a6ed-782256649232)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1428,6 +1428,14 @@ interface nsIDOMWindowUtils : nsISupports { */ void setAsyncScrollOffset(in nsIDOMNode aNode, in int32_t aX, in int32_t aY); + /** + * Set async zoom value. aRootElement should be the document element of our + * document. The next composite will render with that zoom added to any + * existing zoom if async scrolling is enabled, and then the zoom will be + * removed. Only call this while test-controlled refreshes is enabled. + */ + void setAsyncZoom(in nsIDOMNode aRootElement, in float aValue); + /** * Method for testing StyleAnimationValue::ComputeDistance. * diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 9c1dc5601e7..34f17f63439 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -2750,9 +2750,11 @@ ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() const { } ParentLayerPoint translation = (currentScrollOffset - lastPaintScrollOffset) - * mFrameMetrics.GetZoom(); + * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale; - return ViewTransform(mFrameMetrics.GetAsyncZoom(), -translation); + return ViewTransform( + LayerToParentLayerScale(mFrameMetrics.GetAsyncZoom().scale * mTestAsyncZoom.scale), + -translation); } Matrix4x4 AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll() const { diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index ba756ef40a8..19679b43b8e 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -1069,6 +1069,13 @@ public: { mTestAsyncScrollOffset = aPoint; } + /** + * Set an extra offset for testing async scrolling. + */ + void SetTestAsyncZoom(const LayerToParentLayerScale& aZoom) + { + mTestAsyncZoom = aZoom; + } void MarkAsyncTransformAppliedToContent() { @@ -1083,6 +1090,8 @@ public: private: // Extra offset to add in SampleContentTransformForFrame for testing CSSPoint mTestAsyncScrollOffset; + // Extra zoom to include in SampleContentTransformForFrame for testing + LayerToParentLayerScale mTestAsyncZoom; // Flag to track whether or not the APZ transform is not used. This // flag is recomputed for every composition frame. bool mAsyncTransformAppliedToContent; diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp index 8fde530862f..f1cb1143a10 100644 --- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -783,6 +783,22 @@ LayerTransactionParent::RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aSc return true; } +bool +LayerTransactionParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollID, + const float& aValue) +{ + if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) { + return false; + } + + AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID); + if (!controller) { + return false; + } + controller->SetTestAsyncZoom(LayerToParentLayerScale(aValue)); + return true; +} + bool LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData) { diff --git a/gfx/layers/ipc/LayerTransactionParent.h b/gfx/layers/ipc/LayerTransactionParent.h index d63a3117d6b..e59b956deb9 100644 --- a/gfx/layers/ipc/LayerTransactionParent.h +++ b/gfx/layers/ipc/LayerTransactionParent.h @@ -133,6 +133,8 @@ protected: override; virtual bool RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aId, const int32_t& aX, const int32_t& aY) override; + virtual bool RecvSetAsyncZoom(const FrameMetrics::ViewID& aId, + const float& aValue) override; virtual bool RecvGetAPZTestData(APZTestData* aOutData) override; virtual bool RecvRequestProperty(const nsString& aProperty, float* aValue) override; virtual bool RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId, diff --git a/gfx/layers/ipc/PLayerTransaction.ipdl b/gfx/layers/ipc/PLayerTransaction.ipdl index 11a8ac65cf1..249d79097cd 100644 --- a/gfx/layers/ipc/PLayerTransaction.ipdl +++ b/gfx/layers/ipc/PLayerTransaction.ipdl @@ -89,6 +89,11 @@ parent: // Useful for testing rendering of async scrolling. sync SetAsyncScrollOffset(ViewID id, int32_t x, int32_t y); + // The next time the layer tree is composited, include this async zoom in + // for the given ViewID. + // Useful for testing rendering of async zooming. + sync SetAsyncZoom(ViewID id, float zoom); + // Drop any front buffers that might be retained on the compositor // side. async ClearCachedResources(); diff --git a/layout/reftests/reftest-sanity/async-zoom-1-ref.html b/layout/reftests/reftest-sanity/async-zoom-1-ref.html new file mode 100644 index 00000000000..734bce16da8 --- /dev/null +++ b/layout/reftests/reftest-sanity/async-zoom-1-ref.html @@ -0,0 +1,6 @@ + + + +This is some content. + + diff --git a/layout/reftests/reftest-sanity/async-zoom-1.html b/layout/reftests/reftest-sanity/async-zoom-1.html new file mode 100644 index 00000000000..7f4d2244d1b --- /dev/null +++ b/layout/reftests/reftest-sanity/async-zoom-1.html @@ -0,0 +1,6 @@ + + + +This is some content. + + diff --git a/layout/reftests/reftest-sanity/async-zoom-2-ref.html b/layout/reftests/reftest-sanity/async-zoom-2-ref.html new file mode 100644 index 00000000000..939f62579f3 --- /dev/null +++ b/layout/reftests/reftest-sanity/async-zoom-2-ref.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/reftests/reftest-sanity/async-zoom-2.html b/layout/reftests/reftest-sanity/async-zoom-2.html new file mode 100644 index 00000000000..6ca88367885 --- /dev/null +++ b/layout/reftests/reftest-sanity/async-zoom-2.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/reftests/reftest-sanity/reftest.list b/layout/reftests/reftest-sanity/reftest.list index ba8c80cd570..2f3c3e60d82 100644 --- a/layout/reftests/reftest-sanity/reftest.list +++ b/layout/reftests/reftest-sanity/reftest.list @@ -166,6 +166,9 @@ default-preferences pref(layers.low-precision-buffer,false) skip-if(!asyncPanZoom||!browserIsRemote) != async-scroll-1b.html async-scroll-1-ref.html default-preferences +skip-if(!asyncPanZoom) != async-zoom-1.html async-zoom-1-ref.html +fuzzy(112,1197) skip-if(!asyncPanZoom) == async-zoom-2.html async-zoom-2-ref.html + # reftest-opaque-layer == reftest-opaque-layer-pass.html reftest-opaque-layer-pass.html != reftest-opaque-layer-pass.html about:blank diff --git a/layout/tools/reftest/README.txt b/layout/tools/reftest/README.txt index f68ace1dae3..aeab2f4f5d3 100644 --- a/layout/tools/reftest/README.txt +++ b/layout/tools/reftest/README.txt @@ -495,12 +495,12 @@ Zoom Tests: reftest-zoom="" ================================== When the root element of a test has a "reftest-zoom" attribute, that zoom -factor is applied when rendering the test. The reftest document will be -800 device pixels wide by 1000 device pixels high. The reftest harness assumes -that the CSS pixel dimensions are 800/zoom and 1000/zoom. For best results -therefore, choose zoom factors that do not require rounding when we calculate -the number of appunits per device pixel; i.e. the zoom factor should divide 60, -so 60/zoom is an integer. +factor is applied when rendering the test. The corresponds to the desktop "full +zoom" style zoom. The reftest document will be 800 device pixels wide by 1000 +device pixels high. The reftest harness assumes that the CSS pixel dimensions +are 800/zoom and 1000/zoom. For best results therefore, choose zoom factors +that do not require rounding when we calculate the number of appunits per +device pixel; i.e. the zoom factor should divide 60, so 60/zoom is an integer. Setting Viewport Size: reftest-viewport-w/h="" =================================================== @@ -521,7 +521,7 @@ Setting Async Scroll Mode: reftest-async-scroll attribute ========================================================= If the "reftest-async-scroll" attribute is set on the root element, we try to -enable async scrolling for the document. This is unsupported in many +enable async scrolling and zooming for the document. This is unsupported in many configurations. Setting Displayport Dimensions: reftest-displayport-x/y/w/h="" @@ -544,6 +544,16 @@ element where either the "reftest-async-scroll-x" or "reftest-async-scroll-y attributes are nonzero, at the end of the test take the snapshot with the given offset (in CSS pixels) added to the async scroll offset. +Testing Async Zooming: reftest-async-zoom="" +========================================================= + +When the "reftest-async-zoom" attribute is present on the root element then at +the end of the test take the snapshot with the given async zoom on top of any +existing zoom. Content is not re-rendered at the new zoom level. This +corresponds to the mobile style "pinch zoom" style of zoom. This is unsupported +in many configurations, and any tests using this will probably want to have +skip-if(!asyncPanZoom) on them. + Printing Tests: class="reftest-print" ===================================== diff --git a/layout/tools/reftest/reftest-content.js b/layout/tools/reftest/reftest-content.js index acf7cf3fff7..2388b012ecb 100644 --- a/layout/tools/reftest/reftest-content.js +++ b/layout/tools/reftest/reftest-content.js @@ -140,7 +140,7 @@ function StartTestURI(type, uri, timeout) LoadURI(gCurrentURL); } -function setupZoom(contentRootElement) { +function setupFullZoom(contentRootElement) { if (!contentRootElement || !contentRootElement.hasAttribute('reftest-zoom')) return; markupDocumentViewer().fullZoom = @@ -300,6 +300,28 @@ function setupAsyncScrollOffsets(options) { return false; } +function setupAsyncZoom(options) { + var currentDoc = content.document; + var contentRootElement = currentDoc ? currentDoc.documentElement : null; + + if (!contentRootElement || !contentRootElement.hasAttribute('reftest-async-zoom')) + return false; + + var zoom = attrOrDefault(contentRootElement, "reftest-async-zoom", 1); + if (zoom != 1) { + try { + windowUtils().setAsyncZoom(contentRootElement, zoom); + return true; + } catch (e) { + if (!options.allowFailure) { + throw e; + } + } + } + return false; +} + + function resetDisplayportAndViewport() { // XXX currently the displayport configuration lives on the // presshell and so is "reset" on nav when we get a new presshell. @@ -637,7 +659,7 @@ function OnDocumentLoad(event) var contentRootElement = currentDoc ? currentDoc.documentElement : null; currentDoc = null; - setupZoom(contentRootElement); + setupFullZoom(contentRootElement); setupViewport(contentRootElement); setupDisplayport(contentRootElement); var inPrintMode = false; @@ -812,8 +834,14 @@ function RecordResult() // Setup async scroll offsets now in case SynchronizeForSnapshot is not // called (due to reftest-no-sync-layers being supplied, or in the single // process case). - var changedAsyncScrollOffsets = setupAsyncScrollOffsets({allowFailure:true}) ; - if (changedAsyncScrollOffsets && !gBrowserIsRemote) { + var changedAsyncScrollZoom = false; + if (setupAsyncScrollOffsets({allowFailure:true})) { + changedAsyncScrollZoom = true; + } + if (setupAsyncZoom({allowFailure:true})) { + changedAsyncScrollZoom = true; + } + if (changedAsyncScrollZoom && !gBrowserIsRemote) { sendAsyncMessage("reftest:UpdateWholeCanvasForInvalidation"); } @@ -895,6 +923,7 @@ function SynchronizeForSnapshot(flags) // Setup async scroll offsets now, because any scrollable layers should // have had their AsyncPanZoomControllers created. setupAsyncScrollOffsets({allowFailure:false}); + setupAsyncZoom({allowFailure:false}); } function RegisterMessageListeners() From b04762c394b6e8d72d065534bcec251adf37bf21 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sun, 24 May 2015 16:48:26 -0400 Subject: [PATCH 010/107] Bug 1151617 - Add async-zooming tests for async scrollbar positioning. r=tn,botond --- .../apz/reftests/async-scrollbar-zoom-1-ref.html | 9 +++++++++ .../apz/reftests/async-scrollbar-zoom-1.html | 14 ++++++++++++++ .../apz/reftests/async-scrollbar-zoom-2-ref.html | 9 +++++++++ .../apz/reftests/async-scrollbar-zoom-2.html | 14 ++++++++++++++ gfx/layers/apz/reftests/reftest.list | 8 ++++++++ 5 files changed, 54 insertions(+) create mode 100644 gfx/layers/apz/reftests/async-scrollbar-zoom-1-ref.html create mode 100644 gfx/layers/apz/reftests/async-scrollbar-zoom-1.html create mode 100644 gfx/layers/apz/reftests/async-scrollbar-zoom-2-ref.html create mode 100644 gfx/layers/apz/reftests/async-scrollbar-zoom-2.html diff --git a/gfx/layers/apz/reftests/async-scrollbar-zoom-1-ref.html b/gfx/layers/apz/reftests/async-scrollbar-zoom-1-ref.html new file mode 100644 index 00000000000..5ed970f7644 --- /dev/null +++ b/gfx/layers/apz/reftests/async-scrollbar-zoom-1-ref.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/gfx/layers/apz/reftests/async-scrollbar-zoom-1.html b/gfx/layers/apz/reftests/async-scrollbar-zoom-1.html new file mode 100644 index 00000000000..09be51a79af --- /dev/null +++ b/gfx/layers/apz/reftests/async-scrollbar-zoom-1.html @@ -0,0 +1,14 @@ + + + + + + +
+ + + diff --git a/gfx/layers/apz/reftests/async-scrollbar-zoom-2-ref.html b/gfx/layers/apz/reftests/async-scrollbar-zoom-2-ref.html new file mode 100644 index 00000000000..5ed970f7644 --- /dev/null +++ b/gfx/layers/apz/reftests/async-scrollbar-zoom-2-ref.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/gfx/layers/apz/reftests/async-scrollbar-zoom-2.html b/gfx/layers/apz/reftests/async-scrollbar-zoom-2.html new file mode 100644 index 00000000000..abe822c21b8 --- /dev/null +++ b/gfx/layers/apz/reftests/async-scrollbar-zoom-2.html @@ -0,0 +1,14 @@ + + + + + + +
+ + + diff --git a/gfx/layers/apz/reftests/reftest.list b/gfx/layers/apz/reftests/reftest.list index 33c54a36748..bda25be5ce5 100644 --- a/gfx/layers/apz/reftests/reftest.list +++ b/gfx/layers/apz/reftests/reftest.list @@ -6,3 +6,11 @@ skip-if(!asyncPanZoom) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.htm skip-if(!asyncPanZoom) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html skip-if(!asyncPanZoom) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html skip-if(!asyncPanZoom) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html + +# Different zoom levels. Since B2G is the only APZ-enabled platform where we +# currently allow async zooming, that's the only platform on which these tests +# are run. And because the scrollthumb gets async-scaled in the compositor, the +# border-radius ends of the scrollthumb are going to be a little off, hence the +# fuzzy-if clauses. +skip-if(!asyncPanZoom||!B2G) fuzzy-if(B2G,77,82) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html +skip-if(!asyncPanZoom||!B2G) fuzzy-if(B2G,94,146) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html From 92cf4ee2ff575bec8800fbca3dbfb356f1d008dd Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 5 May 2015 16:11:43 -0700 Subject: [PATCH 011/107] Bug 1166598 (part 1) - Use PLDHashTable2 in nsScriptNameSpaceManager. r=froydnj. --- dom/base/nsScriptNameSpaceManager.cpp | 44 ++++++++++----------------- dom/base/nsScriptNameSpaceManager.h | 6 ++-- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/dom/base/nsScriptNameSpaceManager.cpp b/dom/base/nsScriptNameSpaceManager.cpp index d068115be12..3c28890ad73 100644 --- a/dom/base/nsScriptNameSpaceManager.cpp +++ b/dom/base/nsScriptNameSpaceManager.cpp @@ -119,20 +119,29 @@ NS_IMPL_ISUPPORTS( nsISupportsWeakReference, nsIMemoryReporter) +static const PLDHashTableOps hash_table_ops = +{ + GlobalNameHashHashKey, + GlobalNameHashMatchEntry, + PL_DHashMoveEntryStub, + GlobalNameHashClearEntry, + GlobalNameHashInitEntry +}; + +#define GLOBALNAME_HASHTABLE_INITIAL_LENGTH 512 + nsScriptNameSpaceManager::nsScriptNameSpaceManager() - : mIsInitialized(false) + : mGlobalNames(&hash_table_ops, sizeof(GlobalNameMapEntry), + GLOBALNAME_HASHTABLE_INITIAL_LENGTH) + , mNavigatorNames(&hash_table_ops, sizeof(GlobalNameMapEntry), + GLOBALNAME_HASHTABLE_INITIAL_LENGTH) { MOZ_COUNT_CTOR(nsScriptNameSpaceManager); } nsScriptNameSpaceManager::~nsScriptNameSpaceManager() { - if (mIsInitialized) { - UnregisterWeakMemoryReporter(this); - // Destroy the hash - PL_DHashTableFinish(&mGlobalNames); - PL_DHashTableFinish(&mNavigatorNames); - } + UnregisterWeakMemoryReporter(this); MOZ_COUNT_DTOR(nsScriptNameSpaceManager); } @@ -309,30 +318,9 @@ nsScriptNameSpaceManager::RegisterInterface(const char* aIfName, return NS_OK; } -#define GLOBALNAME_HASHTABLE_INITIAL_LENGTH 512 - nsresult nsScriptNameSpaceManager::Init() { - static const PLDHashTableOps hash_table_ops = - { - GlobalNameHashHashKey, - GlobalNameHashMatchEntry, - PL_DHashMoveEntryStub, - GlobalNameHashClearEntry, - GlobalNameHashInitEntry - }; - - PL_DHashTableInit(&mGlobalNames, &hash_table_ops, - sizeof(GlobalNameMapEntry), - GLOBALNAME_HASHTABLE_INITIAL_LENGTH); - - PL_DHashTableInit(&mNavigatorNames, &hash_table_ops, - sizeof(GlobalNameMapEntry), - GLOBALNAME_HASHTABLE_INITIAL_LENGTH); - - mIsInitialized = true; - RegisterWeakMemoryReporter(this); nsresult rv = NS_OK; diff --git a/dom/base/nsScriptNameSpaceManager.h b/dom/base/nsScriptNameSpaceManager.h index a1a93178f07..fe909a2930b 100644 --- a/dom/base/nsScriptNameSpaceManager.h +++ b/dom/base/nsScriptNameSpaceManager.h @@ -234,10 +234,8 @@ private: nsGlobalNameStruct* LookupNameInternal(const nsAString& aName, const char16_t **aClassName = nullptr); - PLDHashTable mGlobalNames; - PLDHashTable mNavigatorNames; - - bool mIsInitialized; + PLDHashTable2 mGlobalNames; + PLDHashTable2 mNavigatorNames; }; #endif /* nsScriptNameSpaceManager_h__ */ From 42ce5ac7f438f6a64f228fe34ef499cf55c587a0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 5 May 2015 16:11:43 -0700 Subject: [PATCH 012/107] Bug 1166598 (part 2) - Use PLDHashTable2 in SpanningCellSorter. r=froydnj,dbaron. --- layout/tables/SpanningCellSorter.cpp | 19 +++---------------- layout/tables/SpanningCellSorter.h | 2 +- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/layout/tables/SpanningCellSorter.cpp b/layout/tables/SpanningCellSorter.cpp index 63bc700bc4c..a2334e35c82 100644 --- a/layout/tables/SpanningCellSorter.cpp +++ b/layout/tables/SpanningCellSorter.cpp @@ -16,6 +16,7 @@ SpanningCellSorter::SpanningCellSorter() : mState(ADDING) + , mHashTable(&HashTableOps, sizeof(HashTableEntry)) , mSortedHashTable(nullptr) { memset(mArray, 0, sizeof(mArray)); @@ -23,9 +24,6 @@ SpanningCellSorter::SpanningCellSorter() SpanningCellSorter::~SpanningCellSorter() { - if (mHashTable.IsInitialized()) { - PL_DHashTableFinish(&mHashTable); - } delete [] mSortedHashTable; } @@ -70,10 +68,6 @@ SpanningCellSorter::AddCell(int32_t aColSpan, int32_t aRow, int32_t aCol) i->next = mArray[index]; mArray[index] = i; } else { - if (!mHashTable.IsInitialized()) { - PL_DHashTableInit(&mHashTable, &HashTableOps, - sizeof(HashTableEntry)); - } HashTableEntry *entry = static_cast (PL_DHashTableAdd(&mHashTable, NS_INT32_TO_PTR(aColSpan), fallible)); @@ -146,14 +140,9 @@ SpanningCellSorter::GetNext(int32_t *aColSpan) /* prepare to enumerate the hash */ mState = ENUMERATING_HASH; mEnumerationIndex = 0; - if (mHashTable.IsInitialized()) { + if (mHashTable.EntryCount() > 0) { HashTableEntry **sh = new HashTableEntry*[mHashTable.EntryCount()]; - if (!sh) { - // give up - mState = DONE; - return nullptr; - } PL_DHashTableEnumerate(&mHashTable, FillSortedArray, sh); NS_QuickSort(sh, mHashTable.EntryCount(), sizeof(sh[0]), SortArray, nullptr); @@ -161,9 +150,7 @@ SpanningCellSorter::GetNext(int32_t *aColSpan) } /* fall through */ case ENUMERATING_HASH: - if (mHashTable.IsInitialized() && - mEnumerationIndex < mHashTable.EntryCount()) - { + if (mEnumerationIndex < mHashTable.EntryCount()) { Item *result = mSortedHashTable[mEnumerationIndex]->mItems; *aColSpan = mSortedHashTable[mEnumerationIndex]->mColSpan; NS_ASSERTION(result, "holes in hash table"); diff --git a/layout/tables/SpanningCellSorter.h b/layout/tables/SpanningCellSorter.h index de8c2143452..6e46cbc62e2 100644 --- a/layout/tables/SpanningCellSorter.h +++ b/layout/tables/SpanningCellSorter.h @@ -62,7 +62,7 @@ private: return SpanToIndex(aSpan) < ARRAY_SIZE; } - PLDHashTable mHashTable; + PLDHashTable2 mHashTable; struct HashTableEntry : public PLDHashEntryHdr { int32_t mColSpan; Item *mItems; From ff4aa2cbfdc8ae58682ad4ea175fc40dfe5e081f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 5 May 2015 18:23:39 -0700 Subject: [PATCH 013/107] Bug 1166598 (part 3) - Use PLDHashTable2 in nsCommandParams. r=froydnj. --- embedding/components/build/nsEmbeddingModule.cpp | 2 +- .../components/commandhandler/nsCommandParams.cpp | 10 +--------- embedding/components/commandhandler/nsCommandParams.h | 4 +--- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/embedding/components/build/nsEmbeddingModule.cpp b/embedding/components/build/nsEmbeddingModule.cpp index 9ab26fbd75f..48351ec3adc 100644 --- a/embedding/components/build/nsEmbeddingModule.cpp +++ b/embedding/components/build/nsEmbeddingModule.cpp @@ -31,7 +31,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebBrowserFind) NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebBrowserPersist) NS_GENERIC_FACTORY_CONSTRUCTOR(nsControllerCommandTable) NS_GENERIC_FACTORY_CONSTRUCTOR(nsCommandManager) -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCommandParams, Init) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsCommandParams) NS_GENERIC_FACTORY_CONSTRUCTOR(nsControllerCommandGroup) NS_GENERIC_FACTORY_CONSTRUCTOR(nsBaseCommandController) diff --git a/embedding/components/commandhandler/nsCommandParams.cpp b/embedding/components/commandhandler/nsCommandParams.cpp index addb9711466..9aaf94744ff 100644 --- a/embedding/components/commandhandler/nsCommandParams.cpp +++ b/embedding/components/commandhandler/nsCommandParams.cpp @@ -25,20 +25,12 @@ const PLDHashTableOps nsCommandParams::sHashOps = NS_IMPL_ISUPPORTS(nsCommandParams, nsICommandParams) nsCommandParams::nsCommandParams() + : mValuesHash(&sHashOps, sizeof(HashEntry), 2) { - // init the hash table later } nsCommandParams::~nsCommandParams() { - PL_DHashTableFinish(&mValuesHash); -} - -nsresult -nsCommandParams::Init() -{ - PL_DHashTableInit(&mValuesHash, &sHashOps, sizeof(HashEntry), 2); - return NS_OK; } NS_IMETHODIMP diff --git a/embedding/components/commandhandler/nsCommandParams.h b/embedding/components/commandhandler/nsCommandParams.h index 32a590f0a08..ee85a5f1817 100644 --- a/embedding/components/commandhandler/nsCommandParams.h +++ b/embedding/components/commandhandler/nsCommandParams.h @@ -20,8 +20,6 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSICOMMANDPARAMS - nsresult Init(); - protected: virtual ~nsCommandParams(); @@ -126,7 +124,7 @@ protected: static void HashClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry); - PLDHashTable mValuesHash; + PLDHashTable2 mValuesHash; static const PLDHashTableOps sHashOps; }; From 0c7ca68cbede9323065d61ad617a8fa7f2642998 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 5 May 2015 18:30:27 -0700 Subject: [PATCH 014/107] Bug 1166598 (part 4) - Use PLDHashTable2 in RDFServiceImpl. r=froydnj. --- rdf/base/nsRDFService.cpp | 31 ++++++------------------------- rdf/base/nsRDFService.h | 10 +++++----- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/rdf/base/nsRDFService.cpp b/rdf/base/nsRDFService.cpp index a4ee099f222..1540d888efd 100644 --- a/rdf/base/nsRDFService.cpp +++ b/rdf/base/nsRDFService.cpp @@ -719,7 +719,12 @@ RDFServiceImpl* RDFServiceImpl::gRDFService; RDFServiceImpl::RDFServiceImpl() - : mNamedDataSources(nullptr) + : mNamedDataSources(nullptr) + , mResources(&gResourceTableOps, sizeof(ResourceHashEntry)) + , mLiterals(&gLiteralTableOps, sizeof(LiteralHashEntry)) + , mInts(&gIntTableOps, sizeof(IntHashEntry)) + , mDates(&gDateTableOps, sizeof(DateHashEntry)) + , mBlobs(&gBlobTableOps, sizeof(BlobHashEntry)) { gRDFService = this; } @@ -738,17 +743,6 @@ RDFServiceImpl::Init() if (! mNamedDataSources) return NS_ERROR_OUT_OF_MEMORY; - PL_DHashTableInit(&mResources, &gResourceTableOps, - sizeof(ResourceHashEntry)); - - PL_DHashTableInit(&mLiterals, &gLiteralTableOps, sizeof(LiteralHashEntry)); - - PL_DHashTableInit(&mInts, &gIntTableOps, sizeof(IntHashEntry)); - - PL_DHashTableInit(&mDates, &gDateTableOps, sizeof(DateHashEntry)); - - PL_DHashTableInit(&mBlobs, &gBlobTableOps, sizeof(BlobHashEntry)); - mDefaultResourceFactory = do_GetClassObject(kRDFDefaultResourceCID, &rv); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get default resource factory"); if (NS_FAILED(rv)) return rv; @@ -766,16 +760,6 @@ RDFServiceImpl::~RDFServiceImpl() PL_HashTableDestroy(mNamedDataSources); mNamedDataSources = nullptr; } - if (mResources.IsInitialized()) - PL_DHashTableFinish(&mResources); - if (mLiterals.IsInitialized()) - PL_DHashTableFinish(&mLiterals); - if (mInts.IsInitialized()) - PL_DHashTableFinish(&mInts); - if (mDates.IsInitialized()) - PL_DHashTableFinish(&mDates); - if (mBlobs.IsInitialized()) - PL_DHashTableFinish(&mBlobs); gRDFService = nullptr; } @@ -793,9 +777,6 @@ RDFServiceImpl::CreateSingleton(nsISupports* aOuter, } nsRefPtr serv = new RDFServiceImpl(); - if (!serv) - return NS_ERROR_OUT_OF_MEMORY; - nsresult rv = serv->Init(); if (NS_FAILED(rv)) return rv; diff --git a/rdf/base/nsRDFService.h b/rdf/base/nsRDFService.h index 8dad600975a..0c193351641 100644 --- a/rdf/base/nsRDFService.h +++ b/rdf/base/nsRDFService.h @@ -38,11 +38,11 @@ class RDFServiceImpl final : public nsIRDFService, { protected: PLHashTable* mNamedDataSources; - PLDHashTable mResources; - PLDHashTable mLiterals; - PLDHashTable mInts; - PLDHashTable mDates; - PLDHashTable mBlobs; + PLDHashTable2 mResources; + PLDHashTable2 mLiterals; + PLDHashTable2 mInts; + PLDHashTable2 mDates; + PLDHashTable2 mBlobs; nsAutoCString mLastURIPrefix; nsCOMPtr mLastFactory; From 6f0071f22914d56ae68b487bbee4955993a90093 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 5 May 2015 18:39:20 -0700 Subject: [PATCH 015/107] Bug 1166598 (part 5) - Use PLDHashTable2 in InMemoryDataSource. r=froydnj. --- rdf/base/nsInMemoryDataSource.cpp | 36 +++++++++---------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/rdf/base/nsInMemoryDataSource.cpp b/rdf/base/nsInMemoryDataSource.cpp index 9b1b6fced17..7414408e37d 100644 --- a/rdf/base/nsInMemoryDataSource.cpp +++ b/rdf/base/nsInMemoryDataSource.cpp @@ -250,8 +250,8 @@ protected: // nsIRDFResource object per unique URI). The value of an entry is // an Assertion struct, which is a linked list of (subject // predicate object) triples. - PLDHashTable mForwardArcs; - PLDHashTable mReverseArcs; + PLDHashTable2 mForwardArcs; + PLDHashTable2 mReverseArcs; nsCOMArray mObservers; uint32_t mNumObservers; @@ -286,7 +286,6 @@ protected: explicit InMemoryDataSource(nsISupports* aOuter); virtual ~InMemoryDataSource(); - nsresult Init(); friend nsresult NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResult); @@ -752,16 +751,11 @@ NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResu } InMemoryDataSource* datasource = new InMemoryDataSource(aOuter); - if (! datasource) - return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(datasource); - nsresult rv = datasource->Init(); - if (NS_SUCCEEDED(rv)) { - datasource->fAggregated.AddRef(); - rv = datasource->AggregatedQueryInterface(aIID, aResult); // This'll AddRef() - datasource->fAggregated.Release(); - } + datasource->fAggregated.AddRef(); + nsresult rv = datasource->AggregatedQueryInterface(aIID, aResult); // This'll AddRef() + datasource->fAggregated.Release(); NS_RELEASE(datasource); return rv; @@ -769,25 +763,18 @@ NS_NewRDFInMemoryDataSource(nsISupports* aOuter, const nsIID& aIID, void** aResu InMemoryDataSource::InMemoryDataSource(nsISupports* aOuter) - : mNumObservers(0), mReadCount(0) + : mForwardArcs(PL_DHashGetStubOps(), sizeof(Entry)) + , mReverseArcs(PL_DHashGetStubOps(), sizeof(Entry)) + , mNumObservers(0) + , mReadCount(0) { NS_INIT_AGGREGATED(aOuter); mPropagateChanges = true; MOZ_COUNT_CTOR(InMemoryDataSource); -} - - -nsresult -InMemoryDataSource::Init() -{ - PL_DHashTableInit(&mForwardArcs, PL_DHashGetStubOps(), sizeof(Entry)); - PL_DHashTableInit(&mReverseArcs, PL_DHashGetStubOps(), sizeof(Entry)); if (! gLog) gLog = PR_NewLogModule("InMemoryDataSource"); - - return NS_OK; } @@ -798,16 +785,13 @@ InMemoryDataSource::~InMemoryDataSource() fprintf(stdout, "%d - RDF: InMemoryDataSource\n", gInstanceCount); #endif - if (mForwardArcs.IsInitialized()) { + if (mForwardArcs.EntryCount() > 0) { // This'll release all of the Assertion objects that are // associated with this data source. We only need to do this // for the forward arcs, because the reverse arcs table // indexes the exact same set of resources. PL_DHashTableEnumerate(&mForwardArcs, DeleteForwardArcsEntry, nullptr); - PL_DHashTableFinish(&mForwardArcs); } - if (mReverseArcs.IsInitialized()) - PL_DHashTableFinish(&mReverseArcs); MOZ_LOG(gLog, PR_LOG_NOTICE, ("InMemoryDataSource(%p): destroyed.", this)); From 16479c956046e22ce3af6e40ca4f445792dd015b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 5 May 2015 21:13:53 -0700 Subject: [PATCH 016/107] Bug 1166598 (part 6) - Clean up nsStaticCaseInsensitiveNameTable. r=froydnj. This patch simplifies nsStaticCaseInsensitiveNameTable greatly by taking advantage of the following observations. - |new| is infallible, so |new nsStaticCaseInsensitiveNameTable()| calls don't need their return value checked. - nsStaticCaseInsensitiveNameTable::Init() checks if any of the added entries differ only in case, so the callers of that function don't need to do the same check. - nsStaticCaseInsensitiveNameTable::Init() never returns false because moz_xmalloc() is infallible. (Its callers never check the return value anyway.) So it can be merged into the constructor. And ~nsStaticCaseInsensitiveNameTable() need not null-check |mNameArray|. - PLDHashTable now has an initializing constructor and destructor, so these can be used in nsStaticCaseInsensitiveNameTable, thus avoiding manual PLD_HashTable{Init,Finish}() calls. --- gfx/src/nsColor.cpp | 17 ++--------- layout/style/nsCSSKeywords.cpp | 25 ++++++--------- layout/style/nsCSSProps.cpp | 21 +++++-------- xpcom/ds/nsStaticNameTable.cpp | 56 ++++++++++++---------------------- xpcom/ds/nsStaticNameTable.h | 5 ++- 5 files changed, 40 insertions(+), 84 deletions(-) diff --git a/gfx/src/nsColor.cpp b/gfx/src/nsColor.cpp index ea59ba64da2..4b8613e5057 100644 --- a/gfx/src/nsColor.cpp +++ b/gfx/src/nsColor.cpp @@ -39,21 +39,8 @@ void nsColorNames::AddRefTable(void) { NS_ASSERTION(!gColorTable, "pre existing array!"); if (!gColorTable) { - gColorTable = new nsStaticCaseInsensitiveNameTable(); - if (gColorTable) { -#ifdef DEBUG - { - // let's verify the table... - for (uint32_t index = 0; index < eColorName_COUNT; ++index) { - nsAutoCString temp1(kColorNames[index]); - nsAutoCString temp2(kColorNames[index]); - ToLowerCase(temp1); - NS_ASSERTION(temp1.Equals(temp2), "upper case char in table"); - } - } -#endif - gColorTable->Init(kColorNames, eColorName_COUNT); - } + gColorTable = + new nsStaticCaseInsensitiveNameTable(kColorNames, eColorName_COUNT); } } diff --git a/layout/style/nsCSSKeywords.cpp b/layout/style/nsCSSKeywords.cpp index c61b0f65715..8dd362e7c2b 100644 --- a/layout/style/nsCSSKeywords.cpp +++ b/layout/style/nsCSSKeywords.cpp @@ -27,24 +27,17 @@ nsCSSKeywords::AddRefTable(void) { if (0 == gKeywordTableRefCount++) { NS_ASSERTION(!gKeywordTable, "pre existing array!"); - gKeywordTable = new nsStaticCaseInsensitiveNameTable(); - if (gKeywordTable) { + gKeywordTable = + new nsStaticCaseInsensitiveNameTable(kCSSRawKeywords, eCSSKeyword_COUNT); #ifdef DEBUG - { - // let's verify the table... - int32_t index = 0; - for (; index < eCSSKeyword_COUNT && kCSSRawKeywords[index]; ++index) { - nsAutoCString temp1(kCSSRawKeywords[index]); - nsAutoCString temp2(kCSSRawKeywords[index]); - ToLowerCase(temp1); - NS_ASSERTION(temp1.Equals(temp2), "upper case char in table"); - NS_ASSERTION(-1 == temp1.FindChar('_'), "underscore char in table"); - } - NS_ASSERTION(index == eCSSKeyword_COUNT, "kCSSRawKeywords and eCSSKeyword_COUNT are out of sync"); - } -#endif - gKeywordTable->Init(kCSSRawKeywords, eCSSKeyword_COUNT); + // Partially verify the entries. + int32_t index = 0; + for (; index < eCSSKeyword_COUNT && kCSSRawKeywords[index]; ++index) { + nsAutoCString temp(kCSSRawKeywords[index]); + NS_ASSERTION(-1 == temp.FindChar('_'), "underscore char in table"); } + NS_ASSERTION(index == eCSSKeyword_COUNT, "kCSSRawKeywords and eCSSKeyword_COUNT are out of sync"); +#endif } } diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 7a6ccd3aa3d..8527020fc36 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -134,22 +134,15 @@ static nsCSSProperty gAliases[eCSSAliasCount != 0 ? eCSSAliasCount : 1] = { nsStaticCaseInsensitiveNameTable* CreateStaticTable(const char* const aRawTable[], int32_t aLength) { - auto table = new nsStaticCaseInsensitiveNameTable(); - if (table) { + auto table = new nsStaticCaseInsensitiveNameTable(aRawTable, aLength); #ifdef DEBUG - // let's verify the table... - for (int32_t index = 0; index < aLength; ++index) { - nsAutoCString temp1(aRawTable[index]); - nsAutoCString temp2(aRawTable[index]); - ToLowerCase(temp1); - MOZ_ASSERT(temp1.Equals(temp2), - "upper case char in case insensitive name table"); - MOZ_ASSERT(-1 == temp1.FindChar('_'), - "underscore char in case insensitive name table"); - } -#endif - table->Init(aRawTable, aLength); + // Partially verify the entries. + for (int32_t index = 0; index < aLength; ++index) { + nsAutoCString temp(aRawTable[index]); + MOZ_ASSERT(-1 == temp.FindChar('_'), + "underscore char in case insensitive name table"); } +#endif return table; } diff --git a/xpcom/ds/nsStaticNameTable.cpp b/xpcom/ds/nsStaticNameTable.cpp index 1c655baba05..e872da54e7b 100644 --- a/xpcom/ds/nsStaticNameTable.cpp +++ b/xpcom/ds/nsStaticNameTable.cpp @@ -101,45 +101,20 @@ static const struct PLDHashTableOps nametable_CaseInsensitiveHashTableOps = { nullptr, }; -nsStaticCaseInsensitiveNameTable::nsStaticCaseInsensitiveNameTable() +nsStaticCaseInsensitiveNameTable::nsStaticCaseInsensitiveNameTable( + const char* const aNames[], int32_t aLength) : mNameArray(nullptr) + , mNameTable(&nametable_CaseInsensitiveHashTableOps, + sizeof(NameTableEntry), aLength) , mNullStr("") { MOZ_COUNT_CTOR(nsStaticCaseInsensitiveNameTable); -} -nsStaticCaseInsensitiveNameTable::~nsStaticCaseInsensitiveNameTable() -{ - if (mNameArray) { - // manually call the destructor on placement-new'ed objects - for (uint32_t index = 0; index < mNameTable.EntryCount(); index++) { - mNameArray[index].~nsDependentCString(); - } - free((void*)mNameArray); - } - if (mNameTable.IsInitialized()) { - PL_DHashTableFinish(&mNameTable); - } - MOZ_COUNT_DTOR(nsStaticCaseInsensitiveNameTable); -} - -bool -nsStaticCaseInsensitiveNameTable::Init(const char* const aNames[], - int32_t aLength) -{ - NS_ASSERTION(!mNameArray, "double Init"); - NS_ASSERTION(!mNameTable.IsInitialized(), "double Init"); - NS_ASSERTION(aNames, "null name table"); - NS_ASSERTION(aLength, "0 length"); + MOZ_ASSERT(aNames, "null name table"); + MOZ_ASSERT(aLength, "0 length"); mNameArray = (nsDependentCString*) moz_xmalloc(aLength * sizeof(nsDependentCString)); - if (!mNameArray) { - return false; - } - - PL_DHashTableInit(&mNameTable, &nametable_CaseInsensitiveHashTableOps, - sizeof(NameTableEntry), aLength); for (int32_t index = 0; index < aLength; ++index) { const char* raw = aNames[index]; @@ -149,10 +124,10 @@ nsStaticCaseInsensitiveNameTable::Init(const char* const aNames[], nsAutoCString temp1(raw); nsDependentCString temp2(raw); ToLowerCase(temp1); - NS_ASSERTION(temp1.Equals(temp2), "upper case char in table"); - NS_ASSERTION(nsCRT::IsAscii(raw), - "non-ascii string in table -- " - "case-insensitive matching won't work right"); + MOZ_ASSERT(temp1.Equals(temp2), "upper case char in table"); + MOZ_ASSERT(nsCRT::IsAscii(raw), + "non-ascii string in table -- " + "case-insensitive matching won't work right"); } #endif // use placement-new to initialize the string object @@ -175,7 +150,16 @@ nsStaticCaseInsensitiveNameTable::Init(const char* const aNames[], #ifdef DEBUG PL_DHashMarkTableImmutable(&mNameTable); #endif - return true; +} + +nsStaticCaseInsensitiveNameTable::~nsStaticCaseInsensitiveNameTable() +{ + // manually call the destructor on placement-new'ed objects + for (uint32_t index = 0; index < mNameTable.EntryCount(); index++) { + mNameArray[index].~nsDependentCString(); + } + free((void*)mNameArray); + MOZ_COUNT_DTOR(nsStaticCaseInsensitiveNameTable); } int32_t diff --git a/xpcom/ds/nsStaticNameTable.h b/xpcom/ds/nsStaticNameTable.h index 5bcc7039074..f447ab7018d 100644 --- a/xpcom/ds/nsStaticNameTable.h +++ b/xpcom/ds/nsStaticNameTable.h @@ -33,17 +33,16 @@ class nsStaticCaseInsensitiveNameTable public: enum { NOT_FOUND = -1 }; - bool Init(const char* const aNames[], int32_t aLength); int32_t Lookup(const nsACString& aName); int32_t Lookup(const nsAString& aName); const nsAFlatCString& GetStringValue(int32_t aIndex); - nsStaticCaseInsensitiveNameTable(); + nsStaticCaseInsensitiveNameTable(const char* const aNames[], int32_t aLength); ~nsStaticCaseInsensitiveNameTable(); private: nsDependentCString* mNameArray; - PLDHashTable mNameTable; + PLDHashTable2 mNameTable; nsDependentCString mNullStr; }; From 4cdc3dfb3c36fe1854495adf57edf4a9be0b62b3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 12 May 2015 17:33:44 -0700 Subject: [PATCH 017/107] Bug 1166598 (part 7) - Use PLDHashTable2 in nsLoadGroup. r=froydnj. Things to note: - nsLoadGroupConnectionInfo and its methods were just moved higher up in the file so it could be referenced in nsLoadGroup's constructor; none of that code has been changed; - ~nsLoadGroup() is made public because NS_GENERIC_AGGREGATED_CONSTRUCTOR requires it to be. --- netwerk/base/nsLoadGroup.cpp | 159 ++++++++++++++++------------------ netwerk/base/nsLoadGroup.h | 7 +- netwerk/build/nsNetModule.cpp | 2 +- 3 files changed, 76 insertions(+), 92 deletions(-) diff --git a/netwerk/base/nsLoadGroup.cpp b/netwerk/base/nsLoadGroup.cpp index c0b71507623..cf9e4aa07d8 100644 --- a/netwerk/base/nsLoadGroup.cpp +++ b/netwerk/base/nsLoadGroup.cpp @@ -43,6 +43,69 @@ static PRLogModuleInfo* gLoadGroupLog = nullptr; #undef LOG #define LOG(args) MOZ_LOG(gLoadGroupLog, PR_LOG_DEBUG, args) +//////////////////////////////////////////////////////////////////////////////// +// nsLoadGroupConnectionInfo + +class nsLoadGroupConnectionInfo final : public nsILoadGroupConnectionInfo +{ + ~nsLoadGroupConnectionInfo() {} + +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSILOADGROUPCONNECTIONINFO + + nsLoadGroupConnectionInfo(); +private: + Atomic mBlockingTransactionCount; + nsAutoPtr mSpdyCache; +}; + +NS_IMPL_ISUPPORTS(nsLoadGroupConnectionInfo, nsILoadGroupConnectionInfo) + +nsLoadGroupConnectionInfo::nsLoadGroupConnectionInfo() + : mBlockingTransactionCount(0) +{ +} + +NS_IMETHODIMP +nsLoadGroupConnectionInfo::GetBlockingTransactionCount(uint32_t *aBlockingTransactionCount) +{ + NS_ENSURE_ARG_POINTER(aBlockingTransactionCount); + *aBlockingTransactionCount = mBlockingTransactionCount; + return NS_OK; +} + +NS_IMETHODIMP +nsLoadGroupConnectionInfo::AddBlockingTransaction() +{ + mBlockingTransactionCount++; + return NS_OK; +} + +NS_IMETHODIMP +nsLoadGroupConnectionInfo::RemoveBlockingTransaction(uint32_t *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + mBlockingTransactionCount--; + *_retval = mBlockingTransactionCount; + return NS_OK; +} + +/* [noscript] attribute SpdyPushCachePtr spdyPushCache; */ +NS_IMETHODIMP +nsLoadGroupConnectionInfo::GetSpdyPushCache(mozilla::net::SpdyPushCache **aSpdyPushCache) +{ + *aSpdyPushCache = mSpdyCache.get(); + return NS_OK; +} + +NS_IMETHODIMP +nsLoadGroupConnectionInfo::SetSpdyPushCache(mozilla::net::SpdyPushCache *aSpdyPushCache) +{ + mSpdyCache = aSpdyPushCache; + return NS_OK; +} + //////////////////////////////////////////////////////////////////////////////// class RequestMapEntry : public PLDHashEntryHdr @@ -86,6 +149,14 @@ RequestHashInitEntry(PLDHashEntryHdr *entry, const void *key) new (entry) RequestMapEntry(request); } +static const PLDHashTableOps sRequestHashOps = +{ + PL_DHashVoidPtrKeyStub, + RequestHashMatchEntry, + PL_DHashMoveEntryStub, + RequestHashClearEntry, + RequestHashInitEntry +}; static void RescheduleRequest(nsIRequest *aRequest, int32_t delta) @@ -106,11 +177,12 @@ RescheduleRequests(PLDHashTable *table, PLDHashEntryHdr *hdr, return PL_DHASH_NEXT; } - nsLoadGroup::nsLoadGroup(nsISupports* outer) : mForegroundCount(0) , mLoadFlags(LOAD_NORMAL) , mDefaultLoadFlags(0) + , mConnectionInfo(new nsLoadGroupConnectionInfo()) + , mRequests(&sRequestHashOps, sizeof(RequestMapEntry)) , mStatus(NS_OK) , mPriority(PRIORITY_NORMAL) , mIsCanceling(false) @@ -133,10 +205,6 @@ nsLoadGroup::~nsLoadGroup() DebugOnly rv = Cancel(NS_BINDING_ABORTED); NS_ASSERTION(NS_SUCCEEDED(rv), "Cancel failed"); - if (mRequests.IsInitialized()) { - PL_DHashTableFinish(&mRequests); - } - mDefaultLoadRequest = 0; LOG(("LOADGROUP [%x]: Destroyed.\n", this)); @@ -1055,85 +1123,4 @@ nsresult nsLoadGroup::MergeLoadFlags(nsIRequest *aRequest, nsLoadFlags& outFlags return rv; } -// nsLoadGroupConnectionInfo - -class nsLoadGroupConnectionInfo final : public nsILoadGroupConnectionInfo -{ - ~nsLoadGroupConnectionInfo() {} - -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSILOADGROUPCONNECTIONINFO - - nsLoadGroupConnectionInfo(); -private: - Atomic mBlockingTransactionCount; - nsAutoPtr mSpdyCache; -}; - -NS_IMPL_ISUPPORTS(nsLoadGroupConnectionInfo, nsILoadGroupConnectionInfo) - -nsLoadGroupConnectionInfo::nsLoadGroupConnectionInfo() - : mBlockingTransactionCount(0) -{ -} - -NS_IMETHODIMP -nsLoadGroupConnectionInfo::GetBlockingTransactionCount(uint32_t *aBlockingTransactionCount) -{ - NS_ENSURE_ARG_POINTER(aBlockingTransactionCount); - *aBlockingTransactionCount = mBlockingTransactionCount; - return NS_OK; -} - -NS_IMETHODIMP -nsLoadGroupConnectionInfo::AddBlockingTransaction() -{ - mBlockingTransactionCount++; - return NS_OK; -} - -NS_IMETHODIMP -nsLoadGroupConnectionInfo::RemoveBlockingTransaction(uint32_t *_retval) -{ - NS_ENSURE_ARG_POINTER(_retval); - mBlockingTransactionCount--; - *_retval = mBlockingTransactionCount; - return NS_OK; -} - -/* [noscript] attribute SpdyPushCachePtr spdyPushCache; */ -NS_IMETHODIMP -nsLoadGroupConnectionInfo::GetSpdyPushCache(mozilla::net::SpdyPushCache **aSpdyPushCache) -{ - *aSpdyPushCache = mSpdyCache.get(); - return NS_OK; -} - -NS_IMETHODIMP -nsLoadGroupConnectionInfo::SetSpdyPushCache(mozilla::net::SpdyPushCache *aSpdyPushCache) -{ - mSpdyCache = aSpdyPushCache; - return NS_OK; -} - -nsresult nsLoadGroup::Init() -{ - static const PLDHashTableOps hash_table_ops = - { - PL_DHashVoidPtrKeyStub, - RequestHashMatchEntry, - PL_DHashMoveEntryStub, - RequestHashClearEntry, - RequestHashInitEntry - }; - - PL_DHashTableInit(&mRequests, &hash_table_ops, - sizeof(RequestMapEntry)); - - mConnectionInfo = new nsLoadGroupConnectionInfo(); - - return NS_OK; -} - #undef LOG diff --git a/netwerk/base/nsLoadGroup.h b/netwerk/base/nsLoadGroup.h index 28d4c29a9c5..b89380bee21 100644 --- a/netwerk/base/nsLoadGroup.h +++ b/netwerk/base/nsLoadGroup.h @@ -50,12 +50,9 @@ public: // nsLoadGroup methods: explicit nsLoadGroup(nsISupports* outer); - - nsresult Init(); - -protected: virtual ~nsLoadGroup(); +protected: nsresult MergeLoadFlags(nsIRequest *aRequest, nsLoadFlags& flags); private: @@ -73,7 +70,7 @@ protected: nsCOMPtr mConnectionInfo; nsCOMPtr mDefaultLoadRequest; - PLDHashTable mRequests; + PLDHashTable2 mRequests; nsWeakPtr mObserver; nsWeakPtr mParentLoadGroup; diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index af6a3b827fa..63ee99de43d 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -113,7 +113,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsSafeFileOutputStream) NS_GENERIC_FACTORY_CONSTRUCTOR(nsFileStream) -NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT(nsLoadGroup, Init) +NS_GENERIC_AGGREGATED_CONSTRUCTOR(nsLoadGroup) #include "ArrayBufferInputStream.h" NS_GENERIC_FACTORY_CONSTRUCTOR(ArrayBufferInputStream) From adcd365f5e225eb0f017433394fbc30069644a1c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 12 May 2015 17:33:45 -0700 Subject: [PATCH 018/107] Bug 1166598 (part 8) - Use PLDHashTable2 in nsHostResolver. r=froydnj. --- netwerk/dns/nsHostResolver.cpp | 6 +----- netwerk/dns/nsHostResolver.h | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index f173e9b2c3a..9b7cfde4882 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -552,6 +552,7 @@ nsHostResolver::nsHostResolver(uint32_t maxCacheEntries, , mNumIdleThreads(0) , mThreadCount(0) , mActiveAnyThreadCount(0) + , mDB(&gHostDB_ops, sizeof(nsHostDBEnt), 0) , mEvictionQSize(0) , mPendingCount(0) , mShutdown(true) @@ -568,7 +569,6 @@ nsHostResolver::nsHostResolver(uint32_t maxCacheEntries, nsHostResolver::~nsHostResolver() { - PL_DHashTableFinish(&mDB); } nsresult @@ -578,8 +578,6 @@ nsHostResolver::Init() return NS_ERROR_FAILURE; } - PL_DHashTableInit(&mDB, &gHostDB_ops, sizeof(nsHostDBEnt), 0); - mShutdown = false; #if TTL_AVAILABLE @@ -1496,8 +1494,6 @@ nsHostResolver::Create(uint32_t maxCacheEntries, nsHostResolver *res = new nsHostResolver(maxCacheEntries, defaultCacheEntryLifetime, defaultGracePeriod); - if (!res) - return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(res); nsresult rv = res->Init(); diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h index 88b4d618fd6..6c7587d4e55 100644 --- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -345,7 +345,7 @@ private: uint32_t mNumIdleThreads; uint32_t mThreadCount; uint32_t mActiveAnyThreadCount; - PLDHashTable mDB; + PLDHashTable2 mDB; PRCList mHighQ; PRCList mMediumQ; PRCList mLowQ; From 23f2b638f415a3a6b074f81af7b5eeedc823703c Mon Sep 17 00:00:00 2001 From: JW Wang Date: Sun, 10 May 2015 12:07:14 +0800 Subject: [PATCH 019/107] Bug 1163489 - Remove dependency on MediaDecoder from DecodedStream. r=roc. --- dom/media/DecodedStream.cpp | 62 +++++++++++++++++++++---------------- dom/media/DecodedStream.h | 15 ++++----- dom/media/MediaDecoder.cpp | 5 +-- 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/dom/media/DecodedStream.cpp b/dom/media/DecodedStream.cpp index 2a976c7730e..891361345f9 100644 --- a/dom/media/DecodedStream.cpp +++ b/dom/media/DecodedStream.cpp @@ -6,16 +6,15 @@ #include "DecodedStream.h" #include "MediaStreamGraph.h" -#include "MediaDecoder.h" +#include "mozilla/ReentrantMonitor.h" namespace mozilla { class DecodedStreamGraphListener : public MediaStreamListener { typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent; public: - DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData) - : mData(aData) - , mMutex("DecodedStreamGraphListener::mMutex") + explicit DecodedStreamGraphListener(MediaStream* aStream) + : mMutex("DecodedStreamGraphListener::mMutex") , mStream(aStream) , mLastOutputTime(aStream->StreamTimeToMicroseconds(aStream->GetCurrentTime())) , mStreamFinishedOnMainThread(false) {} @@ -52,7 +51,6 @@ public: void Forget() { MOZ_ASSERT(NS_IsMainThread()); - mData = nullptr; MutexAutoLock lock(mMutex); mStream = nullptr; } @@ -64,9 +62,6 @@ public: } private: - // Main thread only - DecodedStreamData* mData; - Mutex mMutex; // Members below are protected by mMutex. nsRefPtr mStream; @@ -74,14 +69,12 @@ private: bool mStreamFinishedOnMainThread; }; -DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder, - int64_t aInitialTime, +DecodedStreamData::DecodedStreamData(int64_t aInitialTime, SourceMediaStream* aStream) : mAudioFramesWritten(0) , mInitialTime(aInitialTime) , mNextVideoTime(-1) , mNextAudioTime(-1) - , mDecoder(aDecoder) , mStreamInitialized(false) , mHaveSentFinish(false) , mHaveSentFinishAudio(false) @@ -91,7 +84,7 @@ DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder, , mHaveBlockedForStateMachineNotPlaying(false) , mEOSVideoCompensation(false) { - mListener = new DecodedStreamGraphListener(mStream, this); + mListener = new DecodedStreamGraphListener(mStream); mStream->AddListener(mListener); } @@ -116,8 +109,8 @@ DecodedStreamData::GetClock() const class OutputStreamListener : public MediaStreamListener { typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent; public: - OutputStreamListener(MediaDecoder* aDecoder, MediaStream* aStream) - : mDecoder(aDecoder), mStream(aStream) {} + OutputStreamListener(DecodedStream* aDecodedStream, MediaStream* aStream) + : mDecodedStream(aDecodedStream), mStream(aStream) {} void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override { @@ -131,22 +124,22 @@ public: void Forget() { MOZ_ASSERT(NS_IsMainThread()); - mDecoder = nullptr; + mDecodedStream = nullptr; } private: void DoNotifyFinished() { MOZ_ASSERT(NS_IsMainThread()); - if (!mDecoder) { + if (!mDecodedStream) { return; } // Remove the finished stream so it won't block the decoded stream. - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - auto& streams = mDecoder->OutputStreams(); - // Don't read |mDecoder| in the loop since removing the element will lead - // to ~OutputStreamData() which will call Forget() to reset |mDecoder|. + ReentrantMonitorAutoEnter mon(mDecodedStream->GetReentrantMonitor()); + auto& streams = mDecodedStream->OutputStreams(); + // Don't read |mDecodedStream| in the loop since removing the element will lead + // to ~OutputStreamData() which will call Forget() to reset |mDecodedStream|. for (int32_t i = streams.Length() - 1; i >= 0; --i) { auto& os = streams[i]; MediaStream* p = os.mStream.get(); @@ -162,7 +155,7 @@ private: } // Main thread only - MediaDecoder* mDecoder; + DecodedStream* mDecodedStream; nsRefPtr mStream; }; @@ -177,37 +170,54 @@ OutputStreamData::~OutputStreamData() } void -OutputStreamData::Init(MediaDecoder* aDecoder, ProcessedMediaStream* aStream) +OutputStreamData::Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream) { mStream = aStream; - mListener = new OutputStreamListener(aDecoder, aStream); + mListener = new OutputStreamListener(aDecodedStream, aStream); aStream->AddListener(mListener); } +DecodedStream::DecodedStream(ReentrantMonitor& aMonitor) + : mMonitor(aMonitor) +{ + // +} + DecodedStreamData* DecodedStream::GetData() { + GetReentrantMonitor().AssertCurrentThreadIn(); return mData.get(); } void DecodedStream::DestroyData() { + MOZ_ASSERT(NS_IsMainThread()); + GetReentrantMonitor().AssertCurrentThreadIn(); mData = nullptr; } void -DecodedStream::RecreateData(MediaDecoder* aDecoder, int64_t aInitialTime, - SourceMediaStream* aStream) +DecodedStream::RecreateData(int64_t aInitialTime, SourceMediaStream* aStream) { + MOZ_ASSERT(NS_IsMainThread()); + GetReentrantMonitor().AssertCurrentThreadIn(); MOZ_ASSERT(!mData); - mData.reset(new DecodedStreamData(aDecoder, aInitialTime, aStream)); + mData.reset(new DecodedStreamData(aInitialTime, aStream)); } nsTArray& DecodedStream::OutputStreams() { + GetReentrantMonitor().AssertCurrentThreadIn(); return mOutputStreams; } +ReentrantMonitor& +DecodedStream::GetReentrantMonitor() +{ + return mMonitor; +} + } // namespace mozilla diff --git a/dom/media/DecodedStream.h b/dom/media/DecodedStream.h index 74dd93c89f7..f6ce4503ba8 100644 --- a/dom/media/DecodedStream.h +++ b/dom/media/DecodedStream.h @@ -14,13 +14,14 @@ namespace mozilla { -class MediaDecoder; class MediaInputPort; class SourceMediaStream; class ProcessedMediaStream; +class DecodedStream; class DecodedStreamGraphListener; class OutputStreamData; class OutputStreamListener; +class ReentrantMonitor; namespace layers { class Image; @@ -36,8 +37,7 @@ class Image; */ class DecodedStreamData { public: - DecodedStreamData(MediaDecoder* aDecoder, int64_t aInitialTime, - SourceMediaStream* aStream); + DecodedStreamData(int64_t aInitialTime, SourceMediaStream* aStream); ~DecodedStreamData(); bool IsFinished() const; int64_t GetClock() const; @@ -55,7 +55,6 @@ public: // to the output stream. int64_t mNextVideoTime; // microseconds int64_t mNextAudioTime; // microseconds - MediaDecoder* mDecoder; // The last video image sent to the stream. Useful if we need to replicate // the image. nsRefPtr mLastVideoImage; @@ -89,7 +88,7 @@ public: // to work. OutputStreamData(); ~OutputStreamData(); - void Init(MediaDecoder* aDecoder, ProcessedMediaStream* aStream); + void Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream); nsRefPtr mStream; // mPort connects DecodedStreamData::mStream to our mStream. nsRefPtr mPort; @@ -98,16 +97,18 @@ public: class DecodedStream { public: + explicit DecodedStream(ReentrantMonitor& aMonitor); DecodedStreamData* GetData(); void DestroyData(); - void RecreateData(MediaDecoder* aDecoder, int64_t aInitialTime, - SourceMediaStream* aStream); + void RecreateData(int64_t aInitialTime, SourceMediaStream* aStream); nsTArray& OutputStreams(); + ReentrantMonitor& GetReentrantMonitor(); private: UniquePtr mData; // Data about MediaStreams that are being fed by the decoder. nsTArray mOutputStreams; + ReentrantMonitor& mMonitor; }; } // namespace mozilla diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 041417ba2c6..3d05d8ab528 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -384,7 +384,7 @@ void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs, } DestroyDecodedStream(); - mDecodedStream.RecreateData(this, aStartTimeUSecs, aGraph->CreateSourceStream(nullptr)); + mDecodedStream.RecreateData(aStartTimeUSecs, aGraph->CreateSourceStream(nullptr)); // Note that the delay between removing ports in DestroyDecodedStream // and adding new ones won't cause a glitch since all graph operations @@ -419,7 +419,7 @@ void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream, RecreateDecodedStream(mLogicalPosition, aStream->Graph()); } OutputStreamData* os = OutputStreams().AppendElement(); - os->Init(this, aStream); + os->Init(&mDecodedStream, aStream); ConnectDecodedStreamToOutputStream(os); if (aFinishWhenEnded) { // Ensure that aStream finishes the moment mDecodedStream does. @@ -483,6 +483,7 @@ MediaDecoder::MediaDecoder() : mMediaSeekable(true), mSameOriginMedia(false), mReentrantMonitor("media.decoder"), + mDecodedStream(mReentrantMonitor), mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING, "MediaDecoder::mPlayState (Canonical)"), mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED, From 2be891d110b78cd3b7683c5d4f4f2d1d73638b64 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Mon, 25 May 2015 14:31:02 +1200 Subject: [PATCH 020/107] back out 780f3d9c7cc3..afb28a3157b3 for test failures (bug 1167045, bug 1162364, bug 1166107) CLOSED TREE --- dom/media/platforms/SharedDecoderManager.cpp | 13 +++++++------ dom/media/platforms/SharedDecoderManager.h | 1 - dom/media/platforms/wmf/MFTDecoder.cpp | 18 +++++++++--------- dom/media/platforms/wmf/WMFAudioMFTManager.cpp | 6 ------ dom/media/platforms/wmf/WMFVideoMFTManager.cpp | 6 ------ 5 files changed, 16 insertions(+), 28 deletions(-) diff --git a/dom/media/platforms/SharedDecoderManager.cpp b/dom/media/platforms/SharedDecoderManager.cpp index 05c896a14b7..6203973a699 100644 --- a/dom/media/platforms/SharedDecoderManager.cpp +++ b/dom/media/platforms/SharedDecoderManager.cpp @@ -74,7 +74,7 @@ SharedDecoderManager::SharedDecoderManager() , mActiveProxy(nullptr) , mActiveCallback(nullptr) , mWaitForInternalDrain(false) - , mMonitor("SharedDecoderManager") + , mMonitor("SharedDecoderProxy") , mDecoderReleasedResources(false) { MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread. @@ -163,15 +163,16 @@ void SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy) { if (aProxy && mActiveProxy == aProxy) { + MonitorAutoLock mon(mMonitor); + mWaitForInternalDrain = true; + nsresult rv; { - MonitorAutoLock mon(mMonitor); - mWaitForInternalDrain = true; - // We don't want to hold the lock while calling Drain() as some + // We don't want to hold the lock while calling Drain() has some // platform implementations call DrainComplete() immediately. + MonitorAutoUnlock mon(mMonitor); + rv = mActiveProxy->Drain(); } - nsresult rv = mActiveProxy->Drain(); if (NS_SUCCEEDED(rv)) { - MonitorAutoLock mon(mMonitor); while (mWaitForInternalDrain) { mon.Wait(); } diff --git a/dom/media/platforms/SharedDecoderManager.h b/dom/media/platforms/SharedDecoderManager.h index 7c283085662..c6b33d8fec5 100644 --- a/dom/media/platforms/SharedDecoderManager.h +++ b/dom/media/platforms/SharedDecoderManager.h @@ -56,7 +56,6 @@ private: SharedDecoderProxy* mActiveProxy; MediaDataDecoderCallback* mActiveCallback; nsAutoPtr mCallback; - // access protected by mMonitor bool mWaitForInternalDrain; Monitor mMonitor; bool mDecoderReleasedResources; diff --git a/dom/media/platforms/wmf/MFTDecoder.cpp b/dom/media/platforms/wmf/MFTDecoder.cpp index 7a77c980b38..5fac9fdba1f 100644 --- a/dom/media/platforms/wmf/MFTDecoder.cpp +++ b/dom/media/platforms/wmf/MFTDecoder.cpp @@ -201,15 +201,15 @@ MFTDecoder::Output(RefPtr* aOutput) MFT_OUTPUT_DATA_BUFFER output = {0}; + bool providedSample = false; RefPtr sample; - if (!mMFTProvidesOutputSamples) { - if (*aOutput) { - output.pSample = *aOutput; - } else { - hr = CreateOutputSample(&sample); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - output.pSample = sample; - } + if (*aOutput) { + output.pSample = *aOutput; + providedSample = true; + } else if (!mMFTProvidesOutputSamples) { + hr = CreateOutputSample(&sample); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + output.pSample = sample; } DWORD status = 0; @@ -248,7 +248,7 @@ MFTDecoder::Output(RefPtr* aOutput) } *aOutput = output.pSample; // AddRefs - if (mMFTProvidesOutputSamples) { + if (mMFTProvidesOutputSamples && !providedSample) { // If the MFT is providing samples, we must release the sample here. // Typically only the H.264 MFT provides samples when using DXVA, // and it always re-uses the same sample, so if we don't release it diff --git a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp index 9968a68fe69..64bfab608cf 100644 --- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp @@ -205,7 +205,6 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset, aOutData = nullptr; RefPtr sample; HRESULT hr; - int typeChangeCount = 0; while (true) { hr = mDecoder->Output(&sample); if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { @@ -214,11 +213,6 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset, if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { hr = UpdateOutputType(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - // Catch infinite loops, but some decoders perform at least 2 stream - // changes on consecutive calls, so be permissive. - // 100 is arbitrarily > 2. - NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE); - ++typeChangeCount; continue; } break; diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp index d50e0d7a749..c345fa8bb4d 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -482,7 +482,6 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset, RefPtr sample; HRESULT hr; aOutData = nullptr; - int typeChangeCount = 0; // Loop until we decode a sample, or an unexpected error that we can't // handle occurs. @@ -498,12 +497,7 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset, MOZ_ASSERT(!sample); hr = ConfigureVideoFrameGeometry(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - // Catch infinite loops, but some decoders perform at least 2 stream - // changes on consecutive calls, so be permissive. - // 100 is arbitrarily > 2. - NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE); // Loop back and try decoding again... - ++typeChangeCount; continue; } if (SUCCEEDED(hr)) { From cd5471d5c91f5344f50c369ed09b609919dac122 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:15 +1000 Subject: [PATCH 021/107] Bug 1159171: Part2. Fix pts calculations in older LibAV version. r=edwin LibAV v0.8 always set the pts to 0 while the dts is set to the pts. --- .../platforms/ffmpeg/FFmpegH264Decoder.cpp | 30 ++++++++++++++++++- .../platforms/ffmpeg/FFmpegH264Decoder.h | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp index ff31439af29..94735a86976 100644 --- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp @@ -12,6 +12,7 @@ #include "MediaInfo.h" #include "FFmpegH264Decoder.h" +#include "FFmpegLog.h" #define GECKO_FRAME_TYPE 0x00093CC0 @@ -49,6 +50,20 @@ FFmpegH264Decoder::Init() return NS_OK; } +int64_t +FFmpegH264Decoder::GetPts(const AVPacket& packet) +{ +#if LIBAVCODEC_VERSION_MAJOR == 53 + if (mFrame->pkt_pts == 0) { + return mFrame->pkt_dts; + } else { + return mFrame->pkt_pts; + } +#else + return mFrame->pkt_pts; +#endif +} + FFmpegH264Decoder::DecodeResult FFmpegH264Decoder::DoDecodeFrame(MediaRawData* aSample) { @@ -68,10 +83,19 @@ FFmpegH264Decoder::DoDecodeFrame(MediaRawData* aSample) return DecodeResult::DECODE_ERROR; } + // Required with old version of FFmpeg/LibAV + mFrame->reordered_opaque = AV_NOPTS_VALUE; + int decoded; int bytesConsumed = avcodec_decode_video2(mCodecContext, mFrame, &decoded, &packet); + FFMPEG_LOG("DoDecodeFrame:decode_video: rv=%d decoded=%d " + "(Input: pts(%lld) dts(%lld) Output: pts(%lld) " + "opaque(%lld) pkt_pts(%lld) pkt_dts(%lld))", + bytesConsumed, decoded, packet.pts, packet.dts, mFrame->pts, + mFrame->reordered_opaque, mFrame->pkt_pts, mFrame->pkt_dts); + if (bytesConsumed < 0) { NS_WARNING("FFmpeg video decoder error."); mCallback->Error(); @@ -80,6 +104,10 @@ FFmpegH264Decoder::DoDecodeFrame(MediaRawData* aSample) // If we've decoded a frame then we need to output it if (decoded) { + int64_t pts = GetPts(packet); + FFMPEG_LOG("Got one frame output with pts=%lld opaque=%lld", + pts, mCodecContext->reordered_opaque); + VideoInfo info; info.mDisplay = nsIntSize(mDisplayWidth, mDisplayHeight); @@ -105,7 +133,7 @@ FFmpegH264Decoder::DoDecodeFrame(MediaRawData* aSample) nsRefPtr v = VideoData::Create(info, mImageContainer, aSample->mOffset, - mFrame->pkt_pts, + pts, aSample->mDuration, b, aSample->mKeyframe, diff --git a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h index 82d5329b9a8..0ff1f5bed42 100644 --- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h +++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h @@ -59,6 +59,7 @@ private: static int AllocateBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame); static void ReleaseBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame); + int64_t GetPts(const AVPacket& packet); MediaDataDecoderCallback* mCallback; nsRefPtr mImageContainer; From 50d43ee5fbec637b1912633a8e38bde215a4472f Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:15 +1000 Subject: [PATCH 022/107] Bug 1165938: Disable test on XP/Linux for intermittent failures. r=karlt --- .../meta/media-source/mediasource-redundant-seek.html.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini b/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini index 0e6464a8947..17e9746b948 100644 --- a/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini @@ -1,8 +1,9 @@ [mediasource-redundant-seek.html] type: testharness prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false] - expected: - if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): TIMEOUT + disabled: + if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1165938 + if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1165938 [Test redundant fully prebuffered seek] disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1154881 From 88bb1a8eb1bcfc40e20c6ca23e260c41ab62be39 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 023/107] Bug 1166282: Have TimeIntervals and IntervalSet nsTArray_CopyChooser specialization. r=nfroyd --- dom/media/Intervals.h | 14 ++++++++++++++ dom/media/TimeUnits.h | 12 ++++++++++++ dom/media/gtest/TestIntervalSet.cpp | 9 +++++++++ 3 files changed, 35 insertions(+) diff --git a/dom/media/Intervals.h b/dom/media/Intervals.h index f5d747be1b0..8655b824578 100644 --- a/dom/media/Intervals.h +++ b/dom/media/Intervals.h @@ -11,6 +11,20 @@ #include "mozilla/TypeTraits.h" #include "nsTArray.h" +// Specialization for nsTArray CopyChooser. +namespace mozilla { +namespace media { +template +class IntervalSet; +} +} + +template +struct nsTArray_CopyChooser> +{ + typedef nsTArray_CopyWithConstructors> Type; +}; + namespace mozilla { namespace media { diff --git a/dom/media/TimeUnits.h b/dom/media/TimeUnits.h index f4784814157..6828221a1f7 100644 --- a/dom/media/TimeUnits.h +++ b/dom/media/TimeUnits.h @@ -12,6 +12,18 @@ #include "mozilla/FloatingPoint.h" #include "mozilla/dom/TimeRanges.h" +namespace mozilla { +namespace media { +class TimeIntervals; +} +} +// CopyChooser specalization for nsTArray +template<> +struct nsTArray_CopyChooser +{ + typedef nsTArray_CopyWithConstructors Type; +}; + namespace mozilla { namespace media { diff --git a/dom/media/gtest/TestIntervalSet.cpp b/dom/media/gtest/TestIntervalSet.cpp index 266ecb2fc02..08aac2041ee 100644 --- a/dom/media/gtest/TestIntervalSet.cpp +++ b/dom/media/gtest/TestIntervalSet.cpp @@ -709,3 +709,12 @@ TEST(IntervalSet, FooIntervalSet) EXPECT_EQ(Foo(), is[0].mStart); EXPECT_EQ(Foo(4,5,6), is[0].mEnd); } + +TEST(IntervalSet, StaticAssert) +{ + typedef media::IntervalSet IntIntervals; + media::Interval i; + + static_assert(mozilla::IsSame::Type, nsTArray_CopyWithConstructors>::value, "Must use copy constructor"); + static_assert(mozilla::IsSame::Type, nsTArray_CopyWithConstructors>::value, "Must use copy constructor"); +} From 7801d659451b8b01b29cba1bda8ce6331cf6bbea Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 024/107] Bug 1163227: Part1. Notify parent decoder that data got removed. r=mattwoodrow --- dom/media/mediasource/TrackBuffer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index 1138a1782ce..2b49e57253c 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -429,6 +429,10 @@ TrackBuffer::EvictData(double aPlaybackTime, } } + if (evicted) { + mParentDecoder->NotifyTimeRangesChanged(); + } + return evicted; } @@ -493,6 +497,7 @@ TrackBuffer::EvictBefore(double aTime) mInitializedDecoders[i]->GetResource()->EvictBefore(endOffset); } } + mParentDecoder->NotifyTimeRangesChanged(); } media::TimeIntervals @@ -1104,6 +1109,7 @@ TrackBuffer::RangeRemoval(media::TimeUnit aStart, RemoveEmptyDecoders(decoders); + mParentDecoder->NotifyTimeRangesChanged(); return true; } From da404110e2a7de7143117c4cf1b9b833422ec888 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 025/107] Bug 1163227: Part2. Add MediaDecoderReader::NotifyDataRemoved method. r=cpearce --- dom/media/MediaDataDemuxer.h | 6 ++++++ dom/media/MediaDecoderReader.h | 2 ++ dom/media/MediaFormatReader.cpp | 27 ++++++++++++++++++++++++++- dom/media/MediaFormatReader.h | 1 + dom/media/fmp4/MP4Demuxer.cpp | 8 ++++++++ dom/media/fmp4/MP4Demuxer.h | 2 ++ 6 files changed, 45 insertions(+), 1 deletion(-) diff --git a/dom/media/MediaDataDemuxer.h b/dom/media/MediaDataDemuxer.h index e91d12b4116..9a966802e84 100644 --- a/dom/media/MediaDataDemuxer.h +++ b/dom/media/MediaDataDemuxer.h @@ -85,6 +85,12 @@ public: // data is available. virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset) { } + // Notifies the demuxer that the underlying resource has had data removed. + // The demuxer can use this mechanism to inform all track demuxers to update + // its TimeIntervals. + // This will be called should the demuxer be used with MediaSource. + virtual void NotifyDataRemoved() { } + protected: virtual ~MediaDataDemuxer() { diff --git a/dom/media/MediaDecoderReader.h b/dom/media/MediaDecoderReader.h index 510bd0ec64b..cfce796c871 100644 --- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -247,6 +247,8 @@ public: // Only used by WebMReader and MediaOmxReader for now, so stub here rather // than in every reader than inherits from MediaDecoderReader. virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {} + // Notify the reader that data from the resource was evicted (MediaSource only) + virtual void NotifyDataRemoved() {} virtual int64_t GetEvictionOffset(double aTime) { return -1; } virtual MediaQueue& AudioQueue() { return mAudioQueue; } diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index d48d18f0088..a1f9c3d37be 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -1334,7 +1334,11 @@ MediaFormatReader::NotifyDemuxer(uint32_t aLength, int64_t aOffset) return; } - mDemuxer->NotifyDataArrived(aLength, aOffset); + if (aLength || aOffset) { + mDemuxer->NotifyDataArrived(aLength, aOffset); + } else { + mDemuxer->NotifyDataRemoved(); + } if (HasVideo()) { mVideo.mReceivedNewData = true; ScheduleUpdate(TrackType::kVideoTrack); @@ -1355,6 +1359,7 @@ MediaFormatReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int6 } MOZ_ASSERT(mMainThreadDemuxer); + MOZ_ASSERT(aBuffer || aLength); mMainThreadDemuxer->NotifyDataArrived(aLength, aOffset); // Queue a task to notify our main demuxer. @@ -1365,4 +1370,24 @@ MediaFormatReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int6 GetTaskQueue()->Dispatch(task.forget()); } +void +MediaFormatReader::NotifyDataRemoved() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mInitDone) { + return; + } + + MOZ_ASSERT(mMainThreadDemuxer); + mMainThreadDemuxer->NotifyDataRemoved(); + + // Queue a task to notify our main demuxer. + RefPtr task = + NS_NewRunnableMethodWithArgs( + this, &MediaFormatReader::NotifyDemuxer, + 0, 0); + GetTaskQueue()->Dispatch(task.forget()); +} + } // namespace mozilla diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 9895c7be788..6a6680dcc06 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -76,6 +76,7 @@ public: virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) override; + virtual void NotifyDataRemoved() override; virtual media::TimeIntervals GetBuffered() override; diff --git a/dom/media/fmp4/MP4Demuxer.cpp b/dom/media/fmp4/MP4Demuxer.cpp index 601f746c63c..ce0c1016094 100644 --- a/dom/media/fmp4/MP4Demuxer.cpp +++ b/dom/media/fmp4/MP4Demuxer.cpp @@ -114,6 +114,14 @@ MP4Demuxer::NotifyDataArrived(uint32_t aLength, int64_t aOffset) } } +void +MP4Demuxer::NotifyDataRemoved() +{ + for (uint32_t i = 0; i < mDemuxers.Length(); i++) { + mDemuxers[i]->NotifyDataArrived(); + } +} + UniquePtr MP4Demuxer::GetCrypto() { diff --git a/dom/media/fmp4/MP4Demuxer.h b/dom/media/fmp4/MP4Demuxer.h index 48b2a6bb24a..6e98b7f50d8 100644 --- a/dom/media/fmp4/MP4Demuxer.h +++ b/dom/media/fmp4/MP4Demuxer.h @@ -45,6 +45,8 @@ public: virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset) override; + virtual void NotifyDataRemoved() override; + private: friend class MP4TrackDemuxer; nsRefPtr mResource; From 7186ab86c18afd7f7c5718e92b229dd5382818eb Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 026/107] Bug 1163227: Part3. Notify mediasource sub-readers that data was evicted. r=mattwoodrow --- dom/media/mediasource/TrackBuffer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index 2b49e57253c..a49528850b0 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -347,6 +347,7 @@ TrackBuffer::EvictData(double aPlaybackTime, playbackOffset); } } + decoders[i]->GetReader()->NotifyDataRemoved(); } } @@ -368,6 +369,7 @@ TrackBuffer::EvictData(double aPlaybackTime, buffered.GetStart().ToSeconds(), buffered.GetEnd().ToSeconds(), aPlaybackTime, decoders[i]->GetResource()->GetSize()); toEvict -= decoders[i]->GetResource()->EvictAll(); + decoders[i]->GetReader()->NotifyDataRemoved(); } // Evict all data from future decoders, starting furthest away from @@ -413,6 +415,7 @@ TrackBuffer::EvictData(double aPlaybackTime, buffered.GetStart().ToSeconds(), buffered.GetEnd().ToSeconds(), aPlaybackTime, decoders[i]->GetResource()->GetSize()); toEvict -= decoders[i]->GetResource()->EvictAll(); + decoders[i]->GetReader()->NotifyDataRemoved(); } RemoveEmptyDecoders(decoders); @@ -495,6 +498,7 @@ TrackBuffer::EvictBefore(double aTime) MSE_DEBUG("decoder=%u offset=%lld", i, endOffset); mInitializedDecoders[i]->GetResource()->EvictBefore(endOffset); + mInitializedDecoders[i]->GetReader()->NotifyDataRemoved(); } } mParentDecoder->NotifyTimeRangesChanged(); @@ -1094,6 +1098,7 @@ TrackBuffer::RangeRemoval(media::TimeUnit aStart, decoders[i]->GetResource()->EvictData(offset, offset); } } + decoders[i]->GetReader()->NotifyDataRemoved(); } } else { // Only trimming existing buffers. @@ -1104,6 +1109,7 @@ TrackBuffer::RangeRemoval(media::TimeUnit aStart, } else { decoders[i]->Trim(aStart.ToMicroseconds()); } + decoders[i]->GetReader()->NotifyDataRemoved(); } } From f7e131b6e07125636629bfa757820cc2b17f7ef2 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 027/107] Bug 1163227: Part4. Fix MP4TrackDemuxer eviction offset calculations. r=mattwoodrow --- dom/media/fmp4/MP4Demuxer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dom/media/fmp4/MP4Demuxer.cpp b/dom/media/fmp4/MP4Demuxer.cpp index ce0c1016094..001c5ba4809 100644 --- a/dom/media/fmp4/MP4Demuxer.cpp +++ b/dom/media/fmp4/MP4Demuxer.cpp @@ -298,7 +298,8 @@ int64_t MP4TrackDemuxer::GetEvictionOffset(media::TimeUnit aTime) { MonitorAutoLock mon(mMonitor); - return int64_t(mIndex->GetEvictionOffset(aTime.ToMicroseconds())); + uint64_t offset = mIndex->GetEvictionOffset(aTime.ToMicroseconds()); + return int64_t(offset == std::numeric_limits::max() ? 0 : offset); } media::TimeIntervals From c2737df50a39bbe0215a1835d0e97d4a825dac8b Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 028/107] Bug 1166836: Part1. Cache main thread buffered time range. r=cpearce GetBuffered() can be particularly slow under some circumstances. --- dom/media/MediaFormatReader.cpp | 45 ++++++++++++++++++++++++++++++--- dom/media/MediaFormatReader.h | 10 +++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index a1f9c3d37be..9519cefadc0 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -69,6 +69,7 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, , mInitDone(false) , mSeekable(false) , mIsEncrypted(false) + , mCachedTimeRangesStale(true) #if defined(READER_DORMANT_HEURISTIC) , mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false)) #endif @@ -1251,14 +1252,43 @@ MediaFormatReader::GetBuffered() media::TimeIntervals videoti; media::TimeIntervals audioti; + if (!mInitDone) { + return media::TimeIntervals(); + } if (NS_IsMainThread()) { + if (!mCachedTimeRangesStale) { + return mCachedTimeRanges; + } + MOZ_ASSERT(mMainThreadDemuxer); + if (!mDataRange.IsEmpty()) { + mMainThreadDemuxer->NotifyDataArrived(mDataRange.Length(), mDataRange.mStart); + } if (mVideoTrackDemuxer) { videoti = mVideoTrackDemuxer->GetBuffered(); } if (mAudioTrackDemuxer) { audioti = mAudioTrackDemuxer->GetBuffered(); } + if (HasAudio() && HasVideo()) { + mCachedTimeRanges = Intersection(Move(videoti), Move(audioti)); + } else if (HasAudio()) { + mCachedTimeRanges = Move(audioti); + } else if (HasVideo()) { + mCachedTimeRanges = Move(videoti); + } + mDataRange = ByteInterval(); + mCachedTimeRangesStale = false; + return mCachedTimeRanges; } else { + if (OnTaskQueue()) { + // Ensure we have up to date buffered time range. + if (HasVideo()) { + UpdateReceivedNewData(TrackType::kVideoTrack); + } + if (HasAudio()) { + UpdateReceivedNewData(TrackType::kAudioTrack); + } + } if (HasVideo()) { MonitorAutoLock lock(mVideo.mMonitor); videoti = mVideo.mTimeRanges; @@ -1354,14 +1384,18 @@ MediaFormatReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int6 { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aBuffer || aLength); + if (mDataRange.IsEmpty()) { + mDataRange = ByteInterval(aOffset, aOffset + aLength); + } else { + mDataRange = mDataRange.Span(ByteInterval(aOffset, aOffset + aLength)); + } + mCachedTimeRangesStale = true; + if (!mInitDone) { return; } - MOZ_ASSERT(mMainThreadDemuxer); - MOZ_ASSERT(aBuffer || aLength); - mMainThreadDemuxer->NotifyDataArrived(aLength, aOffset); - // Queue a task to notify our main demuxer. RefPtr task = NS_NewRunnableMethodWithArgs( @@ -1375,6 +1409,9 @@ MediaFormatReader::NotifyDataRemoved() { MOZ_ASSERT(NS_IsMainThread()); + mDataRange = ByteInterval(); + mCachedTimeRangesStale = true; + if (!mInitDone) { return; } diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 6a6680dcc06..66ec550eefc 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -26,6 +26,7 @@ namespace mozilla { class MediaFormatReader final : public MediaDecoderReader { typedef TrackInfo::TrackType TrackType; + typedef media::Interval ByteInterval; public: explicit MediaFormatReader(AbstractMediaDecoder* aDecoder, @@ -104,6 +105,9 @@ public: private: bool InitDemuxer(); + // Notify the demuxer that new data has been received. + // The next queued task calling GetBuffered() is guaranteed to have up to date + // buffered ranges. void NotifyDemuxer(uint32_t aLength, int64_t aOffset); void ReturnOutput(MediaData* aData, TrackType aTrack); @@ -305,7 +309,6 @@ private: DecoderDataWithPromise mVideo; // Returns true when the decoder for this track needs input. - // aDecoder.mMonitor must be locked. bool NeedInput(DecoderData& aDecoder); DecoderData& GetDecoderData(TrackType aTrack); @@ -390,9 +393,14 @@ private: nsRefPtr mSharedDecoderManager; // Main thread objects + // Those are only used to calculate our buffered range on the main thread. + // The cached buffered range is calculated one when required. nsRefPtr mMainThreadDemuxer; nsRefPtr mAudioTrackDemuxer; nsRefPtr mVideoTrackDemuxer; + ByteInterval mDataRange; + media::TimeIntervals mCachedTimeRanges; + bool mCachedTimeRangesStale; #if defined(READER_DORMANT_HEURISTIC) const bool mDormantEnabled; From 70c9952e4ac1190012d1185d7a2b74b9743b9b01 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 029/107] Bug 1166836: Part2. Only rescans MP4's moof when necessary. r=cpearce --- dom/media/fmp4/MP4Demuxer.cpp | 37 ++++++++++++++++++++--------------- dom/media/fmp4/MP4Demuxer.h | 2 ++ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/dom/media/fmp4/MP4Demuxer.cpp b/dom/media/fmp4/MP4Demuxer.cpp index 001c5ba4809..ada2c4c3d32 100644 --- a/dom/media/fmp4/MP4Demuxer.cpp +++ b/dom/media/fmp4/MP4Demuxer.cpp @@ -151,6 +151,7 @@ MP4TrackDemuxer::MP4TrackDemuxer(MP4Demuxer* aParent, uint32_t aTrackNumber) : mParent(aParent) , mStream(new mp4_demuxer::ResourceStream(mParent->mResource)) + , mNeedReIndex(true) , mMonitor("MP4TrackDemuxer") { mInfo = mParent->mMetadata->GetTrackInfo(aType, aTrackNumber); @@ -176,6 +177,23 @@ MP4TrackDemuxer::GetInfo() const return mInfo->Clone(); } +void +MP4TrackDemuxer::EnsureUpToDateIndex() +{ + if (!mNeedReIndex) { + return; + } + AutoPinned resource(mParent->mResource); + nsTArray byteRanges; + nsresult rv = resource->GetCachedRanges(byteRanges); + if (NS_FAILED(rv)) { + return; + } + MonitorAutoLock mon(mMonitor); + mIndex->UpdateMoofIndex(byteRanges); + mNeedReIndex = false; +} + nsRefPtr MP4TrackDemuxer::Seek(media::TimeUnit aTime) { @@ -305,6 +323,7 @@ MP4TrackDemuxer::GetEvictionOffset(media::TimeUnit aTime) media::TimeIntervals MP4TrackDemuxer::GetBuffered() { + EnsureUpToDateIndex(); AutoPinned resource(mParent->mResource); nsTArray byteRanges; nsresult rv = resource->GetCachedRanges(byteRanges); @@ -315,16 +334,9 @@ MP4TrackDemuxer::GetBuffered() nsTArray> timeRanges; MonitorAutoLock mon(mMonitor); - int64_t endComposition = - mIndex->GetEndCompositionIfBuffered(byteRanges); - mIndex->ConvertByteRangesToTimeRanges(byteRanges, &timeRanges); - if (endComposition) { - mp4_demuxer::Interval::SemiNormalAppend( - timeRanges, mp4_demuxer::Interval(endComposition, endComposition)); - } // convert timeRanges. - media::TimeIntervals ranges; + media::TimeIntervals ranges = media::TimeIntervals(); for (size_t i = 0; i < timeRanges.Length(); i++) { ranges += media::TimeInterval(media::TimeUnit::FromMicroseconds(timeRanges[i].start), @@ -336,14 +348,7 @@ MP4TrackDemuxer::GetBuffered() void MP4TrackDemuxer::NotifyDataArrived() { - AutoPinned resource(mParent->mResource); - nsTArray byteRanges; - nsresult rv = resource->GetCachedRanges(byteRanges); - if (NS_FAILED(rv)) { - return; - } - MonitorAutoLock mon(mMonitor); - mIndex->UpdateMoofIndex(byteRanges); + mNeedReIndex = true; } void diff --git a/dom/media/fmp4/MP4Demuxer.h b/dom/media/fmp4/MP4Demuxer.h index 6e98b7f50d8..dd8618f9d4e 100644 --- a/dom/media/fmp4/MP4Demuxer.h +++ b/dom/media/fmp4/MP4Demuxer.h @@ -85,6 +85,7 @@ private: friend class MP4Demuxer; void NotifyDataArrived(); void UpdateSamples(nsTArray>& aSamples); + void EnsureUpToDateIndex(); nsRefPtr mParent; nsRefPtr mIndex; UniquePtr mIterator; @@ -93,6 +94,7 @@ private: Maybe mNextKeyframeTime; // Queued samples extracted by the demuxer, but not yet returned. nsRefPtr mQueuedSample; + bool mNeedReIndex; // We do not actually need a monitor, however MoofParser will assert // if a monitor isn't held. From b37bb7571f24f10f90b963515940c1049f6dfd53 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 030/107] Bug 1166836: Part3. Optimise most common addition to IntervalSet. r=mattwoodrow --- dom/media/Intervals.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/dom/media/Intervals.h b/dom/media/Intervals.h index 8655b824578..65fb80da032 100644 --- a/dom/media/Intervals.h +++ b/dom/media/Intervals.h @@ -212,6 +212,15 @@ public: mFuzz = aFuzz; } + // Returns true if the two intervals intersect with this being on the right + // of aOther + bool TouchesOnRight(const SelfType& aOther) const + { + return aOther.mStart <= mStart && + (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) && + (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz); + } + T mStart; T mEnd; T mFuzz; @@ -305,10 +314,14 @@ public: mIntervals.AppendElement(aInterval); return *this; } + ElemType& last = mIntervals.LastElement(); + if (aInterval.TouchesOnRight(last)) { + last = last.Span(aInterval); + return *this; + } // Most of our actual usage is adding an interval that will be outside the // range. We can speed up normalization here. - if (aInterval.RightOf(mIntervals.LastElement()) && - !aInterval.Touches(mIntervals.LastElement())) { + if (aInterval.RightOf(last)) { mIntervals.AppendElement(aInterval); return *this; } From 2b44f4a3e52c59cb5e34a8bf9605acdaacd0b9b6 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 031/107] Bug 1163227: Part5: Increase verbosity content for debugging purposes. r=cpearce Small reorganisation of Update(). With bug 1153295 fixed, remove need for StorensRefPtrPassByPtr. --- dom/media/MediaFormatReader.cpp | 65 +++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 9519cefadc0..21c061e31d7 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -657,6 +657,8 @@ void MediaFormatReader::NotifyNewOutput(TrackType aTrack, MediaData* aSample) { MOZ_ASSERT(OnTaskQueue()); + LOGV("Received new sample time:%lld duration:%lld", + aSample->mTime, aSample->mDuration); auto& decoder = GetDecoderData(aTrack); if (!decoder.mOutputRequested) { LOG("MediaFormatReader produced output while flushing, discarding."); @@ -671,6 +673,7 @@ void MediaFormatReader::NotifyInputExhausted(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); + LOGV("Decoder has requested more %s data", TrackTypeToStr(aTrack)); auto& decoder = GetDecoderData(aTrack); decoder.mInputExhausted = true; ScheduleUpdate(aTrack); @@ -693,6 +696,7 @@ void MediaFormatReader::NotifyError(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); + LOGV("%s Decoding error", TrackTypeToStr(aTrack)); auto& decoder = GetDecoderData(aTrack); decoder.mError = true; ScheduleUpdate(aTrack); @@ -719,6 +723,7 @@ MediaFormatReader::NeedInput(DecoderData& aDecoder) return !aDecoder.mError && aDecoder.HasPromise() && + !aDecoder.mDemuxRequest.Exists() && aDecoder.mOutput.IsEmpty() && (aDecoder.mInputExhausted || !aDecoder.mQueuedSamples.IsEmpty() || aDecoder.mNumSamplesInput - aDecoder.mNumSamplesOutput < aDecoder.mDecodeAhead); @@ -767,13 +772,16 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack) decoder.mDemuxEOSServiced = false; } - if (!decoder.mError && decoder.HasWaitingPromise()) { + if (decoder.mError) { + return false; + } + if (decoder.HasWaitingPromise()) { MOZ_ASSERT(!decoder.HasPromise()); LOG("We have new data. Resolving WaitingPromise"); decoder.mWaitingPromise.Resolve(decoder.mType, __func__); return true; } - if (!decoder.mError && !mSeekPromise.IsEmpty()) { + if (!mSeekPromise.IsEmpty()) { MOZ_ASSERT(!decoder.HasPromise()); if (mVideoSeekRequest.Exists() || mAudioSeekRequest.Exists()) { // Already waiting for a seek to complete. Nothing more to do. @@ -791,20 +799,19 @@ MediaFormatReader::RequestDemuxSamples(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); auto& decoder = GetDecoderData(aTrack); + MOZ_ASSERT(!decoder.mDemuxRequest.Exists()); if (!decoder.mQueuedSamples.IsEmpty()) { // No need to demux new samples. return; } - if (decoder.mDemuxRequest.Exists()) { - // We are already in the process of demuxing. - return; - } if (decoder.mDemuxEOS) { // Nothing left to demux. return; } + + LOGV("Requesting extra demux %s", TrackTypeToStr(aTrack)); if (aTrack == TrackInfo::kVideoTrack) { DoDemuxVideo(); } else { @@ -822,7 +829,7 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack, if (decoder.mQueuedSamples.IsEmpty()) { return; } - + LOGV("Giving %s input to decoder", TrackTypeToStr(aTrack)); decoder.mOutputRequested = true; // Decode all our demuxed frames. @@ -846,9 +853,7 @@ MediaFormatReader::Update(TrackType aTrack) return; } - // Record number of frames decoded and parsed. Automatically update the - // stats counters using the AutoNotifyDecoded stack-based class. - AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder); + LOGV("Processing update for %s", TrackTypeToStr(aTrack)); bool needInput = false; bool needOutput = false; @@ -856,6 +861,7 @@ MediaFormatReader::Update(TrackType aTrack) decoder.mUpdateScheduled = false; if (UpdateReceivedNewData(aTrack)) { + LOGV("Nothing more to do"); return; } @@ -872,13 +878,14 @@ MediaFormatReader::Update(TrackType aTrack) } } else if (decoder.mWaitingForData) { // Nothing more we can do at present. + LOGV("Still waiting for data."); return; } - if (NeedInput(decoder)) { - needInput = true; - decoder.mInputExhausted = false; - } + // Record number of frames decoded and parsed. Automatically update the + // stats counters using the AutoNotifyDecoded stack-based class. + AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder); + if (aTrack == TrackInfo::kVideoTrack) { uint64_t delta = decoder.mNumSamplesOutput - mLastReportedNumDecodedFrames; @@ -899,18 +906,27 @@ MediaFormatReader::Update(TrackType aTrack) } } - LOGV("Update(%s) ni=%d no=%d", TrackTypeToStr(aTrack), needInput, needOutput); - if (decoder.mDemuxEOS && !decoder.mDemuxEOSServiced) { decoder.mOutputRequested = true; decoder.mDecoder->Drain(); decoder.mDemuxEOSServiced = true; - } - - if (!needInput) { + LOGV("Requesting decoder to drain"); return; } + if (!NeedInput(decoder)) { + LOGV("No need for additional input"); + return; + } + + needInput = true; + decoder.mInputExhausted = false; + + LOGV("Update(%s) ni=%d no=%d ie=%d, in:%d out:%d qs=%d", + TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted, + decoder.mNumSamplesInput, decoder.mNumSamplesOutput, + size_t(decoder.mSizeOfQueue)); + // Demux samples if we don't have some. RequestDemuxSamples(aTrack); // Decode all pending demuxed samples. @@ -923,6 +939,7 @@ MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack) auto& decoder = GetDecoderData(aTrack); MOZ_ASSERT(decoder.HasPromise()); if (decoder.mDiscontinuity) { + LOGV("Setting discontinuity flag"); decoder.mDiscontinuity = false; aData->mDiscontinuity = true; } @@ -942,6 +959,7 @@ MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack) } else if (aTrack == TrackInfo::kVideoTrack) { mVideo.mPromise.Resolve(static_cast(aData), __func__); } + LOG("Resolved data promise for %s", TrackTypeToStr(aTrack)); } size_t @@ -980,6 +998,7 @@ MediaFormatReader::ResetDecode() { MOZ_ASSERT(OnTaskQueue()); + LOGV(""); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); mAudioSeekRequest.DisconnectIfExists(); @@ -1016,7 +1035,7 @@ MediaFormatReader::Output(TrackType aTrack, MediaData* aSample) } RefPtr task = - NS_NewRunnableMethodWithArgs>( + NS_NewRunnableMethodWithArgs( this, &MediaFormatReader::NotifyNewOutput, aTrack, aSample); GetTaskQueue()->Dispatch(task.forget()); } @@ -1168,6 +1187,7 @@ void MediaFormatReader::OnSeekFailed(TrackType aTrack, DemuxerFailureReason aResult) { MOZ_ASSERT(OnTaskQueue()); + LOGV("%s failure = %d", TrackTypeToStr(aTrack), aResult); if (aTrack == TrackType::kVideoTrack) { mVideoSeekRequest.Complete(); } else { @@ -1187,6 +1207,7 @@ void MediaFormatReader::DoVideoSeek() { MOZ_ASSERT(mPendingSeekTime.isSome()); + LOGV("Seeking video to %lld", mPendingSeekTime.ref().ToMicroseconds()); media::TimeUnit seekTime = mPendingSeekTime.ref(); mVideoSeekRequest.Begin(mVideo.mTrackDemuxer->Seek(seekTime) ->RefableThen(GetTaskQueue(), __func__, this, @@ -1198,6 +1219,7 @@ void MediaFormatReader::OnVideoSeekCompleted(media::TimeUnit aTime) { MOZ_ASSERT(OnTaskQueue()); + LOGV("Video seeked to %lld", aTime.ToMicroseconds()); mVideoSeekRequest.Complete(); if (HasAudio()) { @@ -1214,6 +1236,7 @@ void MediaFormatReader::DoAudioSeek() { MOZ_ASSERT(mPendingSeekTime.isSome()); + LOGV("Seeking audio to %lld", mPendingSeekTime.ref().ToMicroseconds()); media::TimeUnit seekTime = mPendingSeekTime.ref(); mAudioSeekRequest.Begin(mAudio.mTrackDemuxer->Seek(seekTime) ->RefableThen(GetTaskQueue(), __func__, this, @@ -1225,6 +1248,7 @@ void MediaFormatReader::OnAudioSeekCompleted(media::TimeUnit aTime) { MOZ_ASSERT(OnTaskQueue()); + LOGV("Audio seeked to %lld", aTime.ToMicroseconds()); mAudioSeekRequest.Complete(); mPendingSeekTime.reset(); mSeekPromise.Resolve(aTime.ToMicroseconds(), __func__); @@ -1360,6 +1384,7 @@ MediaFormatReader::NotifyDemuxer(uint32_t aLength, int64_t aOffset) { MOZ_ASSERT(OnTaskQueue()); + LOGV("aLength=%u, aOffset=%lld", aLength, aOffset); if (mShutdown) { return; } From 04ba19f4883908f17f511d977deeea965f97b974 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 032/107] Bug 1163227: Part6. Always notify MediaSourceReader of new data on its own task queue. r=mattwoodrow --- dom/media/mediasource/TrackBuffer.cpp | 23 ++++++++++++++++------- dom/media/mediasource/TrackBuffer.h | 2 ++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index a49528850b0..384c70d6d2a 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -241,7 +241,7 @@ TrackBuffer::AppendData(MediaLargeByteBuffer* aData, int64_t aTimestampOffset) // Tell our reader that we have more data to ensure that playback starts if // required when data is appended. - mParentDecoder->GetReader()->MaybeNotifyHaveData(); + NotifyTimeRangesChanged(); mInitializationPromise.Resolve(gotMedia, __func__); return p; @@ -263,11 +263,20 @@ TrackBuffer::AppendDataToCurrentResource(MediaLargeByteBuffer* aData, uint32_t a mCurrentDecoder->NotifyDataArrived(reinterpret_cast(aData->Elements()), aData->Length(), appendOffset); mParentDecoder->NotifyBytesDownloaded(); - mParentDecoder->NotifyTimeRangesChanged(); + NotifyTimeRangesChanged(); return true; } +void +TrackBuffer::NotifyTimeRangesChanged() +{ + RefPtr task = + NS_NewRunnableMethod(mParentDecoder->GetReader(), + &MediaSourceReader::NotifyTimeRangesChanged); + mParentDecoder->GetReader()->GetTaskQueue()->Dispatch(task.forget()); +} + class DecoderSorter { public: @@ -433,7 +442,7 @@ TrackBuffer::EvictData(double aPlaybackTime, } if (evicted) { - mParentDecoder->NotifyTimeRangesChanged(); + NotifyTimeRangesChanged(); } return evicted; @@ -501,7 +510,7 @@ TrackBuffer::EvictBefore(double aTime) mInitializedDecoders[i]->GetReader()->NotifyDataRemoved(); } } - mParentDecoder->NotifyTimeRangesChanged(); + NotifyTimeRangesChanged(); } media::TimeIntervals @@ -797,7 +806,7 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder) // Tell our reader that we have more data to ensure that playback starts if // required when data is appended. - mParentDecoder->GetReader()->MaybeNotifyHaveData(); + NotifyTimeRangesChanged(); MSE_DEBUG("Reader %p activated", aDecoder->GetReader()); @@ -839,7 +848,7 @@ TrackBuffer::RegisterDecoder(SourceBufferDecoder* aDecoder) return false; } mInitializedDecoders.AppendElement(aDecoder); - mParentDecoder->NotifyTimeRangesChanged(); + NotifyTimeRangesChanged(); return true; } @@ -1115,7 +1124,7 @@ TrackBuffer::RangeRemoval(media::TimeUnit aStart, RemoveEmptyDecoders(decoders); - mParentDecoder->NotifyTimeRangesChanged(); + NotifyTimeRangesChanged(); return true; } diff --git a/dom/media/mediasource/TrackBuffer.h b/dom/media/mediasource/TrackBuffer.h index 14358a84a19..2a0c6a46d8c 100644 --- a/dom/media/mediasource/TrackBuffer.h +++ b/dom/media/mediasource/TrackBuffer.h @@ -129,6 +129,8 @@ private: // data is appended to the current decoder's SourceBufferResource. bool AppendDataToCurrentResource(MediaLargeByteBuffer* aData, uint32_t aDuration /* microseconds */); + // Queue on the parent's decoder task queue a call to NotifyTimeRangesChanged. + void NotifyTimeRangesChanged(); // Queue execution of InitializeDecoder on mTaskQueue. bool QueueInitializeDecoder(SourceBufferDecoder* aDecoder); From 4dc728204b1ff23db624ba3140e03bc1c6876f62 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 033/107] Bug 1163227: Part7. Never do blocking read if we don't have data. r=kentuckyfriedtakahe --- media/libstagefright/binding/Index.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/media/libstagefright/binding/Index.cpp b/media/libstagefright/binding/Index.cpp index ec14b6d027a..cb66baec61c 100644 --- a/media/libstagefright/binding/Index.cpp +++ b/media/libstagefright/binding/Index.cpp @@ -91,6 +91,13 @@ already_AddRefed SampleIterator::GetNext() return nullptr; } + int64_t length = std::numeric_limits::max(); + mIndex->mSource->Length(&length); + if (s->mByteRange.mEnd > length) { + // We don't have this complete sample. + return nullptr; + } + nsRefPtr sample = new MediaRawData(); sample->mTimecode= s->mDecodeTime; sample->mTime = s->mCompositionRange.start; From f7a6aa17d038181d1a0692e1668a08c111c07fcb Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 034/107] Bug 1163227: Part8. Fix incorrect seconds / microseconds conversion. r=mattwoodrow This led to incorrect data eviction in sourcebuffer --- dom/media/mediasource/TrackBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index 384c70d6d2a..659618626d0 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -346,7 +346,7 @@ TrackBuffer::EvictData(double aPlaybackTime, toEvict -= decoders[i]->GetResource()->EvictAll(); } else { int64_t playbackOffset = - decoders[i]->ConvertToByteOffset(time.ToMicroseconds()); + decoders[i]->ConvertToByteOffset(time.ToSeconds()); MSE_DEBUG("evicting some bufferedEnd=%f " "aPlaybackTime=%f time=%f, playbackOffset=%lld size=%lld", buffered.GetEnd().ToSeconds(), aPlaybackTime, time, From 076004e0b2191e92adb159c77184aefbbf2d9193 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 035/107] Bug 1163227: Part9. Don't reset demuxer when skipping to next keyframe. r=cpearce --- dom/media/MediaFormatReader.cpp | 8 +++++--- dom/media/MediaFormatReader.h | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 21c061e31d7..b9363c568a8 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -108,6 +108,7 @@ MediaFormatReader::Shutdown() mAudio.mDecoder = nullptr; } if (mAudio.mTrackDemuxer) { + mAudio.ResetDemuxer(); mAudio.mTrackDemuxer->BreakCycles(); mAudio.mTrackDemuxer = nullptr; } @@ -124,6 +125,7 @@ MediaFormatReader::Shutdown() mVideo.mDecoder = nullptr; } if (mVideo.mTrackDemuxer) { + mVideo.ResetDemuxer(); mVideo.mTrackDemuxer->BreakCycles(); mVideo.mTrackDemuxer = nullptr; } @@ -510,6 +512,7 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe, MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise(), "No duplicate sample requests"); MOZ_DIAGNOSTIC_ASSERT(!mVideoSeekRequest.Exists()); MOZ_DIAGNOSTIC_ASSERT(!mAudioSeekRequest.Exists()); + MOZ_DIAGNOSTIC_ASSERT(!mSkipRequest.Exists(), "called mid-skipping"); MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek"); LOGV("RequestVideoData(%d, %lld)", aSkipToNextKeyframe, aTimeThreshold); @@ -1014,9 +1017,11 @@ MediaFormatReader::ResetDecode() mPendingSeekTime.reset(); if (HasVideo()) { + mVideo.ResetDemuxer(); Flush(TrackInfo::kVideoTrack); } if (HasAudio()) { + mAudio.ResetDemuxer(); Flush(TrackInfo::kAudioTrack); } return MediaDecoderReader::ResetDecode(); @@ -1078,9 +1083,6 @@ MediaFormatReader::Flush(TrackType aTrack) return; } - // Clear demuxer related data. - decoder.mDemuxRequest.DisconnectIfExists(); - decoder.mTrackDemuxer->Reset(); decoder.mDecoder->Flush(); // Purge the current decoder's state. // ResetState clears mOutputRequested flag so that we ignore all output until diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 66ec550eefc..49d3faf8c9e 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -256,6 +256,13 @@ private: virtual void RejectPromise(MediaDecoderReader::NotDecodedReason aReason, const char* aMethodName) = 0; + void ResetDemuxer() + { + // Clear demuxer related data. + mDemuxRequest.DisconnectIfExists(); + mTrackDemuxer->Reset(); + } + void ResetState() { MOZ_ASSERT(mOwner->OnTaskQueue()); From de9065833d1301c931cc6d86dd7d777b97a3cc63 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 15:09:16 +1000 Subject: [PATCH 036/107] Bug 1163227: Part10. Properly recalculate next keyframe time after seeking. r=cpearce --- dom/media/fmp4/MP4Demuxer.cpp | 21 +++++++++++++++------ dom/media/fmp4/MP4Demuxer.h | 1 + 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/dom/media/fmp4/MP4Demuxer.cpp b/dom/media/fmp4/MP4Demuxer.cpp index ada2c4c3d32..db719d999b3 100644 --- a/dom/media/fmp4/MP4Demuxer.cpp +++ b/dom/media/fmp4/MP4Demuxer.cpp @@ -208,6 +208,7 @@ MP4TrackDemuxer::Seek(media::TimeUnit aTime) if (mQueuedSample) { seekTime = mQueuedSample->mTime; } + SetNextKeyFrameTime(); return SeekPromise::CreateAndResolve(media::TimeUnit::FromMicroseconds(seekTime), __func__); } @@ -240,6 +241,17 @@ MP4TrackDemuxer::GetSamples(int32_t aNumSamples) } } +void +MP4TrackDemuxer::SetNextKeyFrameTime() +{ + mNextKeyframeTime.reset(); + mp4_demuxer::Microseconds frameTime = mIterator->GetNextKeyframeTime(); + if (frameTime != -1) { + mNextKeyframeTime.emplace( + media::TimeUnit::FromMicroseconds(frameTime)); + } +} + void MP4TrackDemuxer::Reset() { @@ -247,6 +259,7 @@ MP4TrackDemuxer::Reset() // TODO, Seek to first frame available, which isn't always 0. MonitorAutoLock mon(mMonitor); mIterator->Seek(0); + SetNextKeyFrameTime(); } void @@ -266,12 +279,7 @@ MP4TrackDemuxer::UpdateSamples(nsTArray>& aSamples) } if (mNextKeyframeTime.isNothing() || aSamples.LastElement()->mTime >= mNextKeyframeTime.value().ToMicroseconds()) { - mNextKeyframeTime.reset(); - mp4_demuxer::Microseconds frameTime = mIterator->GetNextKeyframeTime(); - if (frameTime != -1) { - mNextKeyframeTime.emplace( - media::TimeUnit::FromMicroseconds(frameTime)); - } + SetNextKeyFrameTime(); } } @@ -304,6 +312,7 @@ MP4TrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold) mQueuedSample = sample; } } + SetNextKeyFrameTime(); if (found) { return SkipAccessPointPromise::CreateAndResolve(parsed, __func__); } else { diff --git a/dom/media/fmp4/MP4Demuxer.h b/dom/media/fmp4/MP4Demuxer.h index dd8618f9d4e..57bbd744444 100644 --- a/dom/media/fmp4/MP4Demuxer.h +++ b/dom/media/fmp4/MP4Demuxer.h @@ -86,6 +86,7 @@ private: void NotifyDataArrived(); void UpdateSamples(nsTArray>& aSamples); void EnsureUpToDateIndex(); + void SetNextKeyFrameTime(); nsRefPtr mParent; nsRefPtr mIndex; UniquePtr mIterator; From 0f158b319fdde1f2c4c2d79ce17753205e764d51 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 22 May 2015 15:23:00 +1200 Subject: [PATCH 037/107] bug 1162364 detect and abort MF_E_TRANSFORM_STREAM_CHANGE infinite loops r=cpearce --- dom/media/platforms/wmf/WMFAudioMFTManager.cpp | 6 ++++++ dom/media/platforms/wmf/WMFVideoMFTManager.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp index 64bfab608cf..9968a68fe69 100644 --- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp @@ -205,6 +205,7 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset, aOutData = nullptr; RefPtr sample; HRESULT hr; + int typeChangeCount = 0; while (true) { hr = mDecoder->Output(&sample); if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { @@ -213,6 +214,11 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset, if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { hr = UpdateOutputType(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + // Catch infinite loops, but some decoders perform at least 2 stream + // changes on consecutive calls, so be permissive. + // 100 is arbitrarily > 2. + NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE); + ++typeChangeCount; continue; } break; diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp index c345fa8bb4d..d50e0d7a749 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -482,6 +482,7 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset, RefPtr sample; HRESULT hr; aOutData = nullptr; + int typeChangeCount = 0; // Loop until we decode a sample, or an unexpected error that we can't // handle occurs. @@ -497,7 +498,12 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset, MOZ_ASSERT(!sample); hr = ConfigureVideoFrameGeometry(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + // Catch infinite loops, but some decoders perform at least 2 stream + // changes on consecutive calls, so be permissive. + // 100 is arbitrarily > 2. + NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE); // Loop back and try decoding again... + ++typeChangeCount; continue; } if (SUCCEEDED(hr)) { From 29634532f020db64c23cad7a77f01247122266c1 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Mon, 25 May 2015 08:52:30 +1200 Subject: [PATCH 038/107] bug 1166107 release internal drain monitor before calling Flush() r=gerald The DrainComplete() caught with mWaitForInternalDrain still won't necessarily be from the internal Drain(), but all we need is that one DrainComplete() is caught for the internal Drain() because one more will be generated if there is a Drain() in progress. What protecting mWaitForInternalDrain access with the monitor provides here is that the compiler won't use its address for storage of other data meaningless in the context of mWaitForInternalDrain and so, for example, two DrainComplete() calls won't unintentionally think that they are both for one internal drain. And TSan warnings. --- dom/media/platforms/SharedDecoderManager.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dom/media/platforms/SharedDecoderManager.cpp b/dom/media/platforms/SharedDecoderManager.cpp index 6203973a699..55c50680533 100644 --- a/dom/media/platforms/SharedDecoderManager.cpp +++ b/dom/media/platforms/SharedDecoderManager.cpp @@ -163,16 +163,15 @@ void SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy) { if (aProxy && mActiveProxy == aProxy) { - MonitorAutoLock mon(mMonitor); - mWaitForInternalDrain = true; - nsresult rv; { - // We don't want to hold the lock while calling Drain() has some + MonitorAutoLock mon(mMonitor); + mWaitForInternalDrain = true; + // We don't want to hold the lock while calling Drain() as some // platform implementations call DrainComplete() immediately. - MonitorAutoUnlock mon(mMonitor); - rv = mActiveProxy->Drain(); } + nsresult rv = mActiveProxy->Drain(); if (NS_SUCCEEDED(rv)) { + MonitorAutoLock mon(mMonitor); while (mWaitForInternalDrain) { mon.Wait(); } From 49b1bd6b85774dfa2107d4e1128126ef7308c409 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 22 May 2015 11:10:00 +1200 Subject: [PATCH 039/107] bug 1166107 documentation of mWaitForInternalDrain thread access r=gerald --- dom/media/platforms/SharedDecoderManager.cpp | 2 +- dom/media/platforms/SharedDecoderManager.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dom/media/platforms/SharedDecoderManager.cpp b/dom/media/platforms/SharedDecoderManager.cpp index 55c50680533..05c896a14b7 100644 --- a/dom/media/platforms/SharedDecoderManager.cpp +++ b/dom/media/platforms/SharedDecoderManager.cpp @@ -74,7 +74,7 @@ SharedDecoderManager::SharedDecoderManager() , mActiveProxy(nullptr) , mActiveCallback(nullptr) , mWaitForInternalDrain(false) - , mMonitor("SharedDecoderProxy") + , mMonitor("SharedDecoderManager") , mDecoderReleasedResources(false) { MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread. diff --git a/dom/media/platforms/SharedDecoderManager.h b/dom/media/platforms/SharedDecoderManager.h index c6b33d8fec5..7c283085662 100644 --- a/dom/media/platforms/SharedDecoderManager.h +++ b/dom/media/platforms/SharedDecoderManager.h @@ -56,6 +56,7 @@ private: SharedDecoderProxy* mActiveProxy; MediaDataDecoderCallback* mActiveCallback; nsAutoPtr mCallback; + // access protected by mMonitor bool mWaitForInternalDrain; Monitor mMonitor; bool mDecoderReleasedResources; From e847e83a18f754d3e29ca3337113fb8a9b5e3b54 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 19:27:21 +1000 Subject: [PATCH 040/107] Bug 1168004: Part1. Ensure buffered range referential starts at 0. r=bholley --- dom/media/Intervals.h | 3 +- dom/media/MediaFormatReader.cpp | 69 ++++++++++++++++++--------------- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/dom/media/Intervals.h b/dom/media/Intervals.h index 65fb80da032..a4d70b06b0c 100644 --- a/dom/media/Intervals.h +++ b/dom/media/Intervals.h @@ -537,12 +537,13 @@ public: } // Shift all values by aOffset. - void Shift(const T& aOffset) + SelfType& Shift(const T& aOffset) { for (auto& interval : mIntervals) { interval.mStart = interval.mStart + aOffset; interval.mEnd = interval.mEnd + aOffset; } + return *this; } void SetFuzz(const T& aFuzz) { diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index b9363c568a8..776c114c50d 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -1277,34 +1277,40 @@ MediaFormatReader::GetBuffered() { media::TimeIntervals videoti; media::TimeIntervals audioti; + media::TimeIntervals intervals; if (!mInitDone) { - return media::TimeIntervals(); + return intervals; + } + int64_t startTime; + { + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + MOZ_ASSERT(mStartTime != -1, "Need to finish metadata decode first"); + startTime = mStartTime; } if (NS_IsMainThread()) { - if (!mCachedTimeRangesStale) { - return mCachedTimeRanges; + if (mCachedTimeRangesStale) { + MOZ_ASSERT(mMainThreadDemuxer); + if (!mDataRange.IsEmpty()) { + mMainThreadDemuxer->NotifyDataArrived(mDataRange.Length(), mDataRange.mStart); + } + if (mVideoTrackDemuxer) { + videoti = mVideoTrackDemuxer->GetBuffered(); + } + if (mAudioTrackDemuxer) { + audioti = mAudioTrackDemuxer->GetBuffered(); + } + if (HasAudio() && HasVideo()) { + mCachedTimeRanges = media::Intersection(Move(videoti), Move(audioti)); + } else if (HasAudio()) { + mCachedTimeRanges = Move(audioti); + } else if (HasVideo()) { + mCachedTimeRanges = Move(videoti); + } + mDataRange = ByteInterval(); + mCachedTimeRangesStale = false; } - MOZ_ASSERT(mMainThreadDemuxer); - if (!mDataRange.IsEmpty()) { - mMainThreadDemuxer->NotifyDataArrived(mDataRange.Length(), mDataRange.mStart); - } - if (mVideoTrackDemuxer) { - videoti = mVideoTrackDemuxer->GetBuffered(); - } - if (mAudioTrackDemuxer) { - audioti = mAudioTrackDemuxer->GetBuffered(); - } - if (HasAudio() && HasVideo()) { - mCachedTimeRanges = Intersection(Move(videoti), Move(audioti)); - } else if (HasAudio()) { - mCachedTimeRanges = Move(audioti); - } else if (HasVideo()) { - mCachedTimeRanges = Move(videoti); - } - mDataRange = ByteInterval(); - mCachedTimeRangesStale = false; - return mCachedTimeRanges; + intervals = mCachedTimeRanges; } else { if (OnTaskQueue()) { // Ensure we have up to date buffered time range. @@ -1323,17 +1329,16 @@ MediaFormatReader::GetBuffered() MonitorAutoLock lock(mAudio.mMonitor); audioti = mAudio.mTimeRanges; } - } - if (HasAudio() && HasVideo()) { - videoti.Intersection(audioti); - return videoti; - } else if (HasAudio()) { - return audioti; - } else if (HasVideo()) { - return videoti; + if (HasAudio() && HasVideo()) { + intervals = media::Intersection(Move(videoti), Move(audioti)); + } else if (HasAudio()) { + intervals = Move(audioti); + } else if (HasVideo()) { + intervals = Move(videoti); + } } - return media::TimeIntervals(); + return intervals.Shift(media::TimeUnit::FromMicroseconds(-startTime)); } bool MediaFormatReader::IsDormantNeeded() From 885ef719d63f3479f18402b1434b0d9bb3b7e685 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 25 May 2015 19:28:02 +1000 Subject: [PATCH 041/107] Bug 1168004: Part2. Add sample file. r=bholley Add MP4 sample file where video starts at 0.8s and audio at 1.95s. --- dom/media/test/bipbop-lateaudio.mp4 | Bin 0 -> 70404 bytes dom/media/test/bipbop-lateaudio.mp4^headers^ | 1 + dom/media/test/manifest.js | 3 +++ dom/media/test/mochitest.ini | 2 ++ 4 files changed, 6 insertions(+) create mode 100644 dom/media/test/bipbop-lateaudio.mp4 create mode 100644 dom/media/test/bipbop-lateaudio.mp4^headers^ diff --git a/dom/media/test/bipbop-lateaudio.mp4 b/dom/media/test/bipbop-lateaudio.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..5b4cc57095617301994aa3e5a1a1440693598976 GIT binary patch literal 70404 zcmb@t2Ut_xwl1RzKVgyC4MT~Go5nDih}C?e_r^Pm22 z-!B1>)bHnhhI#ul|p^T_Z%^Ko+lxYavh;Y@js3Gc*?1908Yv*ilk8p@-)Rb9bz2x!rEKo?I7g8&M)UD`*0nz)oKau#91=>jHuBv;H(S%y@<-3#NR0COYDAh1 z{W1@%*1UwSD>5EqLQdEmn_kSq0mZF-?J@bbjro@nzd}~@<7C1&m9E7V;j?Stt4H;} zdy&JM3HM=maRmHIzPio+y~smOd`zX=(xya2RCVB+dTuuVr?(MlzUC!i+wm@D)0mDSJ;9eH1m00PW6${aeow7AU>R6=v!W>4+$MO66X4zDl%2fNWQ$0Uvb zdl!09vP-7-y2+KAFK&FapdlK%-uK?tg3Y(5$SJtZ=dpp?uZ#Xd7Dt_nM(Wu)5qG?kNoRu8(7IF zQ@j4fP8XK~$q#v#@XE|Tb4YJ{DV<~@#eC57Pd!VLop|?`9`@X5U;Jt9?L&3EVOf(D z{&mXDy$aqf0RQNqVDKl!Ef{r0-Ms6|o1f0D-sUI40G2A$${fiYF;Vmxj@m6{iMSH# zDF#eUt8|Z{O*3Upgs0zrE@|YMXTKvdEzWlFD)Hqf0-XP3s7{38!x}KbzIPr%w%7}d z1-_iBhND%{E=wqReZ~k zy^iBDixjj|f1#`)yMN#HD2a<=MJ;Y#zrIUdzB8$Jv9|A40W~Vumqoh$%laU9I3=xa8>$F%l zBWQbxVXy32*4nONt@10`Pmqtk9Dgp{WSVUQHV0|*+61g3(Da$NMrL}O$$Qd=)E7~0 zd&%;TpmmYY?Vx>+)Li+BWj0V?<==31U2#mSG`gOt3rvzlka=# z4JF)dZ~zH|1k{nicJt3vMZ`yYS^WTrf9qcx+$(1}CcS3}f=S(Vxyj z_2N-XuOBDVNh@SeWJUsN{7uv0`C12f`cHRzZ`l^y@-DOp>m8kpeOuSrZ)UH-tJlAA zsQ@!GdXhHJE_H*@Q+q4zfni>GV_id>>L!x45g(_^y!*fiac%6rnMFk?RYR;RJCVvV zNBvQbYI$dD_5CKwhw(QqRq3PR?UrxmZ8SY*zB#J?n!AdFX>z7YZt|uj&j?-AI4kGC zUTHx!U)AX9$B_s5cJgO%5pEVVx^+`0gd^9@_2`;8o>}(!7$P*+7+(gzZ<+Y(h2eJ` zvdh#y|U7N?lX# z)7DAVD+Ud=%Nf8RW1`Eboo=PX<@BEq*?UFfSyrW4inh{YJGbuvq|Y`>6j*V;k`l=E zOf)y|r}?j61_mj3F_Fvd*Pk{;7 z7|{dAg_HcJl<(_Vv{1RqG2;AMZVzPY&Ro~${Bjnfxc9?eo!@b_swN=tG1SAt5JsiT#4iBAvV{(ko-SO}0|_}ic_eVv+Yd9rwHLh=ND_Sp z$+;s+E;$}WD+vj{F36j{>yIdjNm4pVQH5|vyhV`sRnQwHvN+b`l54lvz6RF-z4{D( zMhiDcd^!rNd#bAmPBG1fN>}uuq6xH3C__Xo+Fej{oc9Y6|=(-8!&i7`1i#v#suaBtBAQ_*0;Uda#HEmDl zxLaxO8MBgppz!<5@1-G=*s_m;t6J)4MfqDK@|P98feV3mLm>(uJXd!a3Y$H4N3hiN zUFO>K>7DoV6k_vf6%uE8ZBpcm;&P~SjaJR?;0Mi~i#mC`G<7>_~h9tJer3eU(<3wnJ`=%F|SxgJJvP9?v_IktjN z_K|Gl4ecx4W_@+}N#;_0lJ15Rz4W#8Vo9|#{wQwu7~bu{Ff63sYUkF=zC*JD7l71D zm!OBH&irt6#E$ud2lqn$cOJvYVaLng9TFZK-#e%opD}RfRjH*X8ob zbAN}gw%78>YwLx98}-nX6i*3yVGP^;b4R8?{1Ra4x)acw;CHs*%(rt_Op889<9c$c zU90a-o=hxN{xE#w3Q&7>yAA|D_r&?XRdwMASaouImOR^Lgzvg|$ZG5V$5tfIVJNeH z^E__V^DXO6fNsxZ9P<}Kh}p^1y#Kcs${LYZ$&S6$EeR!0mmbd}-dr7VB#=uAKY2`} z5k@P#Eb!Z}h`kRlN85YEoM-_}CSz?Wf1V=l;2u{7wb4Vve`mfi5bz0u^6z#?eh8on z`1rv9L2T8ke^|e|_V#J_+md4V#J-3HsvVw2F0isp&bsK?v7A|<4Y*~hX11`nzYU=s z9}f*zI{1C~3z;_TL9c`LAU?ho8DK6XCGd#+UUaE4;jZ3dL4d*W&kOj$c_da_CYH~Y zl4?O#tArz6JM!#mY-7J;krj1`%r0tqsA|!7LhJe4ghwUwz>3<$P>IFjtQjqDO(Zd# zP4f@!S?@}}pZ0Gu9nBp>zlJvFE1zLHRu5;$L4JFDy}k@MRwnfAnQn2IDft7dH>PR3 zVI%n%hI(`Sq7V3}sIGa~@NSnjN(z^s$}{mEX7kc*amC6s_{Ow-2qq-zD~5<#&|+1@ zG_h=z|5z}d9n%FkfQ4-*w&QfJ%&$Qpy6DOsn$F)?+yG!*O-P+MTN>H}t0M5dcwA5Qede_}TL4OG>@_MYbD1(ys~5 zM9@MCBNb>W)3&R6^M!r=n+E~wQa#qjjAzzk?7okzV*d2Hl-^_S-hhkuaSxui2R z6gL*|t14gkU2uoPldscjuBXW74__xVp?2%;(6+Ti{{w42Z%lO;AuYQ(;5I=q4Fj}jo*guS1*^kaUedCx?#$eC*0@K;ns_Kqwl^DTeSGo zi{|6+XydMuSc93(8Pg_^0*E^lQd=eEUtpoKWnyU&pQ=BQ_*n@L1D1Z^5>zE;)s<{A zE>k@(eq$?Tbm)BUTqaShsBAI>hxD6>=po&={yjJFOy7sM|GK*X!AJ>ZhXG)TuEqm2 zzl0^D+G6C6jFfPKfi`X>xNIMs8Je60!Ss^bRiehebT4mF2OxyO@X8Pb!voFB!lzxGGdIw?9?$LezKeTn}lf$>WevmTfm>CQlS_26af0h&D^_}0NerP ztP3zm8VI<2&y^8EKa9k3r4&VR0Rp|8Ce>POki{#Oa(BdsBQnd!?wYF=>(aBXAfs<* zqIshAO(KI!(C`_D#)RBkAE++i9bah&pSA-3$$Mu(3pyMWpjPHj%7MpHWyUbRI1R&V z_g$n|xME!yJ>zfFdIv-+(U#?^?|c*X1$~M+HD=`{faRuQjXH<~juN3g-jrf0=?j8n z=;u;S%aRBZUGiP7r-Nhn0+1?1K{n=gK7*j1)VCyNNMG47QxV+=Btpf>>5-cgTqelz zi;%E#+yE2Z1esmyGYxtQ8RxH{qcuzvgd~M&k$GrP8HT7aDZB;H^0?ar-DI-h zHM=AB!8@Nk_ZH(_u{s2zXEB`k``v=I`TH~IG5-2E%I7`}?Q2M=QlYo#oDo2>a*_)$ z?(*PVj zG`6}v5#miSW@|_icTk17@sTM4Xk|*esC>=xfVjNLp02ej@G~@fbc}sOBY^aGs$sui zDa}Ww02>y0Htkoi-p~Qu02X%i$@1!M-2mcqGMuZt#;(3IEbs@2=gFczDy$yyq^@+s zVG-E~B29=n#o7$wQWeo?b}3bOk#{l=kXW6Jrxq$dco-nu)g8Ytwt@IQ z`?q`ojcxjrOWB)t1O%{1Cr0Q}woEsK()&LVET=&t(AVeLY5!li7wgm2;lKRX0wlTW zG>DIP|6Ro}?Cu;jp1I?rrKWqSHiSYZrf%Ugux%4Yh52m@kl?Vh77%96|HS63Tp=Q8 z&h%hVKsUybz_8)VYdBkY3;;=~`Dn~|2MXkg$Y^l?16xF zYxWR(00!|jyipez9fNTy>JJ2JcUgvC&(xx2wQKw67d3LQKbG`@Z#hmL`IA_b!3ba5ntH83F#l4=;F@^$)wf8GCx((e z1f0pBlMpCCO$z>E~oPy+E@( zCAy>+6x!ZWSF~3$WcMP9n5Or@=+#Y;hfjG?VlSdBrlq>aMk~A=hgcpl5pfa6%!#cpS5*-+H>V{Nep@= z(5EzT0ym+?cDL@`aM9ps6o2LS2i16$Oy!t9l7Pj-j`GnUIC;z$?6XA15v21_w4g!W zW+NUcR9yKrDtP^9{SZzM+gG#5;QC6>2Bqg9*I!J!CUucKnXR0gEGF+|A61wIM1<_B zChd*3s(0qI-+9Q-m@(3LC&t7&tqO7rM@RBMG8y^FBCwVvmpHe5i~)cxDnVr8`ys-! zq?3{+GiByJ{hn--)GxnEwnnuDF*e| zy{G{MxFBl>BjG;U3}i-|K^*%Sx3G(QIx@B3f#d-&h5@GsSV%;N&3ql?)`o}{7YY36 zhE^$Buifi)IgZWrK9d9367<5CG2oQ|kSMo##pw!;Wn=OW;%r_Y`$r}>Y1rBmc}~TI z@L3(6*)!bd_HRQ)wk&?TxIKe-9tPjmsW1R`vlHx&7czHhh|GKm0e`+UY)2tLWds+R zuirvv$I#XnHWc<8$Rg7lcQ`o|-%3T9M<|PP@2LQNt8#K7^W4TI)0TB?1efrp`g5xwq&?B$OGxs}rgz>``5jOfAb0Z^yuKR@5AoWilCM(U zmNv5nPIYq3r1DtIS09f#6z>XY7J05_=4g%;yf#u&q zfuWB=gE#!o@B*C^T4ebbcmY6k0p%phB~AJ!?1#}k?}@bljyqX$uQYF7EDX|~OZv0c zx)50j>HW@snL64(^*K46o;`jGIPv(qT3|c$dl+vf&w}JI_tae-s`Ej?C2*Z6(m1~H z#-n?PDbd?B#pe`h=SVEgHh^;wCz&q}Ekb`c-K{(HO!^N~B~ z6!tsWYy{UFUR>5ZcTV|N0CU>3<1K&`JHrwyKG;HDseyLMZ_&}d-${0(J}c@(2!7Ux zPoGm?^lc%-jC0oNbmHPCdY8={f1v5|;?7}r?h)>=>^zs=9gn-+xkP4T=HBF#)tB(U z&;?XI{D)Rfgb=6IsbjzHWg18tv%C8|M%-8}T4dDQyo!CGkOi0TROiK&nOq#6*K3e6 zwq|v7on0|+W`VhXsTU+nwJ)xsMozVDM>`j~B-px<%@q*Qns<_;)@7@`WDL$;*CdyL zKE2#|0qN8;gx!7QdFA~u_dp>`7^vOtJ$#ks{dLc~+Ytc(2j$GVG`xb!xaiqChT!bO zLBCJd6_b6y}pHt6YngHT0kO~F89d>5n zngyr0`wZ(vLTpFrYQa6UKbEV?jsKMntp$D7v{~LW2mimS1^Vdi>mIz}Wxs^=>w)fA z#eKvLXHh5G2JOP>l>tJiaU5{4F4e?o@llp9xG_Eg*Yj-m5Lv;N2V-D-a&v{YW~oPZ zI`-W^(P5#SZNme5Muhjl;A@jU_R-1t8__f$Mq~FE)P{7jQ$lGRv*znXC7-#PwBGO7 zJjgPkyuIFqq8II3Q}F2PZP35F zzvY3A_T)T#>Nw*%Cb74$I~DNEsf->`@fjP-;zaV_UepjHxCEYy{1j+y{Ba3iHh|tq z=KVPhkhm>1v7>s7qM9sujp@Hnl&mV4wW(ci|KWLL&HeNGf^aN(A9>S*wuMWd1PeO$ za26Dv_L@7L^3_P_6xM|6l05)qHNo&)w<+d!X<=<>Tte0PQ)oX$gmQ+s|GyA{JVj`U z=f5Jt7{I}d(-eewulL79rpGacuBhXJtGq2D$qhsf6j?B*mjcc z!2xXPHPmiXs&tR{39ynV^vUbP!DmR{&`z?-&sUFOteu@1ViJ7;XFs3CMEdb*Ffr(2 z{v>GHmtN127fkrZKJMjo>~y;^gC;kryUDzL=CY!X0( zNwUf-;-2+0ycj1@+wMrFuC9|UO>LSB)OI&! zXmnc_O*`YHhlZbh2M)GFAK@Ox8>J7b%AFUAe?SLe(7e=)bks!iVDL~F%0X-WbaO9>+y%a}HP z;qzC+GBTu8GqoB@fH7K+uU`pu-pk3Hh=gt3s>>#Q3V#aTc=b5p1sX0YY*JEnmN3fq zUr*LHtPr6JMOQyCANc%DdC2!h^;ee&N+E@gS&~IU;GOX0`V4QG=Jv1HU)56sL^Ar> zo`Alavsc28l0OIvT2R^4@pDQ)EtECA;-0Z}nkwq)GHy zNhb>HtRpIoTJsrWb+TxZRImoWNo|(d<%^yb+#D&|!)s2dKAuRwsEokDfPU2MS{LOfj7*j^w*u&p2oGwG1eywrp zdRr#VjY0NvG9^Kd+5RDNa_22Bn3%2~`L;n;6vcW}F5n%G&k2~HR?r|+Dx5{WYprw+X{XLM-D;lm!nqxGu3LX7Y_!%y&<{a z4+S9*`l}#*r@4tOE3d$Dhe(kMUnX(;m$BS@9AALyAi5vRC|oW(xw87~qYqE6h%%p( zyEk)7(zj#NIXGItPwLV{8Rlu=+~&odc9*Bg=Gz?I^=nC)u`Z#@egD)w^Us9zYM3+gxp5puY(wt8 z-=6b3KRk-CD231`YbGlvOyvWZtx;<-@gd_i{$yCXmsEVN}E+I&h>al8P`HXA;Jj>Hlw!jlttcHH{bSwS>MH*?lxWBK3@3# zh`$K}Iqy$aYkKQ=MH+%`6~F#^eWLmst%ap*gX#FNBp+-Fa&Zr*w^KoBYm;zLM##$K157Vtc25|dd7ocsjhOjwF`uEx@o)E`QWd5$i zMCgWhJEiI?$1FD8H%;5P8R5I)tzR=Fw)rM(;B=%hl0XYMN{?MUIrP;T>DX-Jy5E83 zL=DcI5E9|cIAYqXm=GExsm=e} zul;_k%jJvYb~81gdtGj7f8&qZ=g5T!A>CRkg@laF!Oi)rG|$4L*r9?bIgI$ia_(s# zUdCThEnL#nZ}+uxt?Rsng(zIVLf`h$|JW@y`=3=@;BcclD}4dKeZu1&WBDFK^DT6m zAPwOpKRRn^8>S4BT2CNDuVI#1k>csdCi`5Deg77@{WMXyTbxY8Hs6ZNN`?bO$-+-R z5<}U)I`6Q9rk6dxL@i|1W4dDXN@9o1(j4A~ZJg12v`g$9dYqn7Oh~-1Q0}?Zac2qB zn()$og__Xw%7;x^N}VLCNLj%>)=If zjlV`y_09@l=!;oeGkPZL6=iE51f!5rCbdf5k^#ESJtKffSj7FX#$S(|gA2r9^Esw~ z`JZ+RdZNcbW|X-g?1wy#-od_N^{_nm;Fuqm+<;r@U(Gs+j+|OB`yrim*&@*LB)&9Z zn5m&yk=j;!(7#J*A`HT9Z|6YhbtqsfkhlPKYPR(m+y8Xt5^$P{k@1^pU@>LVq~e#1 z9_la&8d`u3R?1g=6-KQeN(d;U6^r`6Okf0&hV>!t2vNyqZ zSPc2&Svus?)u%RalK*5^D+-<6QIEoZF6^)EQiwgFVVO9aV!tZt$rLR|ske7&zVJ6I zG&25-^WqYJo7E3B?Gpb!^%zqoxCX9aeaykp@5BK0=^POd@63uAM4m5DeEMC==Q?Nk zyCfI9?yq{I&mU<-eMwmDT3!JY@(OSH&L`3ox1k2L^hs0Vl5T^~tIEdOu$KR*XvI&B&&=eo8}n!S2(Q>@@7hXzI28YB?E+Iufg%-Exww-` z%A}kXkLWrx|4eh{x;`{VyTqtM-W^x2FPYmm-K~7RPpGD=VaO|~^RRf*bnFQKx$)=L z^w{E=i!ZN=YITl2xe%;L$CZua^L==3Fz7YZYKVO0%V(m1m^ftr{Jk6G*X@^XJQ~lA zd|G%cD)R==>FZEW7;mr}>`$yAx2mr!zMg4uPz)Jrt-S*_yNQG=9~nF>8I@vM$sNe+ zt;tgNVEc%NeT#h3S-qRlm^vwHT&=#sip%W*4z|Yp%WdA-h$-?R9f%Lvgi{Xj?}y22 zF=xV>MQ%WBKX8-Uh|aF|n9WXF|J{VAlNs!n2KnCX^^_^9KvZ8WWLwQT zgne!lSZQ*~uQbQo-W*~~<>;1iOP9Ef*?q3#-xD?+Ta8D2Z%>1&0BS(lPIArIP1fUU z3p9_?yglFbwdvIyc8JEj?vc)F@F?c$Jenuue|1ouxaV}CxqsZ!d>1DCVG{NAB`Ys( zsYYN04wd{N!L>q~jnG#a!bbDLFY&So=^sxzgod&3C{p4d2uxvBUYiW?Uv#j>ovSX` ziF!(NsrAdHKa`+562k|ujavtUx8wIX{naEt#XZ*b!Mvujd1mIv-WnavPfx3(74}v9 z2$A_nzLA?@WtWspMa!VIA4eo{>86Q3vkI3MjeU16LCi!`;M?A)p$UO zACuS;2$iQH+b&|xJiJ)A;olpuh?i?NFWl)(HKY=|#*5lH@Xclv?=r42KQ2&f(RtIV z$<)!bc?H0zh-Un_ge_l3tmZB`Xl)<#Hfu6MvTOXK`{bxLt;Sxpt}#W1qWzUC;X1tu z?CtHZlg5s*e(U}39125AJ)Z~ilnvd|I1FX`7QkWlAo<8gc@`6oErl4QN-UuRW-fb= zlsKH7>o+)>u9AlzwhXtZ*tyX|+@uaDOSbYkMHvTB7+Q`Y)h6>FYC_PG8|ymGx!-Dw zSqyL9YcXFU3%I%5g-6c&8%8nDvP)86ypwE?fk-QQw?>hJmdliA%TR#~i)lYx`1JsK zM4hH*%)Ut@+-cSU~j3pMCTGB$OIn7NKC-^jsM1b((Z=hVoXtpU$Trzi}4S zcwHjFJtM_cLd8$#Odntj|3j%ZFyb|Db)A~sh zYlK!)NHLY`Ed1gXr>0;jmEScf(m7Qa(-y^COHl3jH;zU^v(kLP2LZ|)5WZJ zSIpOxeC{HJcE{NrBk|{r7~tP^IX{j2XGEU;(?%jBzt{A0?veZ)6iWLmCfn#ihMKPW zdwzdogkA!Vq`fyCDw5}`8-E_0c-*rx+TE!_YoNdKN1CDP3%YlZpe zE{C>ak}T!ccqdDy|9t+yW?0Lcm%4KvoXF*%O{LkYT^lj<(u*dRtX?6dyK*SC%HL4<5Gsxv4ehL3x&cgM{VTk9*XI(b$DR zUomwu_ozKtSh;)C<^c=;>#7p`EB4r?4I*Rrf*_Y#XxQ7e-RIirrBFJS7mZQ6af}XR zw|^;6txjO2_|6ofmF1m0tnOC%cy16(O0}UvZmK@rXu`u*N>UppGOpz&O6|-jdz&%2 zod-?t^xWE#I`d`0j9{$7vH>G#GI~#4Qi47bh9Zzox!!M^_??}-f0)eHB{Td`Q?Vm) z1MUBw3^ahgr1)1Z=V&cVDeWAuNB8{f`gwE`1% zLomPO68(O)l2NR552SAnfW4D286g+BBXI4%w}(y|)BAMUu6R0ksVy{aB4Q3VSOKqM zcjiMVrHH&QnXI&ox_3(A?wy3Xyu7CDl&oH7Fd77<<*la+2v)U)TxzSPQ)t~P6YlTo z(5|2l_r_I7N}v;#pBNrdEBblgy1=G&$Y=kGEg-zirSzyE#eN^ib3~nlkDh~g9EF+R zujUfaO_|VY4*SG(FF=q=xlr`%;}{m}PisZ(Z19on`?U@obHLGFHu}B`eFEL*$!*rF z))gZeyr|$V#xQW<1~C9xnF@|L>4zl(DMujhMo0$-j);;y28%-!uRyGk6*M9D``!COV=xeT;YoBYEefe{{D%gRj%mO{69 z-f$%XaKf)exnzdC{Cw)8h3dz>;_nQ(@VA++eVSug z@s9dQFfn2%+Yvy!Uw+y{Gfuj8Pq+;Q%*TfBj`A`6x)1NyC4OjCt+mwuh&qhIMbAk;u(bIoQFF$(Bk6Xw5)s%U2Am{ycuO7kV zQCX#yH1a<@@Ld&TLU~{fxv(*6WHJAoGJ{cf-GaiiX?B0g!cH>N1m(Ff^Ob$!Z(!;W zvoya4^Av9%!t@u_q%4?^He>-{JT&73Z1OPCKg)vb*46uw#>pC3 zx;u@t{p_OpIntr&*gw{}jsV3uFE__96XWy6?jO&c^1_WEuaFflw#>4qW{`~MBLHlG zBdCW3VEM-y-tiCtAR57s03rr_6CxTUAH>cJ1@hxu`!5KE#1#j_OU74s4#Cgy{389K znJnN5YKh293q>p46Kh68Ftmu1s@A9f{Q9qo{=V{G58+T2+T@4-xSvQu{6p)19Ebo5 zna9X~>jqFu=u3lt#jfM^MEI`3HEJt2GS2SSK|n!WtzTZ@dbQ@c&&Mjixcm?Bo&yj$ zwqn{3-`sHRAZ0-UaA<@}71SjQfIp@Vk@lLwZgX{W19T*6ifr3#0!@)_sWR|+FC`U1 z`9eweEKCLhHNY~*cJ#?O4Oeu>`JveL!;Feji4ul!ZV#M(JJAHl`hMi+rZ`{OS}(z2$X92e7Ey0t^it7 z=yM&DZ}j?&(@3j(W?ASgFrQMAfj?6bIJvP_a1S1hU5Y-&0Ec4Qyp>v9REN$luZL^u zs^88xcjjGI=X&&8G4-XG^r@lffjCyl7bl(hUvX@@@f66SPmT|^FsNu=`@hS-Pfxq? zx5NU88rXk3ePpUaKc$#4|GWtR^MX$0bd$T(zL>FY^D zMmw0ZApjr@e4PprJr&b@Nf_!ZMpHs&qLO!BpixO&azfiNBR)JmL&<6B938z~v>Q%u zGEgLdg;p-~Tyn!fv?#}ut?O^g0TRjG{tk+tRZHspvMy}0!~q|t1yL{YfSHzjS4%V~Q`#k#%NJv-13a~EJh(undZb3~in!WqGNA-~!9{oX zq8nKy`d+(!>l>-?P&4xfhOzMzGy;uI%o$G<6w(I7l(XsMQLOEd)o}|G#%Uz?mk&NY z`1FE`o_{cK05Im^8VJ{VlPqm}KV*uZ|8mQv?@3&>v1O|$b?c{8-Uo{|l^fff4L0iv zVNBiqEkZ@q@&2E#7dInF)k=brCc_vca!#LU%OpCyW~s$T z>S)<2VT#_rwl~D@pL~DG%h_5&T=bmJ;akFNGQ;i zMMxsxJ)jU#+n{4BgGW1OLV(lY1+Sq+jY~TH?q4Jp^f?k1RP=9lOM{^=Q~niXNbdq& zsI(;ALmC1Qob-xC=zi=SMq-clUeR3oRD$y;Oxt~sjJjqAHn6l9_eUdG(A z7k3=s<#9|@s}vRnGjeW7%qY9_!!s|CHwXEuC+0LY{8{I;VWu4UMN`Z$I4`mw3{{E!cbi=O%aKB- zWAc~$F9AnVXhr`24med{R!tpyG#OW8+YB@})aD1z0<3vx#lTYOg2zbKgTtRrbs2?_ z?=g>0_e{aZbz`UTz`HG7cdx0eX!v>{rE6)#Ra}14 zbd|MazNJkMMd_2ZZAT>(=(4y(W?!FMZ~2RN+C$y}T40t@H~;!c0 zQrjvWd}{gjvMV#lo1;Cmtep5h-I;}`>?sF3XKXc~Kz&5{n_9Kwe9yC;+v!-|nRMMk z7H6*$DMBn#k2&}Kj7mzcyz;~{@UtV@k?|4wTBZ>Xz+@$Dvi7r#SmiflI=r-{>DJgQ z!`I=5e4Xwj6bt^2fV>>{(12KV#j9w^waZy_mqjj_jqT&=r2LE5S{DR9P>)kDhWDLw_U(ng5? zNG%W<;LQXD^j%99+_nk`ez3Zce^k1R zZ&54cS8RWtJtxrY!Yw;j60{h#{svL*{OE(P+Y!;Q-6hmYgnI6XLRD2s6Y&?4a09CV z>L2$jZ%U${-ZjueO8Pse@hx)bQ`}%Odqa~bmUe%ze=+?&z^e|qQC?vv9dzC+CS7A z61mS~3c)N;EbWO5T&mdn5BjW`ls5{@-|ibzqTzax5j1B7dSZf;`Z?<~zMy{N)t})9 z0xQ5CKSxO^qn(tW{O#96Kp|1^jI-rEYbK+s5TEnrQy-c6z@E;h83-!el=Z#hcOl_3 z_jGj@Xdt?v=RaHiA3gujw1bENZ)aO(&7?+v2mc}j+5cV6oCmaq;XgaSE|^3!ahgPP zLb;jLf3_(EEvxUP?vlukWzU*sSc?t}L;YE%{gVl8 zzZn7sN$>=t9)5gNvW+`E0_d#`WcKzw<3&~foEtcKPt7l1qLeHDlOB;KNFKghj*$_4 zgdVszy{a@m)kTQ{2<4i(;u9#=_)79B9mtvcF=JpFMa+s+iXUegkNgg0xYWQen1`-H zY`aQw(%nwpyGq!%anx|2*MK_z^KjtIL)>5ofXjjI+?xx;jEwqwQem+H=2bmx7I6#1)0|NgQ_~q2xa=!Ti^%jpeB`}Ux>0## zyP&6}#$PvDd2s;#``&&3!aUU0HLpzl1o{#E3OGP=1c?2TLP7=E2mp92NFD7sMHbFP z@@MjLV#&xv%Z9(0JJWau*7l?A`}$IUT4l^3Qe_1xxpJwZg+rVB;Y7I7?2`c0t_{0a$<**7(=k$yjSl7Rk{3*z&r}o)~_RDiG5s-SVyKl z9P9ffYi&TRD=8t4PzS8bfZZJi*^>qov6!8eQ}SY5Q`10?$f%W*HWc?$!QVe6v_`45qm=Csuo*6vKBWRcdnOrMvZj$vw`~;UygXS^oY3oGr6zUf(^;+@yzzq zqy>#77c|0D*3s;8aR;gdRbu6EzD1c$v!ze9asS*IAMhFKTKa%-W)6D#Gn##VHnhIs z`_$cQiH9=9JojAUKWMa#o)rmXxP#>uAb(rlelmw_tcSa{Dli**>~<;4CzG>((s5B-G_JMJVs2PA~r@P)PiZf}Jft z?qNa}8Lf`5ZXNO6@*#IXcR>p1QBl!-m!stA=4_R|@@ zMdq7xuwvmqm9Ij7{fYa9y!347{@iYbm8hH6LH>-G`C$XJA!-e?*XYRSpn&f_^`}D+ zAj@DESC#jGq){@bvj#%#(J_A(7(9uyVAd>0dmyqB>OT=muHOY!!C(uT!-Ej(1XEO> zU4mHu)iSg*4f$lRWWW-de9MS4g`0bpK;a#)HfEcb-B2|h05Jc(CJ*IMIO9_zYTOiL7kw$qR3j{u8p>Rbppf>d_4_bcM@9la#~w!`Q*AU^*72q=dDDgM!004&(? z1$ZUfe}n+w*{M(fKSNWK|5vL;c4&?HDXO2wnZN)706;oDA!E>t@W-C44TT}14#2hP z-U8CFwEnG1SAjN#)!rpGWv?oAi`j`btfAD@@V());15=o@|y~OXNDq)j zj-0}!RVMdS`*p{zgkOB;^FdVl$6RCe0mwCzz`sU%$Yb4NW~w)Z{~alwks7Jk2&_-J zXr|csx0Z3g6E!G}(W>#$#H}|yZ_5_tql*<{dmnf6o3`g+5UW@Qv*?|3mw%JE3zE?_ zVHJWhOo_34JnA;rE^Bvw*{hI+Ot*>D(JnpwW+8kJ-t349Mv%JPL@8b{E3(U)8}#cx zd1cgDc60kP@${kU4r1_2^;qfk8By{;`m-l-06pWoMxtoimlub4x3kOl#BI4h|9)5G zY)vd<6L$zXj9S`ml#)k!d+Czk%w6H@-~xY988`v9woxM_&@oSg@08w>(}6h^g6!7+ zhI4{e39T9ZU!t}BKA84^i66u_|Nf&amWy8632%S>rmr=%dj0EH<>FX9=3iEQ7#9)2 z%Zzdc1#+Sy)T!}1y@IaM)cdM7)QR?@p25A0;X?XfxHUY4k4JC?KWhZ!lc2em)7g82 zfQ-vSSzyz_#a|{7qzV5LV#$_`+I5T39;XyJX6a9gg-9$@>CE!RTtzkgo=KZ^%`SSJ z9oHay@CCPEOkd2_26;F4XKmMF`dz2_3F2=Z4T;cTkQt!hy2kC+)ujJ5M!@aMNdo-^ zOscu4h2#Et7S5Lb=U&D+8k2&}Cgq!rI~Thh7v#@jLLacydQzr^?#;I;4}^M7gW8sf zx9H>W+Ct19xyfR^%=qPn)2=5p5@7XH0!7==UZvhCz6Enwrp-;9b`K5b1Hq|QO6h=g zr@P&*u+OU%cu#EHs{y%yS_HIQiT&Bm&`2$)Glzf@EG66DEp#~iDGv>XShl$Tcz{7R zvMC7i4i-M3qvLk`)068_mVGz4%K$NKzy_Js{Ckcqcw8d2@9sS7* z&GRj>F;J0qIt2iRa$qD&BBTHUD@wa%Cla*W3j36bRhJuaRLz|ZYM7&FY5^GMAeUMP z63?vm9xY$w>^y$j$$rGBs}b@3Q!TZDSVi>3<^(|&pXtg&*r3{BSN?0M&>n4;-wUCacv!NFTcw2{$L%ZVB65b)PyrPB&>%r9^GlTnrs(FYJfWOgVq1yu zULY1Kdb1*$JVZCo6gVKU9D*hZ8fBlryK!dvYwgqrlN*NN_hCLNHCZl2bxV!Tf`{e_ z1C;-a3s2f9%3)5bX&_DAm?UVrp@I5~rrzK@PY*_pp)aVz?6Z1!pWH%T^U4qBW@Q5U zM#&Az*Bl!!HJgm&%W=Rn!g{X2A_m`+%oX23>nq@LEd`*K&!PUQJQxl8@TCBdgGgf@MRN{@4elH7(Y*Tz7(y*4)Hd zC}!>(>7iUFRE&s2zahgRTY@%pa9D6oxbFXRN5LEfe^~$P#@sq8*upi}F z%yVo6FkRo%Ql#8_Jy>~#%zmBd=(D0khcEnN{|nr(9~!r4Vko|ML~S_Ix?aI2V|ugU zIA6rZ*V4G>@PAm2$ye*-blB=tu^l&YA8Md>uvF9W0W5dqi(9zlZ*zU0WPJ9GNpX}4 ze&ight^h6ypLCIutEY~tqx ztNc0@1ywmgjcMD~VXXOxcRbiK?J%Kcuq&KJL1SP6QVCBDMCVnQme#~jj^3OKO`>-} zHKP82wvC}ia)BlP9OQB*;VeACw}+~)m9@Fzm62Jhw(wzuq$&wUgv3qoGrL^sRs@0h zr}`9~MS3mOPxp<5KeiSL=xzU`%_;pee4AI8qHDF@!_a*=eAze_lE@aOvb+cXk>tyr z(rSX!j}_>V%FZa%J&MFa0z-9UL85b!-|9^Jxstk@@&aUy5B&$K>uUL<8&-@NbW7i0 zK`44x`D=Zys( zkegMH`zk;J8mgDbxqkcshf$h%jG)zx=)gQ3u73Gi> zKcdXm{pRlrYxm@Rv8fB9ap@lp=cvWCtFWrFF;v_BHQdO{-N50*Z%mamP8_GN2UR<~ z+rk%;MgeF+FHWCgk=$u;H2fB&A-(p{s4cxy)4lydZ^f50tGt44iL7m_L^ky|x!Hv4 z=Pv7OuGxDzfqPwC!0Y2)&#%$Gxpx_6@JVjVO7ABYM|RcdcJa@t?j=S4um>?7ljon0 z=F+o-@zbO~)0}(9NgJJw7tn|_nCyq%_*hlt8eZ!5g-)JH6r_L79FxkJVMpUbCdu^r zPdjoJq&X@qVx`*BKWegOJVG6R>%j zLR#fqpb^s%^WE)IZ^wZgf%*TzQav0?cdUrVj{W%47D=O`1RCkF1r=@I zES^542r8c%)a!8;?oR&PMZV7jF%hgno332x3(0}@i)EzMDo71}0Fl3iC;G2?v*o^J zgE8~`NPzorNe@szxIcYH*-j-j)wCCTfsoJ?itXvNbZ`?C6+W7L^87+Nk%aHn~7N8C)9gBym_V@WWkM_%@4AfT{*o+71S~v9`rCcNF?ccQFqqmACr9(z>T~E zsin`5TB>pvom0W>JFqKJG|(GNvtK`R&bJn>xfxMIMupE??cQZrnc9BEuB3>-KS)1# zl3=&2e)jM-bRxf2(Z71#j-dW_-*Lz_%(O*vsCOX?OS(PK6TIn}1bLplgWG1wf5=@A z%cEn?Js<_rrYG|zXBUSK} zur^viuW6X@0PDZPM$_3!D^IBsSc@iD{$q;!UEkleT~op;IR1G_;s&BRXwyg$pG|TG zo&DS}4uuunK9N(y2Y;?d0{8+>2EL^gfJ<#I7QTrjGxxM#c8H(TjHdN>7x`_vJ{)L} z<@zlTSu|YzJ74$O(v3`9Kzeu=6h7L+Uhv>%^u<7N%rRarg;O9w?1tW8=ixFK?@INh zO`rQT+g~C(aVH-33~DW`dd}YLj1+{d$dWodr7kI2S8C}MBjeNBg0D}0&NC)gw-cBT zy}7##Omd4q`eC*n`k6`HZ3S@;4%-c?F(B)g=|i{i4kZ z%ZLjCQ1_!C{Ze^8PO74Zk7^2^36$4=kf=X)x~S`PsYmzhWnIjQRws1qdfV6^=UxBa z650((e>qDIxKYVVdABGY-+WtD>UMf5PX+3L^#fVw5IyHNW(%~intaB#;cs5Zy*tn! zh<+^*w(dVXADXYOsoMPRD(I5}{96&9g0PiK^xXe-7P#g_IwrQdfP<08kHb6Pz0v3R zPNmU*j|>U4C<<%^XBRz7IinH+2z*{ZLsF5x4VB&2VO57)wA=wI&ZymIx(kTNr*sNm z7Nb22!Ko=#ax@73w>1cUsdZQwbuI167+!nr2)@6|_HPQB?? zc$SX%$1=q0#l;c__O`f#QH@o$Y$y1@)mhIEe%qc`Kq8;k3h_iIvYiiV2Sf-`xwF6x zyyyvwc_va;Rz;)q@X+d7vaL!#=O|{10Ds-)7c@njqYg(8awcT&x|M2Nw=kH=h#Qxf zM9U-{KH_^*0+)9#zHamX(B+PT`tcPE_Py@9!g|r9GY4ECGxoCHjy~*i?h_OeS}$tP z9OQ#gNJSA8qg!E6o0rmxom$G6qwfcuAZb`YW4aCuh9>rs00UakngDCvu5#b#WlTas z=R##2%pHgCbDby`!5HxF4z!&*%G7BYu;@iJJNz@4pE3Nn$Nl8;dIW{J+@)mWg=w4t zPtl|ft2(c{mi;xw4wnKj1xGSY{I&eo4)*^wmNNf;v6TOdrTkwk<^N(S|NqBQN)V1? zC<~gC$*f<|tIZ@wOfFkIv)IlD$S<28@OBZjw@^_0I81!GGj3ld>B5#t+5I!TmRn(f z;B+DQVwH9>ePuZ&Kq0l`J&aG%`}sn3Ht(t2bxGS!YW4&vN6P3q7<`_QEz?7=vkxf-sqyGxW}>H+bT0OSTMrs(a*_v{+kk*qu* z30*w~MY3P5t-!-_c1Cv_q{6&%eLs74A>){`ba!SLQ@~x_7h4eD$d)0=fWtGWHe)zT zE9t9gC&j&E#*0R+$O0yNpB8h0eSG2-WtS6G<`wk=h=Z@sFU7wV+zuiXM(0vDQg_zRZkd z^Zb4)5}5f!X+wFVu0r4Jb;Z2J{K|<2-W)*_#i)v)cVW6uL0Ma4K~?xElX^nhIAM+E zVIsq2MNV}+{u^NTs6G^1`Wt&oz5~4La>6r~X3=yfbk$7){gQ=+AC>(E@&{#60t#E2 z)C$ULz3hFo&Jumx-d8+D-d^vv*)(8EmGR(7vL9ddSRW;Be-86`bKL!v8;-JF()8O7 z(e;6#EiiN~F>z=D09a9SpFf>Yg850eH(vUoZoc-u*C0_U7fx2SFqJ65U~o|-|E*wU zar2cK#<(8S&Upvz+bjU#LI5E9ZI+zSGf@(3Ry#UC_-I5^J>S6e^NWPj&U(rDtr6?2DEja4&!VNb=msbK&KPn*_D5zYLnkS>&!ecb?SZ9Ylw`&fmT=@S%_u z)B26+98r9}VJ@`r=h^iUNL5fjd7@`opqJc8wU=LZT8>~2uWgMN%m!S6_$rCTVw0Ra z=S3Y`>j0*`lAqSy-zId7T4eeZ9>B&GpJ!r0=?ov&<*+?J&zfqlh4k%RlzwM3kfS8GO)FYe#WS+g+umMLk_UM` z!6=V5evlXd{KUbTx0BafJNO?AX_3eV`r7_NWgJ7=EeN7FL?S$f3lX#XX>J;1 z1r~qC*x6TJ*M4?n^P6z>HN&aV#?vF`52<6J-_L_S2!z#X^<2`6KJJOHXEBL>M?#P~ zO@W6FG5JF2Vb6L+YDJ~Dqmaast8eO_iEuNy=&<=G>MGS0oExX!_jEc**Y3 zbqGrN2Pu*S+N?Xm&Vr$@VfQ?fEBt;{vzSgU$OD(tPtd^d*ffl&+&gM>h~s2V1N1)G zb+=S5`*@XqtD--j2jVi?IKK7k%UXD1H8;1Op1M^;XHPz8_;hzi*Xjzg{Y){<)TjH* zWUD`Qo@j1thNIFcHbv^v!_Oac>S%Xj@#BeTWiC6-ZB&{n-z~6dGhA_6Qd>&&v^5z2 zqF!Bw$jtDK38DQymG>gzMe=}~J>M~cs&R?L>Jy8ZFdUVedS{m{^~u5DUVZR6)f42D z(5ZQ*;Cvh7dfY}26Q%RBM+<2Wnv;!Q5G!RSwA(GJ6NVc128;;1antViq?p@a;i=Wl z5H@n@?_ncBP(n}%NG8-gNc=9HPP2M@U{1EP947ljU-fnsDvh94B*#?n=>&25^s!T` z=z~|UJ8w?X4h98Ik{uKD$pzG6(kOA&<u7>Lxn0_kHYJ%J64^?uwHU&N_MwuUWrqn$}pR@f9P%B&Os^HH-_JD)fpU9xT1X z4<6pZv9X{5K+K*1qs@XVO!bJJ~J(ckwzFZjtXS24WeKI+i;9@=XeU+0qW3fW-+(P>q)_wjT40gGJcA87bk^q(09yVjyP$G zAyvDmyD>>?cnqtC==Fg7qz6=&zc!;9_FSnm${jy5`iu&a>4sKqd7)98hKobB({fykGJNId0aoBs=H zc0cBzJyn$!rH&#)1U&uYiJA})$FRbom(b?{eN87%B>4W%LCug6xJ@vl@*=nac-<&U21jFvJoP_1CieUM+)oTOiTTksx~3J&(w+U{~Eb8V6Z; znTyC$LQ0R$KL0ra-u^|1H0uk1Nh=#KzjHh8c2#v7_dPmQkTE6~E9w-f{cA@Sf?-m+ zd46`AGWZ(%P=hvjsBd?PYTTj~!l4iUF83gqe zeyB;GkO`2FTXXtf{(rBF#MS)Ej}y3-VBk!4@reZjR<9B0ddF%|{c!Kc&JlNg?G5$W@pACnwSSUR1+*?Xw z)OS2&e*4c`;P6ZT0~0e0VncI;q#e5qQ_A2@*}P>NaAdza87OIb((rvz%z4i?>)_V4 zi$AgDI|f{V-PV|qRTt@QstTni3sGk-qCw*Nfn5byL)<4;5K}fHFyiXr%Z?yI#=qiMe zy6{FdByAN|RWV=*u8OxHK}#rZC;~)-U@#IJ{*1Ve%o+-3mSV|uQJvXgpg5TU`_`w% z;H-1CVQUb?5146U=%s2}?&(k_CVp#&-Dp2?Oi?T7wuYHy3s6(s_KPL*^NJn$g)}W$ z3=mIo$6fM_W^>+T<5`SK2=i)ltL5+A+$iL(hSj5_u1+rUh@DoUwdeV_5dBnmX+)uG za?& zA9e)ZcB52=qdO*}N`sZbBK#e%__3TGe{hvtD1aJ(E3-UK(E3LvsEOxb29@)Q{K)iV3oavx`&gpD7ZNSm02m3WQE$#U1#EpL zqT%TXaGdB*nx?c-%LqY9>K!z4J8d5X?VNu0U0G1vTRV+8@@9n^0twQCA-BVR#o$?0 zSh3~TDXBRTbj%vP;3NXQjE2n1PCNPFp5^~N>ks%Bv}aD1afcw=_*nCksWRvZ>c>Jh z>8+_`T?#K6Rn418e5By;m%6mv4o3*&abk%MU+DU;9CXV|L$Tp)bi-)8(QmLPDfHWO z#;#UyL!!x266-OidS&=0HanvKkikFrH;P9Cum)DVlR)LBU%ad|cRYZnT_nm(GSkuq!OF>MFqIF?MknWEAdpLX=N zPDtyd@JKT0R`wm-raDU~gpRcT+H0|Y&%bTp-?Kd<3mpZF>#Q>C+BWkOc@r&GucgB( z{J44bLrO2XcVofBa5<<+Fs~&!Eq7M6Bm82#3p2N@yd29`y9|e0a^95>D(ek`h9kl< zlLmbvjHx#{+qFHJB#y}9-TVnyUum_|XKxkT-T{FH(r9d7l(m@FAfq;-XBDNWduAW{A~EfQX+_8`NoO@r zI5|NTWvyMN421I|B;GmG$_OSU@>x?_GuXfezKa%8jP)HI!_w++L#wwm5rReV&#@*JE#wp33elZd|MmK1Xt#jD^%vvVn;I z)aGLeA>iJCFqA>ec+c1HvsK2s-tY_JUD1%E5)%y>QP@76<-^RoY$*E$8#DDoI)d}| zC;1u59+hp1IZHN{-}_WV8|R!!r%!(YfBXsna-x=cgTLE8p$E32?Juj9SyL8j0(2 zO_wLKX}5jpeGsxD!Y}baLvr4+&Ve59G_Bp$S7tX&@yf{_3QU6UyxlB9Reox&vkL#@<6QL$otu&&@A~1k=KaGeNPaL7e_NK`ZwE;beJ8VgrXQxVbo0F;Qm34TVRkjSyA|@tBMP?)Bcbb+ z?I(33)Q32df6DLD7TUr8H@fcYzeRr@M0OA~zfM`(lK@@h(lw#*avt*QY)}w3X3iW% zXf6Yr{1F-1tOCB3V}KLjRxi@004!jjl7E>?R^KZci5vfy9Cq)|CJy@x{? zI*I5bl%Mk2qWGS{{EK?Iu^_Id-yUmKRPGcn?u{#J3Z)~rjUIoMZGKjls#bh#aqdES zQIMDOBl8+dMRtE=Y9zVsBp^PE1Ds9hxgK?vi06C&jp8oxmA^E45s7cTW9JMun0E3@$-r44Zy>5zLIBwf6DH)0qeP4H9gl!MJ8E)1Ynlh=^tglRy&2`0E@w(|{}*q8v{m zX3a4U?wVpuK{PVVYH=OtPQ#^X9q6$og)%`X$!S99K1Y(!`oG~f5(+_5*+4>Ko;p1I z%LfG(*L{G2>iIuB{%ItJ%wHe>CMo+$hKuI#zMhVrDvA>AExDnmvVXvT8G4@iy(@Xk z-I>4`U4fZ2xKQD>uPCP-%2WmH-BvYO?NFU`VUR?9^XFEl&Y05%rod$|KHB1NZrKY= zjn&-#s(g)l{CQLZ21ID1j~g?(Z0ScIzqwI;&#U^6$NxU(8H$Tv<6YVK!`-hWYL@5H zJ2lF_9%R*gM^vz+8wysv&fjP4?0Fp&rnFRdP?g8>TloCJ}z*$7b z4!EM|tzf8Sc{7?AM&UMRN(Af!a7=_QCQ}3G2UOh%k$79OCu+{E*PB1ML7u!|dozQR z)H%!1oUygQC-g{SgWaU+(|#X^t$`Zw!1Wf;ShaFSPVA)8(MitP0?D_s_;s(#c^Ebu zl@tewJ?oNlJysln95HEUw4f;I6II)U7=etnY4Z4{T%Yv`D~k+2&n&7K**LNWXpmmXk?I3H2=n-euD|OBc5>DS!}PQgKlI3 z5A0&OlbkPetnWFm1aY9D=gm{$K`jI6pQkdn^oonaIj>vX4lQl00YTD9h zWn_n(8e@!zx-pW(IBtSc*E(wEpPs(}(d1)C`9F154J8!Gi`_|}Q7&O(C*FoE<^M!cOsIZl-!KtqTSN;nK=fIM`K z(1%^Ii<|@h;W!VbF(5KkhLa zmu(zcwNDBwflv+|&KfIMXVTbp$S*8?(!PPG%zalk{F0_GoWMTuM(GzME$-&lpR1B+ zxmPeWjMp#SZB_D`i(=bUwS6t@xCe&4FxzJ1RifgJS6$^lCu#V@^~>AjpRb^Pk)0sC zZKLC;Qf_?j*1^AUzJr`f@}1KCyAzOa7*yzW)!)dPU1+9={WU22mm~$W^`>g*#kgOx z9eic{_{haLMU_N&+18g6Rh%E(cUusCz$ikbX;6fFTT!?e|$ByukPF6B&p97*Dc7O|NRpzp+p?Y zQ}Cy5fhgU9vsX3j;!A|&D@PL1;$ z2J%2+g%MB zV?;}5>(|!sl47pai+Vok+gHC|s$0G#?zB4x5uUhgmX=}zIWNh}PuX{{1`aHM`DgjW zRi4-^8C&Vwb?dU6oSkXEei|gupuPT*#2{Xm`g8IXj`6A>dg-Tn$I(IIXAu{>W*(r< zdecwLLpTO%wS~9m^4@z7CH_JP7H%OgM79}^EzLg`>4w~+NyHaIVNHgCx-Np>_f(j< zp(e?>mXyul5c7il9SXX}zs3j250yDEhiG|tL|Tawc@Gc&@)5iG{{ryx-xLHXn?VLC z5L?85ueiAu%4X1p1zL_}gm*DY>TntR1ZnaqbE6Nb*C4(8jk<3`-X)e>!a<_}kxgvK zndf1G^w~92iKI8Px(Ot?6vKKVT;Vh7Uk zM}zn{O_Dx`yY?n!257+wGM+{A?Kq;Hepl!D+OS^YicZ+*8lLy6>jBr#i!LSeKez6) z9;xlVZ%O1omDhGgL@<5xek8M=9n<{o3ou6**E!9;`*^xG^-X%W(a~9EF;W@>wgU@5 z!#J6OD|iRm>=3*aT=ecPio!2pRPBb~L~?xqW`(y3Z&9Bt=64Xu4!ba7rLjtXRR9QS zV51m-RxGnpz>oM#0Z08mO0Z(d+USDINL@=>rj&}3mMPQGkYz;cKflTd0%SVgJkV+X zc`-^Z13_N?)p33RWDTKcF9v9zx}%Qib4vs4frdwwXep{+{G=X6<@k9KmAo}C% zO3q#jFvEY?(_T~S6-KN%1Lk=I>>g-!gL}N$A%hkegcOu=yanzJ zrMdu_tCvIG8C?2gl*7i&yZ#+ul+*AOn3)9Y-CiXwYA+&*5D@h ztr^jo7i~o2e3B#Oada1~J5f~k2_~IJY&RBfWeZ?q&Nt9-bX2r|e$c1D)$uUGrN9%3~1ZPPnXC_N}&4 zU7Ik%F?VWb5ZgKwQUZd_VtR7I^pvg{d(K+h=Z+6lZD0{&Ze{e1_|t8v>-KHG3Vcu} zSFN{&Mpu^HQ2@X+0ZlQvouq^dT3Y;aG!%FId#1uERi{!dyPJ91ca}jKWB)eljn$)(oX+;Dx+K;%F_Ee+(H+McfAoN;PN8VgXQqnb>kKWmN?Ft?B1BzoU`)5yf{ z?SI`_O<#vWsT6=Ei){sbmK=(8J{^Di@-aQ|F{Bv?@uV3;fs|C^=1goj`x6n&jS8}t=cB&) zgRhYoDkSTytXt?L9G%@0x1ti4;B&XCvNf9b%8zs-?!_fAF1F6rK|?xjxb$=SsSe90 zU&O$lZ$T7ew0V5A=_&@;om0}?=J~?PnET22axUX}*+_)j<(cP$VZ*d8P%4I4F)?s% z2?Y4Zg!Qxq_Hi(5XCqyY-{ZEmOl%nK*~fp#GS=4qvTu0UNt>~L%c$hm=5=U}T`9!D zoci2#*BS7@!fcPxd^N6!`$N@JA6)p*H|s@2U~7)!e0kB*E%t;Z)ZE#fUi5BwI36Ib z$+&%Hg0%UXl-}o~GR!e4Bfbo-c^2oBWS3Vjaa!T`qeq4Zu;rVY_ZOF&vEWzjNiuXw z(9ya#IM@g1ruAN$oN}1+@sTY|atJT^d|%1JZbY+gF{f2S)|}~l^sWlXX95D!VO#>s zN23AlX0zPQxK-MPo!E=@XLSG;mXuo^%$>`x5_Qc>pLOJH?L0+;gGAusw0%P5B|NY? z?U=CQx7eQ4J+|(2(U6c6wCz*<0ikN!=@y`KSlvU!fb3vuoMI&R4tjea%3@7{PDX?CYQ1u z69~MlKaXBy?}3F~QglRvtA^}aTq&U=!(9%$GIO*^_m`rA{E6dL(Lh9s8(&t=Bvqvu z^UKETzSv}NfsbpRuf{)evE%hftjUB9T)~D7xxJ4>F)gIb@MT2dVY=;zq{>FbX~NH& z9diQB4t-71P7~K%kW>b5P7e#O4yOJtY|=r=+Dil#L5haJ&?UguhZk>#I)bSeiORZpta-fH1A#5y|d zA*?Utq)&wV6ED@^;ltvtuWh1%mnoy?LR1X-N^0OIWqNdEau+*f-zdClTYPy}G-AKn zpZFd6Ze0ALt#av~%{0j~(wn^sQPHQEz85mw1^SfG;nhXI$-EMLkk^egRf6G zp`<%2gF;j$>Fn>)ez*A4>x%d~sao03weu_O#&R21PO6UWU>0)wr$3IO@=SZlQ#4=| z@R0WH(05Lc9Z%a)%_Gkr*C0bSU3oZjuKn;!r0bGeu1X~iVGkOe{_6~f&K27qerXDt z7dYw|9%OS=$`dV1n5;)&Q1vL=!Ex+CD~OVIy}`k2LF%;dq_oECE;(2a@U{JMhratk z3$2UoSSoU;(^$OnbuUSxKTI&Px&B)OS8g@BhYF6-t?F_;Tkf{s z>5Hmd($|BL>py)Uc}x@hN(nGOQMat8P4Z*a0Ce()*G{drUWTl)_=lPE>U)N>Qx>n)h)@Ykd~+ST9o2aZp^%xk$Ptt4gH7oQ^c z&N99Z2V3dSb;{@v6HzjUx2FSw0~H9i-9gF->|hILvb7Tkfggh{M>x8^Nnsf*nyv>%Vmj%cF?DiSJhtt7LH z@bJM_#SmSIs7d;H^COoiZ$MBg{L9XiU8_tn1~ePLcaiCoW8pISQ?NK=lI$Id-!qpn zwb+@hJ)ixQB!L~OpVU6!f7w*yzP9PE-20TrWy4IYvF5N-@I0PVl4}_f8Q!h1h0FpA z5Db(Izm(x{M0{2*bJpz_y~D>k?c)Y1g`bQAxP)K^pJAVli^1f$mDR?)y9XBe4>qlQ zmZWh$hDtd6+Sx(*b{oC}=6D0l@3ytxWkkdJdp?NifW*m=r?)h=EK7kO(h4Epe%H$? z^0qrOy`ap{C^ownbykh3D);S8Ba}j(oV8L4dI*iCl=ikSyenkzVKOD^>S_`I=l&=r zU~{?^yUv`J3rESE63$rV8x2z**N*WuG_>oK4BketY04Lkh+oF#)YM?$U%Aq0>;0dg7cv;WNP=7H{< zpH}(Cl`2O~u`I^B7SasMed*z4CMcET#v`s0=O%GX8fj}kt~QUJA~4wqw5`)X`H0g3G(HqBuF zxgBPJSAraK?PJ-sw@PPE(>~z)qjPS|%o(-VJjQSjWmdQSwVkV*j;a|V7#;nL`KoNO z8}118aq6#a!Lvr-F}bB(lKy{jqlD(Vbjbf za%;x7*S0*Xp)20W%`!oCNVp^m4NXpBoM*l6;@sj5fjF{^rK(bJZ3nK|8)zhYWslDw zInQCh8&l_gL7SSgm@hJnI^LYJE^l%Y(5r=~#eDOhAFD@V(=0u%?7>ba9MN)AqiGTS zpv$fo%%E^=v=$Jwk41G0Ys7W*h+uRX_493@fgZq;e?6?*v{ggPM2y|)-HWy-cbwzhd0s6P=WnO;BUvxXS`JJ%h(E7Cpg1BlGOor4T_HRz zu6`V9V)U?ye1CA!>>Zcx(mOs3?kVKCa7IS`(pTwm zQbKZEZ@seIK=xIpAV=#@4$;YEbZ5|P*IKltr+V*_3k%G>O1o|Zg?XZona8Po8f@68 zW{>#NPV}9kM3IR;)q3&c1pjdE(ObN4Z-%;uUmFBa7DXyQ!)We%(o@oFZ|;Hfb9>gA z)jY7s+;e$wB7+Lf&?F-OI9Hi_Qqg>|{C#;$`ObWy=pqd+bXE(ClcDfs;r3y@#@G}y zWVD6OKhVeu2o!won*3dkQh>!(+_meAr#t5$h3*ldD5nXZ&t*5*oA~mT+Affxuci5w zae37;`Y|0q`{0d1A1h-*C768W=+;U!ydSH=9seP~U7JAxj1CU`hy~S)8H~`XlI$Xzka8V2x}>Gi4|sjSkMTWS3!P_nq zZFIVt1w!bNKQ?Vth+&K~o+53FedjPGMO`Tmn%CUwp5HnL1jZc^x>0!*b=5gM`ptP? zIdu*`N2_6a{T zm7p6GpuJ&pSzVr)acgy_Bbcm(!QV3fKr%DoP(&U>JM*{ngOu+~;yz_RjWN2J9`peM(c^GupPEo+Z?& z&-@;%1EUB#s*6e(a(3s)d)Hh3t9>VxJ)_%`OOqz3>67Q)@o79o;X89R8NSP@E_&55 zRXTH+1A2gOgIUS@cl4&pX0gnC6&!D>87t&Y4S0kO5bAhBgVJFn;fzM?>;&yKl&@nr z7L8t}S8BLB=AHlM4Bd~&tfwv3o87f!eR5i6L!gT6=ge`Q>mr!2EOXdI#=dN{RxPib z2uAhRc}1%9Fyxe?Zk`B zdtI=H3aJ?DkFK3lTVKBPD8*A%HI;^Q2yXLRR41oqovNjRT2DK_+Ze5>Qpv}y?o7KW z{H=SKPcHCdmK^UA)AOAvX8>At4f0`r_Z$A78o!VI+3832Fh$N=>>QUY5cnx6GXmF} zS$buoRLA<`2RHr;wU5m0nV2?+Y1flZ(RtoxFdU@12xyeR*3Xnd+M)aQij~$e+FW=G zxzqsvC3OmF`xH5zJT7Ro@eOI!u4S|+RLT7z;v?7=t`(#1jag8>)UpxvD)rikgn$Vu zQ1MEx8UwE!&x%Q$u0qi#gz93)yB55A&meG2hvD8@>*X8HuDZ`J{6&n?+l}aZ!ngO3 zFe81~DK5NvjxXmuz;PF8qD2=M8%PIeEvoCK) zu{$se)ooXlewE)I(MQed{CYl~sU~3?HfTttR{V1n@BqI0v&}7dQi^bkCZPXb9swu^ z%0F><4|AX>DDQ1=A9%DCcA7^Rl&3 z3k{+V!XXDac9Me}<*@L)8Y+RoHlacNp;Sq!@nH@W0g+y^jg64ApB~ePE#angWp!he zeWfy<{_E)Lcl=q1^h0cr;=~A4o8MNarqE zPtJHPBgmHeW}m~GYc|?BLQA$Sp|8QM^&_xPpE!+l`%9*U&5TuJtM)ufduAb93SbJ^ zbV+pG)I#1hZOCnypLw4|s<=yZ$^!R@=y?##{BKE6ey2o1l1vzfC4hzb*;F9}a}yN! zTLxda^}h%3t|LoW=r~}?cu(C@%;McJiK`#0npAmpvwsYp8@zq!FB|N&CckrRzv&ALj$pagy`}}@GH~vRob|p7ju})6PhD0+ zLX0eu$#%yQ9B$O2<8)_M=aZny$W+8pR_aS09i^^3Ls9$o;|M;Pn$5_%P>dmZn)_=X z`!(c@ePdhQ(?0F*pky5Q^)|aW(Bc_4CX(y2A%7Gi%g6fnSM6qXd*8IuF@J=}lW(bd z*(&mY8ZFW`WQ-hgu4Ll(Ga)gsx@b2s^@ zTioR@+@TbArxcXQOr}QmKfBmJFuNecxT!XlyTLAEkafi2SC{}aj^98wK?nkz@jKN? z=nVSb`p!VvV&;`sLB;RC9cYqrpxi2Y(?^*b5}<@Bfpp1^hdE9D%zY9DQ@wvg<9{z_*U-i}bgV*R)F#H0Ql zd-xBY+=&_Zv;7;oGa6GBb@f?qVaEejsUUZ~)nRMSBES#MM0tU53%al16pGkI50}!5 z@5V!bx#v&Pn&|}Y{sEYCJ#UkKETQ~DDwP+_|e@R$-TaBQo;-`qWfYpUC3`SQ~1qmhaW8frRKq>pMDEDVr&&p2nbh;>chn zot&H*Tq=~5lw@QsU|q|_av0iI}&;okRrV(y@PmiR#P^Q+tc)1)abYWi0P5sn_9_!uOz^X*YUl`1vJ}Bod(5jI;Rm8WKu`^8= z>0Nv6!#wnLf}4BKp2a=<8Sz=<<`xU%taMXWurQmm93;Bk1@5Xd-GlKuc)>Kx>*XTa z&plm}!Zf`C0*=K@)IZLrA5Z87bG^b3RF zf}bI{AY)Ud>GwvLUh!ZsHP8HFMg3cq{pC+5EG}U{DW(aAe)=X!?X)W+X+IS23Z}t}@s6_AW=}#?;M12F~X}C*>3ed;+B%GE?3=%p#?aB5Y6`Mds{8I76 zqOemj*TMnn&Zqo-%VZc=d^U0mS`!89P2y8-Q-*6@r532TXaaCVO|6bqr-4NJFr)>Hl!?qn4bLl7HwSdVK&{^u( z1Vqoc&cPrh6;a+uFP;a*#yM*v>as+ZiUZUuQnwCN8-0xGMjrd_@tVX3OX|!a=30YS z=g6``EpZ7=pTz2E{Un1j^Ss8L;XaD7z~dMx2O%P>6pK2<_-NkpO5+m1l#_e`2rejJ z#RvR&{H#-$+$?nATTLTFpPQr0LH}iuF9GVIQ_V+CQaq3zsLDw}eJ04wdNOk~rH3=S zN6V6pyc?q>Qr}jse4NEtwdPVuNk&@o$-+IMb%MYYA2D)zdkw0EyI2gU)|6CLA}KCo ze_>!$Cs={=I<(N77<~!f$&Tu1vJLa*6{Y-3fjr-?%OZu=G+D!*jN5%N2>^*kJV-km z?5rAIOgA;=B9#?f(2*uhR50#NqUON6=RC%!CXN5VD2e`ivN2SMnQuo@)4B^u2knQVIgO$pf6ZkwRZn)k zrJy_K^&a7!dx55Hp@P1R)1LQtB`~QsyWv$wZl9KXoxm!wVtVaw+BSQPHRF2H?fS1X zTD7+_C*_>V%_`2wzN?I&jjFaNoYn(D`<(Jgx?^DblB2Avlo}&y0G)s+{90r&EniLA zGt}n##*iX&auKr36r5=~Uo@7T-kuY^Sd*TZFmyI81U5KX89>ZtpEu1F2T7e{4I z#D#o^s2TaxIMKB|^&2P^h_=u;rEVZH9C~a=AP_4-Drh9Nt~YY6{Yz}(ueVhlk9|6W zIa2Sgt+Sy9W|e$n=#%PK3iXpOjA z;>J`C4qf|qF_15PN5ky9joCqT)p1wvw5+Km;76nY2hDIqTg z9?U0hMBgMqt}Q9v#PNlp{XJYizBs~j%8`cB{+~)PLu=$Il^^l2^wGoM-jC3Z?K=)i zkF?WzE&0v7h3h^}5gkyZf=(&#)GYc;@`UJH2l3?GF2F?FoZ(xwRDP4Dem-4hmNwV? z5mK3+CS;xl5}lS|LEGtyCp$1zuN+{EWC?hzt<5E zWRZ+s1FbGpb!&h~?qmM3I@nVMshjT|Wh`pMx68aEk5P8rcHT(;B}%@0L}9CM?5Ksy zCaq6vL#hSPZU}PlpX*IaY#vxA1qu5zpIEI{!ta~XSEc6#xGjX#?!szL@Q@cc`&{2K z$Na*Zxr4W-aRS=xS@e1}be`CIJw>qb++hK8kRQ}YZ*f>$J>f4N(uDPPJ$77=QpYk- z#Bv&Y?pdW%Bqk>kYnwH)^3}*51MUzW|j*jvt*-PAWbJ04_jgHEE^!(9zW z=qPv|Ll-92|LjWWvh~{1@GbJ5UE{OMn@km69m!hdobumE(aQPEzhenO_#$z^Bb@49 zq?mYDOLpmz<#WOJX0y6p9tykY$j|!x!3WjbUb#nBRp;7(!&=9^3BU|_f*(*soBY zeAHokZ?F;-F~K8AX5B${W1uxhk-DH3*F}Uq_bHo0s}}ioL%A|F|Mhg*8dZ#*$P(dHtOA z1D^Hc7u6;>p2#y^EQl7TG`{xC^oxwjp)?;9Mlz6;Y@W!WM(KxD83!DL#`#N_=}o@$ z5M*%>R#JiyVg=MO@2cQX(C$ekl1#9z*F0pdE~kgLOtMDilAU+Y?}$mt(|(UykDm}k z&Pxsy#>BT+vALZ3M9P(s?(+woe2LQR2D228Q}`p3X}ue|u1+CuaVT}1jf&3xRggf0 zLOABNxv(sSM*bZlJKlKfBpqVORF5CTzkn9_Nl%=dm4=pctkBfT6pl3V^)xTgIaB%0 znLMQ&d62)Or*;;#%XAGAuoQvWwL;Z4*PD#9WR#hGfZzpHFWHb@M8YxZ_Y^fWEd=8` zLO!+d6%k_(N(E}xQ^}ns4V2mk2Azh=;yR7(LlPc%0mpZBNWK-Y!7?DDx}E>>3b@~v zZ^66)Ge3P05iXrs@F`;Sr=K=-T}h5bsrmoz&WASpKgTv?M2I^~K@Vm*PIv`|qaUE_ z@#!tt`S0=5h7mp}sxnswSh%c*G$#k!iDImpWvtmK_W(%ci{44FX?~&{$)MoqBg6?cdq|?lvDt+;d@Q_Q!QU)Rnew7SI2~Z7FAE(lqPw$#|nC;3OOZfYBe7LF` zV&a%$i!#M?-k=}r7hUg>**g%dcj``3u_5T{QI4A1U~?Oivv*#6p%PrZP1(Ss-lG>j zaXW@=Z8#OF)9Q1@7{~t{J~G$9@rtLMY;Jd48xWAON?%NoJo372 zwq2WHY1J+^`Ni7ZSPbv$<2O^>-6Spr%F$6%r+AsT<;|evTnCB9lR8H%^hEisBLdtkd_bNy2bDt}aOoB#iU*V+E}N)rH0T z82X{CVXy7gl#iJ06y}qrWC#XF#x9s8$2CSj8j)y51<%L4Rgff)x}4|=)9B++xWf_$ zS?shzhsjsXbILub`r^#-C*CMDKHi~gQqFDkHnHPqoJT1nG3XB5%suMUFT*x3{s~+h z5|-U?K2O`hNe?d*7-wq8Q^pDwZ3^pyGuv>IBsGmKtX&^VSAK%JpsNW&nvF+#Bar zFVLVmLljs=lP#F(WbGO10|k($fwnI{$K1)OPx*@x>NvX>uI{d*xhmN${8BLgrQZXy z&@0m2O9D);arw~MytaRn82%2M;g2sjO1aD^so4F7pJn~RQ3W3QMZunDct)Ymyuspc z#~sAB9l%}TR_PZNr=SIEDak`KGHo&Xh_;pej`^Z>{b--phJi3Djw==;dmjXHD~8yq zV2%o}gzI3G>%pAxYm|g*w1&jcX=Ej+MYN#M&c0X751jY@)|%FLl3pq=Hm7ZPrJ84N zX;9(#^Q}8|D*8d&7rZ>So==Z$()|)|O1psSm^B{-*FD&t9Bpv2FZNK$Jc=S1KcDY< zQd=(kK*Sv&*qD`ljvg9Epp{Z2ffiFcAol@nf{7s`)mbA`*pPBoPeLo17FJ=rn2@WN zQBY5(D2sl=Q*rThtyuq&J#j43^r;(*3zNn#LddWp`c_0ngiZ?OVrClCalo;6IVz{V~t+yKIow&#l>YBprv=i&Y~a zmd|NvSMe0)^`uC@MkqPTqC=s{AwFq zW4aQgy&j1Yn;^){_)uaZM4~Z-f)|GBOBiq|asr>`%>5*deu@^uD6-Zvig95o)&KNa zGfD;5yhMOO(JM?1at~j+vis@^J6}F06U4S)OY=s`YE$h zrFiItQEnV>#By(&QMZ6V*s{tq6=DHi8!x%=@bpgnO;u=Ci7#0NexWLhoa;H>ZKGQW z;HG%D37)2EEe zHmSk2+N{?Bgs&v_lWDu@@I{nBx<6VCzLSwN9Ht%0f(L#sm~wyWD-gRL<0LUlS0ONK@BVE zX>Co3C}NJ*+FI%<9WcgryzsLDW@>P``u{gvr```U)TD%6==e|MWF-!{*>wl|#Dje( ziGag22SC#~`;bgfqJ$d9cN}Ux-~fc&pP?9-K*YP5?Mm1^lW4Bor&!EK1G%3ugV7k6 zKzqTxY*!iSXWKkYQLO`f`0bU=XDd+-Gq_OF4L)?Q_lOr=QPzVzCsw7CMw0&DkLjJB zHds3ikrjv_@c>+GYf+#RqH%(YJ*^s5vZ0c$+CNJ*aD(BKyoQ#(w7-|j#<}G`DkL0WMSRpf@>D+F&-|-c@U3T`yPqwQEgFA=btta%1gV@?)Uur8PnIefQo|;JBTMj!WdVPWF zVP^HL&qm)uWp1SqR_FG9D>Y_0E^a+qX=e|5G`EbEou)%q(6ZpS}23k(c*%rA62sb}KI z*>u0gQ9e2cwnrba6i=lx5rS#V?0^>abj^}n+WiR;VTPbw4RR5A0Acp$wsp^x1r-0TPVdT#0wQOcRZ;W z72eqv8J+t<59{p|yn}A;quSfHsOm2L0!+AzlXv*!AJU z_&x918;dW&z>o_<)6NxS<+LY@yeF88IqwC|=dQj8-K~2S?{nqn&ym`DwdLTu$88mT z{dn$@Q`n`%JM!nT-PJ`iM;1H42i6~q5=uevIEHpUCxjP^T0{YIv=Lu^ZDBjVrwI&# z+uF_LLA8DRufqIFp&`agMEr_r1YBtt5U^sWMamM z#&|`6kW6~W{7p4=-#yd~N^b5;=t1HKxXNugVwXBvL(`Dey&Y3}9r&#Cd=DOu5( zcOP*(*kcC2j)laZy7y*^vyF>;i%VhP9RaSGI>6fK0y$!EHEv48ign53yqAq9i}dyC zmZ^hTdkb#wZ$}9VyDJxUkA|GH>p6CfGUrV$2J{ghVp=#+NhMkw6M9A7|EuBp^ZwZ< zyx{H+yKsD+gpYkobx7{n7b_=(@j!SHW?S0eobCVAys))K@NotM0{DAGvtj!Xm ztV*)qNcXlso{VvE$$t1+te;K8awgvVtHAw*wsUTw(0DMu-U^15?vOczM3z4PIYlP| zS2p`jsdo$Q`>=06N44uB;O#%dt1{0pd#&K3P;|sbyz_e9CYacDlu`mb%#VrAPlo%#+h>9 z!!uJ9X^sH{cWzs7*=)~D^4VqizC@3GlRtrN1RRD5YjG>(^>*JldDaQYA!7Ly5cu6~ z0)23&Z#klNSLiPAFh)MYB!%FMHa6i4^88j>hHL%8G z{oW~-6~NK070`rC##!a+y%$Y>U#^%Xe2nG_Uiz%x{&=5Qi`vq#*^Pp?^}KUAq+*NlhxIRZ4lv&pHh6zEd%mg z5=lKo)(gY=06CziO%y(PTfAA%A~5u67c z>D1A`=u#-iUVwZo@%vNzmP%!9RKX!gn{B7|-rqwSP=0dH|+5w7u=jLWwHYT&BM(k@EmE`sL3^Qzm@ zM97Igy!M5>+qfNWd)1@c-J{iDBNXN5pv7rkGZck6u^%_?3q=>tevRcrP2g!8Dtd$R zxxS@wTL;N+;qqW)bXw`xtY6E6L;wzJD5JQ2vhCwl{nd%YFRmijZezd+t}@9TCXJdC zW7ABUHKxZ^E?GcOz4MXACk09s?k98gdfd?UYFfIP&$p3%OYqiv^@bzjhGOXfdxYMC zlGt0CK>rfMgY1!W_ZW#KN=}lcGfkCRa|&<2oYoVeT$Xt09oKE^^==$?;pn40o;jb| zr|#LdNBG}x?sGm8Kcm~~iFFmNy^oMoKp7Z){ya0V(WL?3JZZqhe3AMs4VA$p4Z=)S90)x8N9l?!bgwpa=;w4Ln} zKbE9dQPe0|XmkoZes!|9p%smC)cct74#`(Cc5v{17FPN&e8j6k;qlwfENoG|=8lGM zFBX~3R$jZmzdt0Lx0UzybFFZ@vc)_Zo{YtL@P6XK*VEjEkJZX$4Um*vk3=e&+ESnC zgoG~0KR4$GsB{2jH`Vw=rnUq{K0dtASj%#A0Pj+Hm|+mmgEuA8cG(8YhNbcj+4_^F}8((bwl&? zU$(cN)8%yXU00nvqQr#v-;JY6-0rNh9EchL!6n&rLw=aTO~MYWF%^WzTs{`(Z5bBu zdERq3fpYPS?nLGO6L1SpbuN`su&*Dp@9ryCVXV2G7`Cjrv&vfZIso_Tm%b}Putny6 zg@|vyAUpMRk*o^}u6z-cqBE&+S+=RqvVWwj@&qB+oWz_1LF^hlBDU9r%kUGd-&`cb zQ!2rjpf^!*nh;dWQg;IO);)+;vIyYtD7njS@}|7&0La`7Tk(~FreGC4GFaeM({%`^ z3gBqBq##*QYglN?YOZ7b@cq0Bi(r6~@#8EwIVY>8@|Flc=pK3aWAD{lhZJh0;td@S z-M#%H@^W_rh;ZI1c+r{bEUf_t{_$h3T!<{Wzk)$wa4Wb_0*YurE{bLq4ve~V9K{DH zPmzsshFmT8o6cN=9I6tR6>$Ym_|cxXb2NI06K) z$U4=yNn|Db$d&fFAG=5(zgtGXi<&TCH4tEZMuYm%Jx1p3+F<`Rw7fCE53a57;*p$fhtVhB^mIy3 z*&A(d%FZD-qcn8+pB>WB1FdtIy_I~EE`?;LtoT?6E_!XW(_8Jp?43+c>!Ris8ra3j zEZqbzj^y;J!d%e$NRzMCU4Zl=c>;w3?zZMP(ZC-rg@9KUEDKoamre+C$|qA}-d~wQ z9JCfwy<6a9+0Ij~0y|F7hs{Q4B|krX%N+*@N8dqn>XiASyT0d8?548VX6+WxIE-*( z0Fm(o1w+p0&M$ra{%g$8ELPzoi9lckCo&dF6SxdG_D2pUb%cd9GE=ta{=9)%rGk)` zfYsFS#gm`;8lh$0U&DT9gw_DPx;)~v@re-vWL|sTyr4TFom?zI512d{Z(|F!TvX#P zKPJ*>t?{Ec%w>gAZt5%L33`k_1W`|!3W>pHR#0;nQWcD@cyW?_pIL#VAReqXIE3JY zHz=3Wf&I5xM%CfxARB3QZgU6%nLQG}Iw~x{N2VT8)(xHtV-LJ~e5Ka({Ctk=D`Jai zdPUA!3R7Ke7;|vZ4V=V@V}mpOE2poJ-Eoh7yKYWr(c*pT=rI3|=u36eYNet@!`r7< zsLswr+?I2Di6HviyMGz~%smK>@{ApOwzkjMQSkE9^I6uuhk^B z#3yHv+rD^rj#p4+GjZKDC(o*N5H|ouDLTka)rs6O#jK77d|iQ3g10F~uS6Z#=I^T% z?^3^1Irt#xdvOG@JovR~S@L_&50A=ZDuFA{y3GUXjPs}W#9vkAOz*$w zalP|leOj%Jk~Yj8D=7stxVGY6M+@#++ou^^UhhK=r&!^xoa1UOpu-@{M8J1C1DTg% z!7l*s&hM1oKGlo4hvl&mdiQKNe|578?FUa*q6 zRUl$Jg(gJZ=%SD9y^-;nnIh|(WhEtAE!W);~ll3YyAMZrRiK^dw**^PAbOF2p_?im1A?04vE)4D? zv<*sPUeXPDi=B0)N8Fj;nrS}OC#sw$9=UcYgDV$$kW=0&uL!`Bv|O)`&_AdVn3isO zVq=u~C5$iTLDFplw@M^;trT_6XBXdHqvvnV?W0y!vzY_oNmR2;R2#nNlkRhFmg(3V zRR}ASOkQzmr#VlBr(Tag%`>a~(j2xJ(Kaf-IORG)zUkzjFpWq*eNkpExir{_-=Azu z=e{V}_qQKVJMg(e)B zxD_d^3nK4*hlZ~hnv+u6Iw;n_RpNPGsOU)l5hpdp%P(#&oB5tB0!jkXA| z8LHlafyZYqjE9381PENqRW?!6~3b2~IyJudN2Y!A9gNJEbzk7T4J3%!31yViE z{=vc?><^&&-x!Po2mdRzaPn zCXR}ntVxTp+&-{?rUoaX^cf5@Al5lq1RO^K#9#fj9*ee%rabs2K03Sm~00TV4_Dj zZZGfG=tAY@Q`AyC;d)RH0WY=4^yN)*;Yk1{XzB>P`-w1FT<(wXITAS@>G`v>lzi+t;II3U=x=4HI*uYfFMY-CU?kT4QR6x3fG#l#-8F&y(5l+Ccgg+`ex*m@>0lAhk$`6c zUc^}GQ8U)v{vnD$1~@i4ulFJug{h7&VcAP6g6%`Hi-FekYmr!Y6ok>IxIfiTc_e>^ z25yojC~Ea^0?(N?LHY~O_z-o1$>)f+4?-o7yp7Cjc&paGe!^Y*WbZq^7x<6#oQ{rF zNNN&R9_}FxmuCp)1SgPW6m^|sF#y*gSbkD|wMSTBo~j!}V386!z!RUy2Y7^kXs$Gj zvzUxeyKZg~+T@~y6I020WWKO~SsKsW$D19HmJq~&Vazu6Cfhg0?adv6<+T;9YIli% z(KCaVu=g=aC3n?OYn+TN<{!S7gOeBGY}ZBelC&ExA0<+1oLn(?Z$g1!1RLOEQu2fr z-%xJn4f2ld?_?%FK=YjKY|eWdrNmz0`V4fSn>wJWe1$GQwkAtDm&2M*vIY3J^cvS&I?+?ou zeBw8n&mEY7|6s0Q9WJJBk74!3Z8sr{O_c-4$aKAa&Iq~?mkIxs z;Qc>Ohr_)84R-#$7F639f&*^{3Npcjhw_3aBgoJ>=Jj>ec)W`Me`c0A4r1I4pukP& z63plAzP!TC6=le*tIFau^Zufi1Gy+$dvWDPalU1bZiK}^6FKEOMo{_7HV8FnZ(z>;KB-!l%4Y97SDcTB9Za%gey3Y$GFT%iN8-$C#Ln7&2pi_N$ELN2k2>dbJi>VXe_X8JJH zW5Pr&bP+0=m0Y&5XM49MX>{ZzoJH^fBZmbg*}~LC;^^tWNqpA}w@aV(zyK~uMz=y|drdw51U4qX8gy%9k<`d*XHM?k0P1z(+v=mBbI zAH-D6e$N5HPUMYcuqhR}=O9}#|0qt{j?iSaXlz!mP|R(GT8hA`!Zd2Aj4p+U@!{Lz z|3F!w!~Je}sC4ddVafk6-i#pJx)bH$HbjsedVQ60L>Mh!dFf<`)AU2c{Do)M>P8RM zU;M=$3t~4z_gf<^U=Iz*Dhcn+^64V(%2lW>B0F)mwT_La_1@cc>FVm95)ANPC!EtM zenN36_-wsX!uC4l3%);AWWhQQB4Xbww7zjPldLN8ybhWA z1+l0dRV~g~@!I|MAiq!))HQ-%q^=bc@6Q{jCR@FZ(54UHe8gy)8HeC5duhbTDLvhD zw&|1T)9G=Z_x^-$Zx%t^W&%6l>!tu_xt}cg-UZJTnI`ulf?-$=g{3`fj9)xH&ULrI z|F!x_?KgN1kJl#w?1P*oXnu2Tj*dd>R0tEFtk6w29Jt2zO%9k659ttgo%u0K6K8;q z<`P^zqD?9H$!UC}uOms!o+lL=M=8+UVIf6S-C6jP2w8pgo;cT!A$j>97#&*~i<)To zX;MNsc-IOV@by;n@I2}b3Toe5bsT0|BzfMnK4mFgMJq*qk?Tl8Yd;xH_vCM*PeM#Jprr6iumrCt~p8&w?z zryQ`I&4cSN_46t* zTuYfm%;BVF*Okzh_*|HB*xmQ4o6mlVoe+?`%a-jdRt{ZFsI7rtvl^GY8{AT*!gq(d+NARu7vJ>2Rs9<aN^b@&w{Dzs@DQ&^t_v9Jo3H$zNRqmsZ z3i!tAL4N<3I^n%_A?#&F;N(|R3^?<(ooaC&Ut)+>&EwOIH@zfN)4){z9o`O|rZC9Z zJfV-2NehB8>9aS*krh6d{n>NVQmy@jFHUF;?x@Y|?A$Es=1FN~#d+2pTBJr20Y|_p zY*|{^McpK_bxRT@%-S)~N6%acJlb%iPJ`y|Wb_4}*PDliWenx`;wbFCsEdSIrwWAB z4n|U=haqj?qsWTpwbjD;s9To$!H0 z!s{Ut%=0!1@3ui=CF=LVcQwdf3%{h{lb|{FYV^EqQl5SC!|m(eDH$FNq@Rr9jnC$Y z)|E|wOws1z-xMOC`6*gQL#iBgEMLZrrki?~MU9G>6TWfp>2A4c9AV~&4@#%EjX;xK zq~A=iYtxlg8Cm;#af5i1riQM)?)v8YXTk8qms!#DdMo=}7t3eYcEP!RNc>@M1teOu zDP&mAGu4WIm8i-&Kt~QxT@C|nr(}*&yh#obpb=-tE?q>!WVL5P`0kng*ctB`^ClN2 zmapxagTM4G^K;>re!}Mz6jGT=+7$`)GGJpTyzAR5Z>`!)9OV6N7zo+GAHB0yFxS(0 zv!)FVfHCOChfwP?D&SfKi#8`_Xo>Az&m)yjmQ3N=fz0ui6q@c4)&1o8h0a;Z#C$E< zM#wVjgUMiJka1Sr-KHG0ajw3*fQTKsPC%ZqM!!sjf_7EmH?EZ#%=u+&qwbV1$d$c< zHlBCzxQu|yrU8!jrzJty1jEsA@bI32mRWn~+mW&A+V?5e-Lk{yz^MBBb{H32mDIOT z`BuAHhu3UQp5Cbwn;@f{nL4=+3ej7KzEzEtpRn(Mg zz(F{9o~j}HUhODF=Tsn-;YpfJ9`P%WT!1T2_*ee;D?2V4568HAo{BTp#y*~y>Z8`u zaesULK0?etI;WVL)-+RV*WV4wP$5~}r&@6FmWaCEKH!Gq7+1X+HjT4UOO5r|QFP($ z>82a;$3tY*5YW>ij5-_llJW}h>3lz8lQte)}x;r{rLdmnv%NE#BxC6haXV+ zPYjm>7+X@LU%5d;{$a9|8e_c~LNc#ukllb#?J{p?$aN;{8w_Doo3<|dLm~l_{ihnk zj?4-zn#%2TjjJt%>mPUVF&yjI&8Lt0>8P8X~YS{!jD5AOmm$MS^2 zPny$r3q!#UNGP3S#)?-Rwbnc046W2r84x5Ra}n)c0^6ZKuA4l$D_C1s?+i9#pdSz0 zj?9jW$bV==;EDeh{Jud_T08j`P(NCNUyPnLeL?rn>dU2L}aEzxzberVE%h{dtGu6v>sU>*gJCi+y;B-LZ$A! zpSX4UzV2Lts)$aS7(*5WGBtWu>3v%K3F^-pgS@|jEa;TwKIsW}Osr?lq8e3>3G=z| z#zvCN_8P!@&q8A#*MGY7ZGSR(bo-p3l^ndl?wBb+FOwaARm-uu> zXz(LJUne?Kscc$UJnPU$$S|gIzA_%1WWFnf+bi|6`dHi`G2kCM7VT5frv&dhQ^RP= z_e1kL(TbQZ?*TX^$n3*E2o6vEo&X-Ra)_j_{ncHgqO-MfXT0qsPL{ChojN$NE z2btuK!$6JDs{JXl;Pp3nNJp?NE9G^%2-U1jig?-d?MK7TS=@`7=dr=-Tsb@KlLi7N zg}~xdR@s?frCasP1~mt{*<1ypP&vynPDiws;8~t>l{?n5-J_xQBR@q8`o(FYkX{9?u}}fGBUmR8w>Ms2b&`Ad zl(_A;^93f-DIyWVJNQ|Z9Q|4-^Zv^XroG(>aE@e3v!mxDF!(f}bhGwH%7mQCmu+iU zQRJnkt6?8AzI-`XPB{8;>?+58SW-$muM2nkv2}pJ7VM!_sW*sKB5bOa)rQ;(+7*EA zq;HS`hM4lNoPDU7w+ybU5Vu$FqIqg7L^u{;0;1G3HO8(@c!A(FhQI%0zw0tc5V=TpZ2$vs z@V11X>hUcm^I_bH?^lw8upp4zCh(OcKFDMqrP`xc|Es25EFP6g<7Q^xN-( zSRuywWs_ovN}Z(Bq)O6L&TQ`nxofB?&FL~a1cRALLjieS=hZb8{MvY#@Nka z{jSsLohxtFGH$QPy6t9)4S~=DFwj}-jF=;uzARUb_iywO6DGHh2h=c8WB$CH7n!`r z?LcM7<096&Tf$Y|&hp-z%x~Bf3e+i%y&SIh*jF;~o#iFCkHt zN0ce|A8tk%WzX=6$|Ir&y=6%9TtUqBY;jCQh+#dY3e}rRn$2P#qPe9}q$g7(WJ8O2 zBV~s4DX?)=lND+Cskfvf3qF>bNCcT-#FYiIgVJR{^N)uR5AC_LP$Sc1Ogi3A366Lj<| z)X>qjALL14uncaVB=6}A5q+_)ALKVdP1%bR(;XevAARyQ>)%K=E&iAEAP8>wW;Lhu zkloElY!1#B+9L1EZkhmuwCjX2Z3QsxZIU9CN#nl?ubfKPe4(T3Si|g~Yvg4%Z52rT zfS+C4Svj<12Sc^VY!Z~Bl8-1Ag>-eAe`<4(^-`ee?0M)UTHsDcEpHGgY(F*`q3ead zG;s~qr%>^F(It2@&QmezTGH&assECCXYURIROEuz=$WJrKdNrVZj}iJ*iIhTwJ1sP z#)r&;m>dABHF-3=SJ|E{uCRkSK>aCJ7PVvY(HzPT6N)h{%33b1-uew|I!8;HH0P3K-;Z0- z8ovMeDx{ONOwR+dP8mgKFtNR<1^GrY-iRKZt~w*KYBRj;x71J zZdZ@w*$!|Dx72fgvIfJ<N3zK%|F5oF=W|%xz5P!W9O(StQqiP+NHl2suPhnc-kX@Xp_b_Hz*!n?7+UEmI;v~ zauA>_;9Frl_Xm*=-r&m+;Wh6=gV%@*xKyRBEPP1uNIo5*)O?2WSl*#l*Gx0He&+L? z3VgGjRLlw1<4`aJH9F6&iFb*jWY7C6SnjJJgviJH7WXoWbL13qz}w!L$JLZCqrkA$ ziyf|VT%XEeg=xif)Ch2LDxB7+7~K8&@~NABrhOAkvAG`Ul1)9;9ClLfGFkw0b`L*@p*fDi2z=2S48}Kluq9}E$n10|d z#iA)Av2p6LdML{SQef`@8JVX?Badbok)LwvX+78ulf$#0x(U1xui_WRx7ZTUw<*o& zoKKvyc%nx;NG_AP2j=g4+oURKgcxcZqL7e}p-)M`$Q*iKsQ*j13$_m7?f-3dXc9tC zNWxU0x6?gc;l7ki=1w|i+=m>#V=G&tO^ z7kLsg7lCl=%&)7t)~&b$<|IcyO^1#ogcGoA{mT(~(uFnGLF@oUy5LZU$`Wpfj!}Nq zZ)kb!(af!P-H*pUlR~dMELgGUwzjEaZJq8whlacR(72Ml)Es?R>_KiVW!r@E(DRtl zA#Ajj!pS~q5K{3fd;rZn$TZl&3vh`Kng>17JEoJiVq>FKeFqmjrOSTAcWPn?nifFr z4=x%0Z0nQd1QEkL(U31y9jJz*qYXnRa~y`wPwhReV_ChU8v8V)w_^pOAik;**l?0P z9U4-Hf%+*Q zk9mkBrSwlN0*b6i35x&0Dq#K_0*wgaBm`0dwYZCA6XYLDa^22Z$sBO_?UcuGr=1&1 zGw_XSMyEd4*j?_K1Tr49M09VtUug}GG*1y zDB97_GvE2Ww|=fS#J$+DMsJijhg�jy;>YYgdp04mjEOYHPVk7rK2QK*^;+aea!; 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 Imy|gEA6m}Z@c;k- literal 0 HcmV?d00001 diff --git a/dom/media/test/bipbop-lateaudio.mp4^headers^ b/dom/media/test/bipbop-lateaudio.mp4^headers^ new file mode 100644 index 00000000000..4030ea1d3dd --- /dev/null +++ b/dom/media/test/bipbop-lateaudio.mp4^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/dom/media/test/manifest.js b/dom/media/test/manifest.js index 695aa1d7ed7..d22311d4af9 100644 --- a/dom/media/test/manifest.js +++ b/dom/media/test/manifest.js @@ -227,6 +227,9 @@ var gPlayTests = [ { name:"test-8-7.1.opus", type:"audio/ogg; codecs=opus", duration:13.478 }, { name:"gizmo.mp4", type:"video/mp4", duration:5.56 }, + // Test playback of a MP4 file with a non-zero start time (and audio starting + // a second later). + { name:"bipbop-lateaudio.mp4", type:"video/mp4", duration:2.401 }, { name:"small-shot.m4a", type:"audio/mp4", duration:0.29 }, { name:"small-shot.mp3", type:"audio/mpeg", duration:0.27 }, diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini index ceb57b0e35f..e90301d846b 100644 --- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -66,6 +66,8 @@ support-files = bipbop-cenc1-video1.m4s bipbop-cenc1-video2.m4s bipbop-cenc1-videoinit.mp4 + bipbop-lateaudio.mp4 + bipbop-lateaudio.mp4^headers^ bogus.duh bogus.ogv bogus.ogv^headers^ From 34161b93d21938fa001142f73e068a223b46bdea Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 042/107] Bug 1167423 - patch 1 - Handle return values of FallibleTArray functions in Console API, r=smaug --- dom/base/Console.cpp | 117 ++++++++++++++++++++++++++++--------------- dom/base/Console.h | 4 +- 2 files changed, 79 insertions(+), 42 deletions(-) diff --git a/dom/base/Console.cpp b/dom/base/Console.cpp index 7d186af3aaf..186245700d2 100644 --- a/dom/base/Console.cpp +++ b/dom/base/Console.cpp @@ -174,7 +174,9 @@ public: mMethodString = aString; for (uint32_t i = 0; i < aArguments.Length(); ++i) { - mArguments.AppendElement(aArguments[i]); + if (!mArguments.AppendElement(aArguments[i])) { + return; + } } } @@ -666,7 +668,9 @@ private: return; } - arguments.AppendElement(value); + if (!arguments.AppendElement(value)) { + return; + } } mConsole->ProfileMethod(aCx, mAction, arguments); @@ -826,8 +830,8 @@ Console::Time(JSContext* aCx, const JS::Handle aTime) Sequence data; SequenceRooter rooter(aCx, &data); - if (!aTime.isUndefined()) { - data.AppendElement(aTime); + if (!aTime.isUndefined() && !data.AppendElement(aTime)) { + return; } Method(aCx, MethodTime, NS_LITERAL_STRING("time"), data); @@ -839,8 +843,8 @@ Console::TimeEnd(JSContext* aCx, const JS::Handle aTime) Sequence data; SequenceRooter rooter(aCx, &data); - if (!aTime.isUndefined()) { - data.AppendElement(aTime); + if (!aTime.isUndefined() && !data.AppendElement(aTime)) { + return; } Method(aCx, MethodTimeEnd, NS_LITERAL_STRING("timeEnd"), data); @@ -852,8 +856,8 @@ Console::TimeStamp(JSContext* aCx, const JS::Handle aData) Sequence data; SequenceRooter rooter(aCx, &data); - if (aData.isString()) { - data.AppendElement(aData); + if (aData.isString() && !data.AppendElement(aData)) { + return; } Method(aCx, MethodTimeStamp, NS_LITERAL_STRING("timeStamp"), data); @@ -892,7 +896,9 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction, Sequence& sequence = event.mArguments.Value(); for (uint32_t i = 0; i < aData.Length(); ++i) { - sequence.AppendElement(aData[i]); + if (!sequence.AppendElement(aData[i])) { + return; + } } JS::Rooted eventValue(aCx); @@ -1293,13 +1299,18 @@ Console::ProcessCallData(ConsoleCallData* aData) case MethodAssert: event.mArguments.Construct(); event.mStyles.Construct(); - ProcessArguments(cx, aData->mArguments, event.mArguments.Value(), - event.mStyles.Value()); + if (!ProcessArguments(cx, aData->mArguments, event.mArguments.Value(), + event.mStyles.Value())) { + return; + } + break; default: event.mArguments.Construct(); - ArgumentsToValueList(aData->mArguments, event.mArguments.Value()); + if (!ArgumentsToValueList(aData->mArguments, event.mArguments.Value())) { + return; + } } if (aData->mMethodName == MethodGroup || @@ -1418,48 +1429,52 @@ Console::ProcessCallData(ConsoleCallData* aData) namespace { // Helper method for ProcessArguments. Flushes output, if non-empty, to aSequence. -void -FlushOutput(JSContext* aCx, Sequence& aSequence, nsString &output) +bool +FlushOutput(JSContext* aCx, Sequence& aSequence, nsString &aOutput) { - if (!output.IsEmpty()) { + if (!aOutput.IsEmpty()) { JS::Rooted str(aCx, JS_NewUCStringCopyN(aCx, - output.get(), - output.Length())); + aOutput.get(), + aOutput.Length())); if (!str) { - return; + return false; } - aSequence.AppendElement(JS::StringValue(str)); - output.Truncate(); + if (!aSequence.AppendElement(JS::StringValue(str))) { + return false; + } + + aOutput.Truncate(); } + + return true; } } // anonymous namespace -void +bool Console::ProcessArguments(JSContext* aCx, const nsTArray>& aData, Sequence& aSequence, Sequence& aStyles) { if (aData.IsEmpty()) { - return; + return true; } if (aData.Length() == 1 || !aData[0].isString()) { - ArgumentsToValueList(aData, aSequence); - return; + return ArgumentsToValueList(aData, aSequence); } JS::Rooted format(aCx, aData[0]); JS::Rooted jsString(aCx, JS::ToString(aCx, format)); if (!jsString) { - return; + return false; } nsAutoJSString string; if (!string.init(aCx, jsString)) { - return; + return false; } nsString::const_iterator start, end; @@ -1547,35 +1562,47 @@ Console::ProcessArguments(JSContext* aCx, case 'o': case 'O': { - FlushOutput(aCx, aSequence, output); + if (!FlushOutput(aCx, aSequence, output)) { + return false; + } JS::Rooted v(aCx); if (index < aData.Length()) { v = aData[index++]; } - aSequence.AppendElement(v); + if (!aSequence.AppendElement(v)) { + return false; + } + break; } case 'c': { - FlushOutput(aCx, aSequence, output); + if (!FlushOutput(aCx, aSequence, output)) { + return false; + } if (index < aData.Length()) { JS::Rooted v(aCx, aData[index++]); JS::Rooted jsString(aCx, JS::ToString(aCx, v)); if (!jsString) { - return; + return false; } int32_t diff = aSequence.Length() - aStyles.Length(); if (diff > 0) { for (int32_t i = 0; i < diff; i++) { - aStyles.AppendElement(JS::NullValue()); + if (!aStyles.AppendElement(JS::NullValue())) { + return false; + } } } - aStyles.AppendElement(JS::StringValue(jsString)); + + if (!aStyles.AppendElement(JS::StringValue(jsString))) { + return false; + } } break; } @@ -1585,12 +1612,12 @@ Console::ProcessArguments(JSContext* aCx, JS::Rooted value(aCx, aData[index++]); JS::Rooted jsString(aCx, JS::ToString(aCx, value)); if (!jsString) { - return; + return false; } nsAutoJSString v; if (!v.init(aCx, jsString)) { - return; + return false; } output.Append(v); @@ -1604,7 +1631,7 @@ Console::ProcessArguments(JSContext* aCx, int32_t v; if (!JS::ToInt32(aCx, value, &v)) { - return; + return false; } nsCString format; @@ -1619,7 +1646,7 @@ Console::ProcessArguments(JSContext* aCx, double v; if (!JS::ToNumber(aCx, value, &v)) { - return; + return false; } nsCString format; @@ -1634,7 +1661,9 @@ Console::ProcessArguments(JSContext* aCx, } } - FlushOutput(aCx, aSequence, output); + if (!FlushOutput(aCx, aSequence, output)) { + return false; + } // Discard trailing style element if there is no output to apply it to. if (aStyles.Length() > aSequence.Length()) { @@ -1643,8 +1672,12 @@ Console::ProcessArguments(JSContext* aCx, // The rest of the array, if unused by the format string. for (; index < aData.Length(); ++index) { - aSequence.AppendElement(aData[index]); + if (!aSequence.AppendElement(aData[index])) { + return false; + } } + + return true; } void @@ -1770,13 +1803,17 @@ Console::StopTimer(JSContext* aCx, const JS::Value& aName, return value; } -void +bool Console::ArgumentsToValueList(const nsTArray>& aData, Sequence& aSequence) { for (uint32_t i = 0; i < aData.Length(); ++i) { - aSequence.AppendElement(aData[i]); + if (!aSequence.AppendElement(aData[i])) { + return false; + } } + + return true; } JS::Value diff --git a/dom/base/Console.h b/dom/base/Console.h index 4fff166d3d2..8c958486396 100644 --- a/dom/base/Console.h +++ b/dom/base/Console.h @@ -158,7 +158,7 @@ private: // finds based the format string. The index of the styles matches the indexes // of elements that need the custom styling from aSequence. For elements with // no custom styling the array is padded with null elements. - void + bool ProcessArguments(JSContext* aCx, const nsTArray>& aData, Sequence& aSequence, Sequence& aStyles); @@ -182,7 +182,7 @@ private: DOMHighResTimeStamp aTimestamp); // The method populates a Sequence from an array of JS::Value. - void + bool ArgumentsToValueList(const nsTArray>& aData, Sequence& aSequence); From 98514d828a4eaed1bd72bd3460dad4affd64e77f Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 043/107] Bug 1167423 - patch 2 - Handle return values of FallibleTArray functions in WebSocket, r=smaug --- dom/base/WebSocket.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index b41aa05322a..81278786e52 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -960,7 +960,11 @@ WebSocket::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { Sequence protocols; - protocols.AppendElement(aProtocol); + if (!protocols.AppendElement(aProtocol)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + return WebSocket::Constructor(aGlobal, aUrl, protocols, aRv); } From 4cb28b400a2004cdcab45fb38a40975e342b38f3 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 044/107] Bug 1167423 - patch 3 - Handle return values of FallibleTArray functions in MutationObserver, r=smaug --- dom/base/nsDOMMutationObserver.cpp | 9 +++++++-- dom/base/nsDOMMutationObserver.h | 3 ++- dom/webidl/MutationObserver.webidl | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dom/base/nsDOMMutationObserver.cpp b/dom/base/nsDOMMutationObserver.cpp index 96d1666cdd1..8202b1b056f 100644 --- a/dom/base/nsDOMMutationObserver.cpp +++ b/dom/base/nsDOMMutationObserver.cpp @@ -693,7 +693,9 @@ nsDOMMutationObserver::TakeRecords( } void -nsDOMMutationObserver::GetObservingInfo(nsTArray >& aResult) +nsDOMMutationObserver::GetObservingInfo( + nsTArray>& aResult, + mozilla::ErrorResult& aRv) { aResult.SetCapacity(mReceivers.Count()); for (int32_t i = 0; i < mReceivers.Count(); ++i) { @@ -712,7 +714,10 @@ nsDOMMutationObserver::GetObservingInfo(nsTArray mozilla::dom::Sequence& filtersAsStrings = info.mAttributeFilter.Value(); for (int32_t j = 0; j < filters.Count(); ++j) { - filtersAsStrings.AppendElement(nsDependentAtomString(filters[j])); + if (!filtersAsStrings.AppendElement(nsDependentAtomString(filters[j]))) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } } } info.mObservedNode = mr->Target(); diff --git a/dom/base/nsDOMMutationObserver.h b/dom/base/nsDOMMutationObserver.h index ee6a511e44e..8a53b6bd805 100644 --- a/dom/base/nsDOMMutationObserver.h +++ b/dom/base/nsDOMMutationObserver.h @@ -493,7 +493,8 @@ public: void HandleMutation(); - void GetObservingInfo(nsTArray >& aResult); + void GetObservingInfo(nsTArray>& aResult, + mozilla::ErrorResult& aRv); mozilla::dom::MutationCallback* MutationCallback() { return mCallback; } diff --git a/dom/webidl/MutationObserver.webidl b/dom/webidl/MutationObserver.webidl index e1bbccd4253..f222a8fe996 100644 --- a/dom/webidl/MutationObserver.webidl +++ b/dom/webidl/MutationObserver.webidl @@ -43,7 +43,7 @@ interface MutationObserver { void disconnect(); sequence takeRecords(); - [ChromeOnly] + [ChromeOnly, Throws] sequence getObservingInfo(); [ChromeOnly] readonly attribute MutationCallback mutationCallback; From c89a834b25d818acf236bab5b5e0da25ef621e64 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 045/107] Bug 1167423 - patch 4 - Handle return values of FallibleTArray functions in CanvasRenderingContext2D, r=smaug --- dom/canvas/CanvasRenderingContext2D.cpp | 25 ++++++++++++++++------ dom/canvas/CanvasRenderingContext2D.h | 5 +++-- dom/webidl/CanvasRenderingContext2D.webidl | 4 ++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 39643bd5e3d..7447d7c6218 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2642,7 +2642,8 @@ CanvasRenderingContext2D::Stroke(const CanvasPath& path) Redraw(); } -void CanvasRenderingContext2D::DrawFocusIfNeeded(mozilla::dom::Element& aElement) +void CanvasRenderingContext2D::DrawFocusIfNeeded(mozilla::dom::Element& aElement, + ErrorResult& aRv) { EnsureUserSpacePath(); @@ -2674,8 +2675,12 @@ void CanvasRenderingContext2D::DrawFocusIfNeeded(mozilla::dom::Element& aElement // set dashing for foreground FallibleTArray& dash = CurrentState().dash; - dash.AppendElement(1); - dash.AppendElement(1); + for (uint32_t i = 0; i < 2; ++i) { + if (!dash.AppendElement(1)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + } // set the foreground focus color CurrentState().SetColorStyle(Style::STROKE, NS_RGBA(0,0,0, 255)); @@ -3947,7 +3952,8 @@ CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset) } void -CanvasRenderingContext2D::SetLineDash(const Sequence& aSegments) +CanvasRenderingContext2D::SetLineDash(const Sequence& aSegments, + ErrorResult& aRv) { FallibleTArray dash; @@ -3957,11 +3963,18 @@ CanvasRenderingContext2D::SetLineDash(const Sequence& aSegments) // taken care of by WebIDL return; } - dash.AppendElement(aSegments[x]); + + if (!dash.AppendElement(aSegments[x])) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } } if (aSegments.Length() % 2) { // If the number of elements is odd, concatenate again for (uint32_t x = 0; x < aSegments.Length(); x++) { - dash.AppendElement(aSegments[x]); + if (!dash.AppendElement(aSegments[x])) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } } } diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index 0488bbd4088..efa97b565f8 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -187,7 +187,7 @@ public: void Fill(const CanvasPath& path, const CanvasWindingRule& winding); void Stroke(); void Stroke(const CanvasPath& path); - void DrawFocusIfNeeded(mozilla::dom::Element& element); + void DrawFocusIfNeeded(mozilla::dom::Element& element, ErrorResult& aRv); bool DrawCustomFocusRing(mozilla::dom::Element& element); void Clip(const CanvasWindingRule& winding); void Clip(const CanvasPath& path, const CanvasWindingRule& winding); @@ -363,7 +363,8 @@ public: void SetMozDash(JSContext* cx, const JS::Value& mozDash, mozilla::ErrorResult& error); - void SetLineDash(const Sequence& mSegments); + void SetLineDash(const Sequence& mSegments, + mozilla::ErrorResult& aRv); void GetLineDash(nsTArray& mSegments) const; void SetLineDashOffset(double mOffset); diff --git a/dom/webidl/CanvasRenderingContext2D.webidl b/dom/webidl/CanvasRenderingContext2D.webidl index b471b0e1504..c1157c0284f 100644 --- a/dom/webidl/CanvasRenderingContext2D.webidl +++ b/dom/webidl/CanvasRenderingContext2D.webidl @@ -92,7 +92,7 @@ interface CanvasRenderingContext2D { void fill(Path2D path, optional CanvasWindingRule winding = "nonzero"); void stroke(); void stroke(Path2D path); - [Pref="canvas.focusring.enabled"] void drawFocusIfNeeded(Element element); + [Pref="canvas.focusring.enabled", Throws] void drawFocusIfNeeded(Element element); // NOT IMPLEMENTED void drawSystemFocusRing(Path path, HTMLElement element); [Pref="canvas.customfocusring.enabled"] boolean drawCustomFocusRing(Element element); // NOT IMPLEMENTED boolean drawCustomFocusRing(Path path, HTMLElement element); @@ -246,7 +246,7 @@ interface CanvasDrawingStyles { attribute double miterLimit; // (default 10) // dashed lines - [LenientFloat] void setLineDash(sequence segments); // default empty + [LenientFloat, Throws] void setLineDash(sequence segments); // default empty sequence getLineDash(); [LenientFloat] attribute double lineDashOffset; From f831429bbc5f1c4be52bb7d35b15ed014dd3b5c2 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 046/107] Bug 1167423 - patch 5 - Handle return values of FallibleTArray functions in WebGL2Context, r=smaug --- dom/canvas/WebGL2Context.h | 6 ++-- dom/canvas/WebGL2ContextFramebuffers.cpp | 37 +++++++++++++++++++----- dom/webidl/WebGL2RenderingContext.webidl | 5 ++++ 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/dom/canvas/WebGL2Context.h b/dom/canvas/WebGL2Context.h index 9ea73007f4e..ce8b15ce3e7 100644 --- a/dom/canvas/WebGL2Context.h +++ b/dom/canvas/WebGL2Context.h @@ -10,6 +10,7 @@ namespace mozilla { +class ErrorResult; class WebGLSampler; class WebGLSync; class WebGLTransformFeedback; @@ -56,9 +57,10 @@ public: GLbitfield mask, GLenum filter); void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); void GetInternalformatParameter(JSContext*, GLenum target, GLenum internalformat, GLenum pname, JS::MutableHandleValue retval); - void InvalidateFramebuffer(GLenum target, const dom::Sequence& attachments); + void InvalidateFramebuffer(GLenum target, const dom::Sequence& attachments, + ErrorResult& aRv); void InvalidateSubFramebuffer (GLenum target, const dom::Sequence& attachments, GLint x, GLint y, - GLsizei width, GLsizei height); + GLsizei width, GLsizei height, ErrorResult& aRv); void ReadBuffer(GLenum mode); void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); diff --git a/dom/canvas/WebGL2ContextFramebuffers.cpp b/dom/canvas/WebGL2ContextFramebuffers.cpp index f6083363c36..1d074f14607 100644 --- a/dom/canvas/WebGL2ContextFramebuffers.cpp +++ b/dom/canvas/WebGL2ContextFramebuffers.cpp @@ -343,26 +343,38 @@ WebGL2Context::GetInternalformatParameter(JSContext*, GLenum target, GLenum inte // Map attachments intended for the default buffer, to attachments for a non- // default buffer. -static void +static bool TranslateDefaultAttachments(const dom::Sequence& in, dom::Sequence* out) { for (size_t i = 0; i < in.Length(); i++) { switch (in[i]) { case LOCAL_GL_COLOR: - out->AppendElement(LOCAL_GL_COLOR_ATTACHMENT0); + if (!out->AppendElement(LOCAL_GL_COLOR_ATTACHMENT0)) { + return false; + } break; + case LOCAL_GL_DEPTH: - out->AppendElement(LOCAL_GL_DEPTH_ATTACHMENT); + if (!out->AppendElement(LOCAL_GL_DEPTH_ATTACHMENT)) { + return false; + } break; + case LOCAL_GL_STENCIL: - out->AppendElement(LOCAL_GL_STENCIL_ATTACHMENT); + if (!out->AppendElement(LOCAL_GL_STENCIL_ATTACHMENT)) { + return false; + } break; } } + + return true; } void -WebGL2Context::InvalidateFramebuffer(GLenum target, const dom::Sequence& attachments) +WebGL2Context::InvalidateFramebuffer(GLenum target, + const dom::Sequence& attachments, + ErrorResult& aRv) { if (IsContextLost()) return; @@ -407,7 +419,11 @@ WebGL2Context::InvalidateFramebuffer(GLenum target, const dom::Sequence& if (!fb && !isDefaultFB) { dom::Sequence tmpAttachments; - TranslateDefaultAttachments(attachments, &tmpAttachments); + if (!TranslateDefaultAttachments(attachments, &tmpAttachments)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + gl->fInvalidateFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements()); } else { gl->fInvalidateFramebuffer(target, attachments.Length(), attachments.Elements()); @@ -416,7 +432,8 @@ WebGL2Context::InvalidateFramebuffer(GLenum target, const dom::Sequence& void WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence& attachments, - GLint x, GLint y, GLsizei width, GLsizei height) + GLint x, GLint y, GLsizei width, GLsizei height, + ErrorResult& aRv) { if (IsContextLost()) return; @@ -461,7 +478,11 @@ WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence tmpAttachments; - TranslateDefaultAttachments(attachments, &tmpAttachments); + if (!TranslateDefaultAttachments(attachments, &tmpAttachments)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements(), x, y, width, height); } else { diff --git a/dom/webidl/WebGL2RenderingContext.webidl b/dom/webidl/WebGL2RenderingContext.webidl index 382aa21e7ec..a98a6d127bc 100644 --- a/dom/webidl/WebGL2RenderingContext.webidl +++ b/dom/webidl/WebGL2RenderingContext.webidl @@ -329,9 +329,14 @@ interface WebGL2RenderingContext : WebGLRenderingContext GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); void framebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); any getInternalformatParameter(GLenum target, GLenum internalformat, GLenum pname); + + [Throws] void invalidateFramebuffer(GLenum target, sequence attachments); + + [Throws] void invalidateSubFramebuffer (GLenum target, sequence attachments, GLint x, GLint y, GLsizei width, GLsizei height); + void readBuffer(GLenum src); /* Renderbuffer objects */ From 38122c45e6447bc93425d3dde472e66a2b3c70d2 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 047/107] Bug 1167423 - patch 6 - Handle return values of FallibleTArray functions in WebCryptTask, r=smaug --- dom/crypto/WebCryptoTask.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/crypto/WebCryptoTask.cpp b/dom/crypto/WebCryptoTask.cpp index 1b0936f4f23..26e604b1f0a 100644 --- a/dom/crypto/WebCryptoTask.cpp +++ b/dom/crypto/WebCryptoTask.cpp @@ -1962,7 +1962,9 @@ private: if (!mKeyUsages.IsEmpty()) { mJwk.mKey_ops.Construct(); - mJwk.mKey_ops.Value().AppendElements(mKeyUsages); + if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages)) { + return NS_ERROR_OUT_OF_MEMORY; + } } return NS_OK; From 405a2e5eb650205b42d4f20cb0fccf61f81cc022 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 048/107] Bug 1167423 - patch 7 - Handle return values of FallibleTArray functions in DataStore API, r=smaug --- dom/datastore/DataStoreDB.cpp | 4 +++- dom/datastore/DataStoreService.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dom/datastore/DataStoreDB.cpp b/dom/datastore/DataStoreDB.cpp index fff2e177b38..ed800f7b053 100644 --- a/dom/datastore/DataStoreDB.cpp +++ b/dom/datastore/DataStoreDB.cpp @@ -316,7 +316,9 @@ DataStoreDB::DatabaseOpened() } StringOrStringSequence objectStores; - objectStores.RawSetAsStringSequence().AppendElements(mObjectStores); + if (!objectStores.RawSetAsStringSequence().AppendElements(mObjectStores)) { + return NS_ERROR_OUT_OF_MEMORY; + } nsRefPtr txn; error = mDatabase->Transaction(objectStores, diff --git a/dom/datastore/DataStoreService.cpp b/dom/datastore/DataStoreService.cpp index 716e820003e..b7a4344d711 100644 --- a/dom/datastore/DataStoreService.cpp +++ b/dom/datastore/DataStoreService.cpp @@ -1358,7 +1358,9 @@ DataStoreService::CreateFirstRevisionId(uint32_t aAppId, new FirstRevisionIdCallback(aAppId, aName, aManifestURL); Sequence dbs; - dbs.AppendElement(NS_LITERAL_STRING(DATASTOREDB_REVISION)); + if (!dbs.AppendElement(NS_LITERAL_STRING(DATASTOREDB_REVISION))) { + return NS_ERROR_OUT_OF_MEMORY; + } return db->Open(IDBTransactionMode::Readwrite, dbs, callback); } From 68ce6f4a2c7953e03804cef3746030a82b42dc2e Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 049/107] Bug 1167423 - patch 8 - Handle return values of FallibleTArray functions in HTMLInputElement, r=smaug --- dom/html/HTMLInputElement.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index e7f2c876681..36332e35f8d 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -1860,7 +1860,11 @@ HTMLInputElement::SetValue(const nsAString& aValue, ErrorResult& aRv) return; } Sequence list; - list.AppendElement(aValue); + if (!list.AppendElement(aValue)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + MozSetFileNameArray(list, aRv); return; } @@ -2441,7 +2445,9 @@ HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, uint32_t aLen Sequence list; for (uint32_t i = 0; i < aLength; ++i) { - list.AppendElement(nsDependentString(aFileNames[i])); + if (!list.AppendElement(nsDependentString(aFileNames[i]))) { + return NS_ERROR_OUT_OF_MEMORY; + } } ErrorResult rv; @@ -2492,7 +2498,10 @@ HTMLInputElement::SetUserInput(const nsAString& aValue) if (mType == NS_FORM_INPUT_FILE) { Sequence list; - list.AppendElement(aValue); + if (!list.AppendElement(aValue)) { + return NS_ERROR_OUT_OF_MEMORY; + } + ErrorResult rv; MozSetFileNameArray(list, rv); return rv.StealNSResult(); From 7d65c828b2c6a968c91b50d94126e818c0582b79 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 050/107] Bug 1167423 - patch 9 - Handle return values of FallibleTArray functions in MediaSource, r=jya --- dom/media/mediasource/ContainerParser.cpp | 9 +++++++- dom/media/mediasource/ResourceQueue.cpp | 15 ++++++++----- dom/media/mediasource/ResourceQueue.h | 7 ++++-- .../mediasource/SourceBufferResource.cpp | 9 ++++---- dom/media/mediasource/SourceBufferResource.h | 5 +++-- dom/media/mediasource/TrackBuffer.cpp | 22 ++++++++++++++++--- 6 files changed, 50 insertions(+), 17 deletions(-) diff --git a/dom/media/mediasource/ContainerParser.cpp b/dom/media/mediasource/ContainerParser.cpp index 310918734a4..ee17396ac2d 100644 --- a/dom/media/mediasource/ContainerParser.cpp +++ b/dom/media/mediasource/ContainerParser.cpp @@ -8,6 +8,7 @@ #include "WebMBufferedParser.h" #include "mozilla/Endian.h" +#include "mozilla/ErrorResult.h" #include "mp4_demuxer/MoofParser.h" #include "mozilla/Logging.h" #include "MediaData.h" @@ -324,7 +325,13 @@ public: mp4_demuxer::Interval compositionRange = mParser->GetCompositionRange(byteRanges); - mResource->EvictData(mParser->mOffset, mParser->mOffset); + + ErrorResult rv; + mResource->EvictData(mParser->mOffset, mParser->mOffset, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } if (compositionRange.IsNull()) { return false; diff --git a/dom/media/mediasource/ResourceQueue.cpp b/dom/media/mediasource/ResourceQueue.cpp index ef5248658f1..fb5f8a53883 100644 --- a/dom/media/mediasource/ResourceQueue.cpp +++ b/dom/media/mediasource/ResourceQueue.cpp @@ -89,14 +89,15 @@ ResourceQueue::AppendItem(MediaLargeByteBuffer* aData) } uint32_t -ResourceQueue::Evict(uint64_t aOffset, uint32_t aSizeToEvict) +ResourceQueue::Evict(uint64_t aOffset, uint32_t aSizeToEvict, + ErrorResult& aRv) { SBR_DEBUG("Evict(aOffset=%llu, aSizeToEvict=%u)", aOffset, aSizeToEvict); - return EvictBefore(std::min(aOffset, mOffset + (uint64_t)aSizeToEvict)); + return EvictBefore(std::min(aOffset, mOffset + (uint64_t)aSizeToEvict), aRv); } -uint32_t ResourceQueue::EvictBefore(uint64_t aOffset) +uint32_t ResourceQueue::EvictBefore(uint64_t aOffset, ErrorResult& aRv) { SBR_DEBUG("EvictBefore(%llu)", aOffset); uint32_t evicted = 0; @@ -111,8 +112,12 @@ uint32_t ResourceQueue::EvictBefore(uint64_t aOffset) mOffset += offset; evicted += offset; nsRefPtr data = new MediaLargeByteBuffer; - data->AppendElements(item->mData->Elements() + offset, - item->mData->Length() - offset); + if (!data->AppendElements(item->mData->Elements() + offset, + item->mData->Length() - offset)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return 0; + } + item->mData = data; break; } diff --git a/dom/media/mediasource/ResourceQueue.h b/dom/media/mediasource/ResourceQueue.h index 0f791c81c4b..1ff26d00c54 100644 --- a/dom/media/mediasource/ResourceQueue.h +++ b/dom/media/mediasource/ResourceQueue.h @@ -12,6 +12,8 @@ namespace mozilla { +class ErrorResult; + // A SourceBufferResource has a queue containing the data that is appended // to it. The queue holds instances of ResourceItem which is an array of the // bytes. Appending data to the SourceBufferResource pushes this onto the @@ -47,9 +49,10 @@ public: // Tries to evict at least aSizeToEvict from the queue up until // aOffset. Returns amount evicted. - uint32_t Evict(uint64_t aOffset, uint32_t aSizeToEvict); + uint32_t Evict(uint64_t aOffset, uint32_t aSizeToEvict, + ErrorResult& aRv); - uint32_t EvictBefore(uint64_t aOffset); + uint32_t EvictBefore(uint64_t aOffset, ErrorResult& aRv); uint32_t EvictAll(); diff --git a/dom/media/mediasource/SourceBufferResource.cpp b/dom/media/mediasource/SourceBufferResource.cpp index d0e162ce4d2..3e624031df6 100644 --- a/dom/media/mediasource/SourceBufferResource.cpp +++ b/dom/media/mediasource/SourceBufferResource.cpp @@ -174,12 +174,13 @@ SourceBufferResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCo } uint32_t -SourceBufferResource::EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold) +SourceBufferResource::EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold, + ErrorResult& aRv) { SBR_DEBUG("EvictData(aPlaybackOffset=%llu," "aThreshold=%u)", aPlaybackOffset, aThreshold); ReentrantMonitorAutoEnter mon(mMonitor); - uint32_t result = mInputBuffer.Evict(aPlaybackOffset, aThreshold); + uint32_t result = mInputBuffer.Evict(aPlaybackOffset, aThreshold, aRv); if (result > 0) { // Wake up any waiting threads in case a ReadInternal call // is now invalid. @@ -189,13 +190,13 @@ SourceBufferResource::EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold) } void -SourceBufferResource::EvictBefore(uint64_t aOffset) +SourceBufferResource::EvictBefore(uint64_t aOffset, ErrorResult& aRv) { SBR_DEBUG("EvictBefore(aOffset=%llu)", aOffset); ReentrantMonitorAutoEnter mon(mMonitor); // If aOffset is past the current playback offset we don't evict. if (aOffset < mOffset) { - mInputBuffer.EvictBefore(aOffset); + mInputBuffer.EvictBefore(aOffset, aRv); } // Wake up any waiting threads in case a ReadInternal call // is now invalid. diff --git a/dom/media/mediasource/SourceBufferResource.h b/dom/media/mediasource/SourceBufferResource.h index 227c59f8226..b2d922f5343 100644 --- a/dom/media/mediasource/SourceBufferResource.h +++ b/dom/media/mediasource/SourceBufferResource.h @@ -112,10 +112,11 @@ public: } // Remove data from resource if it holds more than the threshold // number of bytes. Returns amount evicted. - uint32_t EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold); + uint32_t EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold, + ErrorResult& aRv); // Remove data from resource before the given offset. - void EvictBefore(uint64_t aOffset); + void EvictBefore(uint64_t aOffset, ErrorResult& aRv); // Remove all data from the resource uint32_t EvictAll(); diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index 659618626d0..030ca935a13 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -352,8 +352,14 @@ TrackBuffer::EvictData(double aPlaybackTime, buffered.GetEnd().ToSeconds(), aPlaybackTime, time, playbackOffset, decoders[i]->GetResource()->GetSize()); if (playbackOffset > 0) { + ErrorResult rv; toEvict -= decoders[i]->GetResource()->EvictData(playbackOffset, - playbackOffset); + playbackOffset, + rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } } } decoders[i]->GetReader()->NotifyDataRemoved(); @@ -506,7 +512,12 @@ TrackBuffer::EvictBefore(double aTime) if (endOffset > 0) { MSE_DEBUG("decoder=%u offset=%lld", i, endOffset); - mInitializedDecoders[i]->GetResource()->EvictBefore(endOffset); + ErrorResult rv; + mInitializedDecoders[i]->GetResource()->EvictBefore(endOffset, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return; + } mInitializedDecoders[i]->GetReader()->NotifyDataRemoved(); } } @@ -1104,7 +1115,12 @@ TrackBuffer::RangeRemoval(media::TimeUnit aStart, buffered.GetEnd().ToSeconds(), offset, decoders[i]->GetResource()->GetSize()); if (offset > 0) { - decoders[i]->GetResource()->EvictData(offset, offset); + ErrorResult rv; + decoders[i]->GetResource()->EvictData(offset, offset, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } } } decoders[i]->GetReader()->NotifyDataRemoved(); From 961419bb29a2a5da41918420ae674ace5b67261f Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 12:50:15 +0100 Subject: [PATCH 051/107] Bug 1167423 - patch 10 - Handle return values of FallibleTArray functions in MobileMessage, r=smaug --- dom/mobilemessage/MobileMessageManager.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dom/mobilemessage/MobileMessageManager.cpp b/dom/mobilemessage/MobileMessageManager.cpp index 0a918cd81e4..4c1dc2a9644 100644 --- a/dom/mobilemessage/MobileMessageManager.cpp +++ b/dom/mobilemessage/MobileMessageManager.cpp @@ -599,13 +599,17 @@ MobileMessageManager::DispatchTrustedDeletedEventToSelf(nsISupports* aDeletedInf uint32_t msgIdLength = info->GetData().deletedMessageIds().Length(); if (msgIdLength) { Sequence& deletedMsgIds = init.mDeletedMessageIds.SetValue(); - deletedMsgIds.AppendElements(info->GetData().deletedMessageIds()); + if (!deletedMsgIds.AppendElements(info->GetData().deletedMessageIds())) { + return NS_ERROR_OUT_OF_MEMORY; + } } uint32_t threadIdLength = info->GetData().deletedThreadIds().Length(); if (threadIdLength) { Sequence& deletedThreadIds = init.mDeletedThreadIds.SetValue(); - deletedThreadIds.AppendElements(info->GetData().deletedThreadIds()); + if (!deletedThreadIds.AppendElements(info->GetData().deletedThreadIds())) { + return NS_ERROR_OUT_OF_MEMORY; + } } nsRefPtr event = From ae6471ea81eb93f711ae710df4beb5647e2fa22b Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Wed, 20 May 2015 16:01:23 -0400 Subject: [PATCH 052/107] Bug 1166879. Avoid uninitialized read in FlattenBezier. r=mstange We only want to read from t2 if count is equal to 2. Reordering this condition makes that true. --- gfx/2d/Path.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/2d/Path.cpp b/gfx/2d/Path.cpp index d9463952383..a276609bef1 100644 --- a/gfx/2d/Path.cpp +++ b/gfx/2d/Path.cpp @@ -437,7 +437,7 @@ FlattenBezier(const BezierControlPoints &aControlPoints, FindInflectionPoints(aControlPoints, &t1, &t2, &count); // Check that at least one of the inflection points is inside [0..1] - if (count == 0 || ((t1 < 0 || t1 > 1.0) && ((t2 < 0 || t2 > 1.0) || count == 1)) ) { + if (count == 0 || ((t1 < 0 || t1 > 1.0) && (count == 1 || (t2 < 0 || t2 > 1.0))) ) { FlattenBezierCurveSegment(aControlPoints, aSink, aTolerance); return; } From 8ceb41cb432829bae0d4d6e68a09aeb950ddc49f Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Thu, 21 May 2015 13:35:29 +0800 Subject: [PATCH 053/107] Bug 1166183 - Back out the direct listener removal landed by mistake in bug 1141781. r=jesup --- .../signaling/src/mediapipeline/MediaPipeline.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index 7e15811343c..f60a6f128d4 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -630,12 +630,12 @@ void MediaPipelineTransmit::AttachToTrack(const std::string& track_id) { stream_->AddListener(listener_); - // // Is this a gUM mediastream? If so, also register the Listener directly with - // // the SourceMediaStream that's attached to the TrackUnion so we can get direct - // // unqueued (and not resampled) data - // if (domstream_->AddDirectListener(listener_)) { - // listener_->direct_connect_ = true; - // } + // Is this a gUM mediastream? If so, also register the Listener directly with + // the SourceMediaStream that's attached to the TrackUnion so we can get direct + // unqueued (and not resampled) data + if (domstream_->AddDirectListener(listener_)) { + listener_->direct_connect_ = true; + } #ifndef MOZILLA_INTERNAL_API // this enables the unit tests that can't fiddle with principals and the like From 016b4c15501492468b014a6eeb5a9288de286bfd Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Thu, 21 May 2015 13:35:29 +0800 Subject: [PATCH 054/107] Bug 1166183 - Work around bug 934512 in track_peerConnection_replaceTrack.html. r=pehrson --- dom/media/tests/mochitest/test_peerConnection_replaceTrack.html | 1 + 1 file changed, 1 insertion(+) diff --git a/dom/media/tests/mochitest/test_peerConnection_replaceTrack.html b/dom/media/tests/mochitest/test_peerConnection_replaceTrack.html index 8d5956d2314..e7545dbc9c2 100644 --- a/dom/media/tests/mochitest/test_peerConnection_replaceTrack.html +++ b/dom/media/tests/mochitest/test_peerConnection_replaceTrack.html @@ -32,6 +32,7 @@ var audiotrack; return navigator.mediaDevices.getUserMedia({video:true, audio:true, fake:true}) .then(newStream => { + window.grip = newStream; newTrack = newStream.getVideoTracks()[0]; audiotrack = newStream.getAudioTracks()[0]; isnot(newTrack, sender.track, "replacing with a different track"); From 028f3e78aca34798b8762e6cc30642af2a6bdf76 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Thu, 21 May 2015 13:35:29 +0800 Subject: [PATCH 055/107] Bug 1166183 - Reset PipelineListener's flag after ReplaceTrack(). r=bwc --- .../signaling/src/mediapipeline/MediaPipeline.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index f60a6f128d4..0374184a510 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -630,12 +630,10 @@ void MediaPipelineTransmit::AttachToTrack(const std::string& track_id) { stream_->AddListener(listener_); - // Is this a gUM mediastream? If so, also register the Listener directly with - // the SourceMediaStream that's attached to the TrackUnion so we can get direct - // unqueued (and not resampled) data - if (domstream_->AddDirectListener(listener_)) { - listener_->direct_connect_ = true; - } + // Is this a gUM mediastream? If so, also register the Listener directly with + // the SourceMediaStream that's attached to the TrackUnion so we can get direct + // unqueued (and not resampled) data + listener_->direct_connect_ = domstream_->AddDirectListener(listener_); #ifndef MOZILLA_INTERNAL_API // this enables the unit tests that can't fiddle with principals and the like From 874588c43a4feebe8579a475b67de7b8e953b27e Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 21 May 2015 14:57:00 -0400 Subject: [PATCH 056/107] Bug 1167412 - Always register testing JS modules. r=ted --- testing/mochitest/chrome-harness.js | 16 ---------------- .../components/SpecialPowersObserver.js | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/testing/mochitest/chrome-harness.js b/testing/mochitest/chrome-harness.js index 32388c038ad..83e8c5c67ec 100644 --- a/testing/mochitest/chrome-harness.js +++ b/testing/mochitest/chrome-harness.js @@ -293,23 +293,7 @@ function readConfig(filename) { return JSON.parse(str); } -function registerTests() { - var testsURI = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("ProfD", Components.interfaces.nsILocalFile); - testsURI.append("tests.manifest"); - var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Components.interfaces.nsIIOService); - var manifestFile = ioSvc.newFileURI(testsURI). - QueryInterface(Components.interfaces.nsIFileURL).file; - - Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar). - autoRegister(manifestFile); -} - function getTestList(params, callback) { - registerTests(); - var baseurl = 'chrome://mochitests/content'; if (window.parseQueryString) { params = parseQueryString(location.search.substring(1), true); diff --git a/testing/specialpowers/components/SpecialPowersObserver.js b/testing/specialpowers/components/SpecialPowersObserver.js index b1ae61f89e8..14d2b1ecc3d 100644 --- a/testing/specialpowers/components/SpecialPowersObserver.js +++ b/testing/specialpowers/components/SpecialPowersObserver.js @@ -116,6 +116,20 @@ SpecialPowersObserver.prototype = new SpecialPowersObserverAPI(); var obs = Services.obs; obs.addObserver(this, "xpcom-shutdown", false); obs.addObserver(this, "chrome-document-global-created", false); + + // Register special testing modules. + var testsURI = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsILocalFile); + testsURI.append("tests.manifest"); + var ioSvc = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + var manifestFile = ioSvc.newFileURI(testsURI). + QueryInterface(Ci.nsIFileURL).file; + + Components.manager.QueryInterface(Ci.nsIComponentRegistrar). + autoRegister(manifestFile); + obs.addObserver(this, "http-on-modify-request", false); if (messageManager) { From e7ffccf507cab147195909a76793360ba3b740a2 Mon Sep 17 00:00:00 2001 From: chunminchang Date: Sun, 24 May 2015 18:49:00 -0400 Subject: [PATCH 057/107] Bug 1149868 - Move permissionObserver to SpecialPowersObserver to listen all perm-changed signals. r=jmaher --- .../mochitest/tests/Harness_sanity/app.html | 9 ++ .../tests/Harness_sanity/file_app.sjs | 55 +++++++++ .../Harness_sanity/file_app.template.webapp | 6 + .../tests/Harness_sanity/mochitest.ini | 5 + .../test_SpecialPowersPushAppPermissions.html | 111 ++++++++++++++++++ .../components/SpecialPowersObserver.js | 24 ++++ .../content/SpecialPowersObserverAPI.js | 6 +- .../specialpowers/content/specialpowersAPI.js | 103 ++++++++++++++-- 8 files changed, 310 insertions(+), 9 deletions(-) create mode 100644 testing/mochitest/tests/Harness_sanity/app.html create mode 100644 testing/mochitest/tests/Harness_sanity/file_app.sjs create mode 100644 testing/mochitest/tests/Harness_sanity/file_app.template.webapp create mode 100644 testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushAppPermissions.html diff --git a/testing/mochitest/tests/Harness_sanity/app.html b/testing/mochitest/tests/Harness_sanity/app.html new file mode 100644 index 00000000000..7dd21adabcd --- /dev/null +++ b/testing/mochitest/tests/Harness_sanity/app.html @@ -0,0 +1,9 @@ + + + + + empty app + + + + diff --git a/testing/mochitest/tests/Harness_sanity/file_app.sjs b/testing/mochitest/tests/Harness_sanity/file_app.sjs new file mode 100644 index 00000000000..2d04205fb0b --- /dev/null +++ b/testing/mochitest/tests/Harness_sanity/file_app.sjs @@ -0,0 +1,55 @@ +var gBasePath = "tests/testing/mochitest/tests/Harness_sanity/"; + +function handleRequest(request, response) { + var query = getQuery(request); + + var testToken = ''; + if ('testToken' in query) { + testToken = query.testToken; + } + + var template = ''; + if ('template' in query) { + template = query.template; + } + var template = gBasePath + template; + response.setHeader("Content-Type", "application/x-web-app-manifest+json", false); + response.write(readTemplate(template).replace(/TESTTOKEN/g, testToken)); +} + +// Copy-pasted incantations. There ought to be a better way to synchronously read +// a file into a string, but I guess we're trying to discourage that. +function readTemplate(path) { + var file = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("CurWorkD", Components.interfaces.nsILocalFile); + var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. + createInstance(Components.interfaces.nsIFileInputStream); + var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Components.interfaces.nsIConverterInputStream); + var split = path.split("/"); + for(var i = 0; i < split.length; ++i) { + file.append(split[i]); + } + fis.init(file, -1, -1, false); + cis.init(fis, "UTF-8", 0, 0); + + var data = ""; + let str = {}; + let read = 0; + do { + read = cis.readString(0xffffffff, str); // read as much as we can and put it in str.value + data += str.value; + } while (read != 0); + cis.close(); + return data; +} + +function getQuery(request) { + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + return query; +} diff --git a/testing/mochitest/tests/Harness_sanity/file_app.template.webapp b/testing/mochitest/tests/Harness_sanity/file_app.template.webapp new file mode 100644 index 00000000000..d43cd06e113 --- /dev/null +++ b/testing/mochitest/tests/Harness_sanity/file_app.template.webapp @@ -0,0 +1,6 @@ +{ + "name": "EMPTY-APP", + "description": "Empty app for Harness_sanity.", + "launch_path": "/tests/testing/mochitest/tests/Harness_sanity/TESTTOKEN", + "icons": { "128": "default_icon" } +} diff --git a/testing/mochitest/tests/Harness_sanity/mochitest.ini b/testing/mochitest/tests/Harness_sanity/mochitest.ini index 0eefc3feea7..44ef14efc7f 100644 --- a/testing/mochitest/tests/Harness_sanity/mochitest.ini +++ b/testing/mochitest/tests/Harness_sanity/mochitest.ini @@ -12,6 +12,11 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only [test_SpecialPowersExtension2.html] support-files = file_SpecialPowersFrame1.html [test_SpecialPowersPushPermissions.html] +[test_SpecialPowersPushAppPermissions.html] +support-files = + file_app.sjs + file_app.template.webapp + app.html [test_SpecialPowersPushPrefEnv.html] [test_SimpletestGetTestFileURL.html] [test_SpecialPowersLoadChromeScript.html] diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushAppPermissions.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushAppPermissions.html new file mode 100644 index 00000000000..1b516fbeaf3 --- /dev/null +++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushAppPermissions.html @@ -0,0 +1,111 @@ + + + + Test for SpecialPowers extension + + + + + +
+
+
+ + + diff --git a/testing/specialpowers/components/SpecialPowersObserver.js b/testing/specialpowers/components/SpecialPowersObserver.js index 14d2b1ecc3d..95f51445540 100644 --- a/testing/specialpowers/components/SpecialPowersObserver.js +++ b/testing/specialpowers/components/SpecialPowersObserver.js @@ -146,6 +146,9 @@ SpecialPowersObserver.prototype = new SpecialPowersObserverAPI(); obs.removeObserver(this, "chrome-document-global-created"); obs.removeObserver(this, "http-on-modify-request"); obs.removeObserver(this, "xpcom-shutdown"); + this._registerObservers._topics.forEach(function(element) { + obs.removeObserver(this._registerObservers, element); + }); this._removeProcessCrashObservers(); if (this._isFrameScriptLoaded) { @@ -200,6 +203,27 @@ SpecialPowersObserver.prototype = new SpecialPowersObserverAPI(); this._processCrashObserversRegistered = false; }; + SpecialPowersObserver.prototype._registerObservers = { + _self: null, + _topics: [], + _add: function(topic) { + if (this._topics.indexOf(topic) < 0) { + this._topics.push(topic); + Services.obs.addObserver(this, topic, false); + } + }, + observe: function (aSubject, aTopic, aData) { + var msg = { aData: aData }; + switch (aTopic) { + case "perm-changed": + var permission = aSubject.QueryInterface(Ci.nsIPermission); + msg.permission = { appId: permission.appId, type: permission.type }; + default: + this._self._sendAsyncMessage("specialpowers-" + aTopic, msg); + } + } + }; + /** * messageManager callback function * This will get requests from our API in the window and process them in chrome for it diff --git a/testing/specialpowers/content/SpecialPowersObserverAPI.js b/testing/specialpowers/content/SpecialPowersObserverAPI.js index a0d94039ebb..42f4d0daa4c 100644 --- a/testing/specialpowers/content/SpecialPowersObserverAPI.js +++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js @@ -409,12 +409,16 @@ SpecialPowersObserverAPI.prototype = { } case "SPObserverService": { + let topic = aMessage.json.observerTopic; switch (aMessage.json.op) { case "notify": - let topic = aMessage.json.observerTopic; let data = aMessage.json.observerData Services.obs.notifyObservers(null, topic, data); break; + case "add": + this._registerObservers._self = this; + this._registerObservers._add(topic); + break; default: throw new SpecialPowersError("Invalid operation for SPObserverervice"); } diff --git a/testing/specialpowers/content/specialpowersAPI.js b/testing/specialpowers/content/specialpowersAPI.js index d16dc3dedd8..d1442c6955e 100644 --- a/testing/specialpowers/content/specialpowersAPI.js +++ b/testing/specialpowers/content/specialpowersAPI.js @@ -44,6 +44,7 @@ function SpecialPowersAPI() { this._permissionsUndoStack = []; this._pendingPermissions = []; this._applyingPermissions = false; + this._observingPermissions = false; this._fm = null; this._cb = null; this._quotaManagerCallbackInfos = null; @@ -830,6 +831,21 @@ SpecialPowersAPI.prototype = { // that the callback checks for. The second delay is because pref // observers often defer making their changes by posting an event to the // event loop. + if (!this._observingPermissions) { + this._observingPermissions = true; + // If specialpowers is in main-process, then we can add a observer + // to get all 'perm-changed' signals. Otherwise, it can't receive + // all signals, so we register a observer in specialpowersobserver(in + // main-process) and get signals from it. + if (this.isMainProcess()) { + this.permissionObserverProxy._specialPowersAPI = this; + Services.obs.addObserver(this.permissionObserverProxy, "perm-changed", false); + } else { + this.registerObservers("perm-changed"); + // bind() is used to set 'this' to SpecialPowersAPI itself. + this._addMessageListener("specialpowers-perm-changed", this.permChangedProxy.bind(this)); + } + } this._permissionsUndoStack.push(cleanupPermissions); this._pendingPermissions.push([pendingPermissions, this._delayCallbackTwice(callback)]); @@ -839,6 +855,51 @@ SpecialPowersAPI.prototype = { } }, + /* + * This function should be used when specialpowers is in content process but + * it want to get the notification from chrome space. + * + * This function will call Services.obs.addObserver in SpecialPowersObserver + * (that is in chrome process) and forward the data received to SpecialPowers + * via messageManager. + * You can use this._addMessageListener("specialpowers-YOUR_TOPIC") to fire + * the callback. + * + * To get the expected data, you should modify + * SpecialPowersObserver.prototype._registerObservers.observe. Or the message + * you received from messageManager will only contain 'aData' from Service.obs. + * + * NOTICE: there is no implementation of _addMessageListener in + * ChromePowers.js + */ + registerObservers: function(topic) { + var msg = { + 'op': 'add', + 'observerTopic': topic, + }; + this._sendSyncMessage("SPObserverService", msg); + }, + + permChangedProxy: function(aMessage) { + let permission = aMessage.json.permission; + let aData = aMessage.json.aData; + this._permissionObserver.observe(permission, aData); + }, + + permissionObserverProxy: { + // 'this' in permChangedObserverProxy is the permChangedObserverProxy + // object itself. The '_specialPowersAPI' will be set to the 'SpecialPowersAPI' + // object to call the member function in SpecialPowersAPI. + _specialPowersAPI: null, + observe: function (aSubject, aTopic, aData) + { + if (aTopic == "perm-changed") { + var permission = aSubject.QueryInterface(Ci.nsIPermission); + this._specialPowersAPI._permissionObserver.observe(permission, aData); + } + } + }, + popPermissions: function(callback) { if (this._permissionsUndoStack.length > 0) { // See pushPermissions comment regarding delay. @@ -847,7 +908,11 @@ SpecialPowersAPI.prototype = { this._pendingPermissions.push([this._permissionsUndoStack.pop(), cb]); this._applyPermissions(); } else { - this._setTimeout(callback); + if (this._observingPermissions) { + this._observingPermissions = false; + this._removeMessageListener("specialpowers-perm-changed", this.permChangedProxy.bind(this)); + } + this._setTimeout(callback); } }, @@ -870,15 +935,39 @@ SpecialPowersAPI.prototype = { _lastPermission: {}, _callBack: null, _nextCallback: null, - - observe: function (aSubject, aTopic, aData) + _obsDataMap: { + 'deleted':'remove', + 'added':'add' + }, + observe: function (permission, aData) { - if (aTopic == "perm-changed") { - var permission = aSubject.QueryInterface(Ci.nsIPermission); + if (this._self._applyingPermissions) { if (permission.type == this._lastPermission.type) { - Services.obs.removeObserver(this, "perm-changed"); this._self._setTimeout(this._callback); this._self._setTimeout(this._nextCallback); + this._callback = null; + this._nextCallback = null; + } + } else { + var found = false; + for (var i = 0; !found && i < this._self._permissionsUndoStack.length; i++) { + var undos = this._self._permissionsUndoStack[i]; + for (var j = 0; j < undos.length; j++) { + var undo = undos[j]; + if (undo.op == this._obsDataMap[aData] && + undo.appId == permission.appId && + undo.type == permission.type) { + // Remove this undo item if it has been done by others(not + // specialpowers itself.) + undos.splice(j,1); + found = true; + break; + } + } + if (!undos.length) { + // Remove the empty row in permissionsUndoStack + this._self._permissionsUndoStack.splice(i, 1); + } } } } @@ -910,8 +999,6 @@ SpecialPowersAPI.prototype = { self._applyPermissions(); } - Services.obs.addObserver(this._permissionObserver, "perm-changed", false); - for (var idx in pendingActions) { var perm = pendingActions[idx]; this._sendSyncMessage('SPPermissionManager', perm)[0]; From 2635954b4fce79ab3c8850bf8fa721ea3912c095 Mon Sep 17 00:00:00 2001 From: Michael Layzell Date: Fri, 22 May 2015 10:10:00 -0400 Subject: [PATCH 058/107] Bug 1167396 - Make ProtocolCloneContext::mContentParent a smart pointer. r=bent --- ipc/glue/ProtocolUtils.cpp | 12 ++++++++++++ ipc/glue/ProtocolUtils.h | 14 +++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp index 07daf3ba2ad..ee5995602ad 100644 --- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -26,6 +26,18 @@ using base::ProcessId; namespace mozilla { namespace ipc { +ProtocolCloneContext::ProtocolCloneContext() + : mNeckoParent(nullptr) +{} + +ProtocolCloneContext::~ProtocolCloneContext() +{} + +void ProtocolCloneContext::SetContentParent(ContentParent* aContentParent) +{ + mContentParent = aContentParent; +} + static StaticMutex gProtocolMutex; IToplevelProtocol::IToplevelProtocol(ProtocolId aProtoId) diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index af93438cc11..5898d71f390 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -126,19 +126,15 @@ class ProtocolCloneContext typedef mozilla::dom::ContentParent ContentParent; typedef mozilla::net::NeckoParent NeckoParent; - ContentParent* mContentParent; + nsRefPtr mContentParent; NeckoParent* mNeckoParent; public: - ProtocolCloneContext() - : mContentParent(nullptr) - , mNeckoParent(nullptr) - {} + ProtocolCloneContext(); - void SetContentParent(ContentParent* aContentParent) - { - mContentParent = aContentParent; - } + ~ProtocolCloneContext(); + + void SetContentParent(ContentParent* aContentParent); ContentParent* GetContentParent() { return mContentParent; } From 725659916e2c95bab3cee9eb0ff5ed1f394f70de Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Thu, 14 May 2015 11:07:38 -0700 Subject: [PATCH 059/107] Bug 1161221 - Split http referrer tests and enable them on all platforms except https on b2g. r=sstamm --- dom/base/test/bug704320.sjs | 5 +- dom/base/test/mochitest.ini | 12 +- dom/base/test/referrerHelper.js | 163 ++++++++++- dom/base/test/test_bug1163743.html | 2 +- dom/base/test/test_bug704320.html | 252 ------------------ dom/base/test/test_bug704320_http_http.html | 62 +++++ dom/base/test/test_bug704320_http_https.html | 62 +++++ dom/base/test/test_bug704320_https_http.html | 62 +++++ dom/base/test/test_bug704320_https_https.html | 62 +++++ dom/base/test/test_bug704320_policyset.html | 18 +- dom/base/test/test_bug704320_preload.html | 46 +--- 11 files changed, 434 insertions(+), 312 deletions(-) delete mode 100644 dom/base/test/test_bug704320.html create mode 100644 dom/base/test/test_bug704320_http_http.html create mode 100644 dom/base/test/test_bug704320_http_https.html create mode 100644 dom/base/test/test_bug704320_https_http.html create mode 100644 dom/base/test/test_bug704320_https_https.html diff --git a/dom/base/test/bug704320.sjs b/dom/base/test/bug704320.sjs index 652a442c674..b154475d05b 100644 --- a/dom/base/test/bug704320.sjs +++ b/dom/base/test/bug704320.sjs @@ -80,6 +80,7 @@ function createTest(schemeFrom, schemeTo, policy) { \n\ \n\ - - - -Mozilla Bug 704320 -

-
-
-
-
- - - - - diff --git a/dom/base/test/test_bug704320_http_http.html b/dom/base/test/test_bug704320_http_http.html new file mode 100644 index 00000000000..cd5fc27d995 --- /dev/null +++ b/dom/base/test/test_bug704320_http_http.html @@ -0,0 +1,62 @@ + + + + + + Test for Bug 704320 - HTTP to HTTP + + + + + + + + +Mozilla Bug 704320 - HTTP to HTTP +

+
+
+ + + + + diff --git a/dom/base/test/test_bug704320_http_https.html b/dom/base/test/test_bug704320_http_https.html new file mode 100644 index 00000000000..5ec91802d48 --- /dev/null +++ b/dom/base/test/test_bug704320_http_https.html @@ -0,0 +1,62 @@ + + + + + + Test for Bug 704320 - HTTP to HTTPS + + + + + + + + +Mozilla Bug 704320 - HTTP to HTTPS +

+
+
+ + + + + diff --git a/dom/base/test/test_bug704320_https_http.html b/dom/base/test/test_bug704320_https_http.html new file mode 100644 index 00000000000..aaf03a8329f --- /dev/null +++ b/dom/base/test/test_bug704320_https_http.html @@ -0,0 +1,62 @@ + + + + + + Test for Bug 704320 - HTTPS to HTTP + + + + + + + + +Mozilla Bug 704320 - HTTPS to HTTP +

+
+
+ + + + + diff --git a/dom/base/test/test_bug704320_https_https.html b/dom/base/test/test_bug704320_https_https.html new file mode 100644 index 00000000000..5148fbfab84 --- /dev/null +++ b/dom/base/test/test_bug704320_https_https.html @@ -0,0 +1,62 @@ + + + + + + Test for Bug 704320 - HTTPS to HTTPS + + + + + + + + +Mozilla Bug 704320 - HTTPS to HTTPS +

+
+
+ + + + + diff --git a/dom/base/test/test_bug704320_policyset.html b/dom/base/test/test_bug704320_policyset.html index 411ce94721f..7c608ad2b19 100644 --- a/dom/base/test/test_bug704320_policyset.html +++ b/dom/base/test/test_bug704320_policyset.html @@ -37,7 +37,7 @@ var tests = (function() { yield iframe.src = sjs + "&policy=" + escape('default'); // check the first test (two images, no referrers) - yield checkResults("default", ["full"]); + yield checkIndividualResults("default", ["full"]); // check invalid policy // According to the spec section 6.4, if there is a policy token @@ -45,7 +45,7 @@ var tests = (function() { // should be the policy used. yield resetCounter(); yield iframe.src = sjs + "&policy=" + escape('invalid-policy'); - yield checkResults("invalid", ["none"]); + yield checkIndividualResults("invalid", ["none"]); // whitespace checks. // according to the spec section 4.1, the content attribute's value @@ -53,20 +53,20 @@ var tests = (function() { // trailing whitespace. yield resetCounter(); yield iframe.src = sjs + "&policy=" + escape('default '); - yield checkResults("trailing whitespace", ["full"]); + yield checkIndividualResults("trailing whitespace", ["full"]); yield resetCounter(); yield iframe.src = sjs + "&policy=" + escape(' origin\f'); - yield checkResults("trailing form feed", ["origin"]); + yield checkIndividualResults("trailing form feed", ["origin"]); yield resetCounter(); yield iframe.src = sjs + "&policy=" + escape('\f origin'); - yield checkResults("leading form feed", ["origin"]); + yield checkIndividualResults("leading form feed", ["origin"]); // origin when cross-origin (trimming whitespace) yield resetCounter(); yield iframe.src = sjs + "&policy=" + escape(' origin-when-cross-origin'); - yield checkResults("origin-when-cross-origin", ["origin", "full"]); + yield checkIndividualResults("origin-when-cross-origin", ["origin", "full"]); // according to the spec section 4.1: // "If the meta element lacks a content attribute, or if that attribute’s @@ -77,16 +77,16 @@ var tests = (function() { // http://www.w3.org/html/wg/drafts/html/CR/infrastructure.html#space-character yield resetCounter(); yield iframe.src = sjs + "&policy=" + escape(' \t '); - yield checkResults("basic whitespace only policy", ["full"]); + yield checkIndividualResults("basic whitespace only policy", ["full"]); yield resetCounter(); yield iframe.src = sjs + "&policy=" + escape(' \f\r\n\t '); - yield checkResults("whitespace only policy", ["full"]); + yield checkIndividualResults("whitespace only policy", ["full"]); // and double-check that no-referrer works. yield resetCounter(); yield iframe.src = sjs + "&policy=" + escape('no-referrer'); - yield checkResults("no-referrer", ["none"]); + yield checkIndividualResults("no-referrer", ["none"]); // complete. Be sure to yield so we don't call this twice. yield SimpleTest.finish(); diff --git a/dom/base/test/test_bug704320_preload.html b/dom/base/test/test_bug704320_preload.html index 3adfdff1199..d7f2fc72d39 100644 --- a/dom/base/test/test_bug704320_preload.html +++ b/dom/base/test/test_bug704320_preload.html @@ -9,6 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=704320 Test preloads for Bug 704320 + From da54b2402eef6a937234744af6fe7da5ab21b39f Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 25 May 2015 12:11:07 -0400 Subject: [PATCH 071/107] Backed out changeset a3d6ad2faa3a (bug 996652) for checktest bustage. --- testing/xpcshell/head.js | 76 +++++++--------------------------------- 1 file changed, 13 insertions(+), 63 deletions(-) diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index e593e8e9064..829bc113434 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -10,7 +10,6 @@ * for more information. */ -(function(exports) { var _quit = false; var _passed = true; var _tests_pending = 0; @@ -196,7 +195,7 @@ _Timer.prototype = { }; function _do_main() { - if (exports._quit) + if (_quit) return; _testLogger.info("running event loop"); @@ -204,7 +203,7 @@ function _do_main() { var thr = Components.classes["@mozilla.org/thread-manager;1"] .getService().currentThread; - while (!exports._quit) + while (!_quit) thr.processNextEvent(true); while (thr.hasPendingEvents()) @@ -214,7 +213,7 @@ function _do_main() { function _do_quit() { _testLogger.info("exiting test"); _Promise.Debugging.flushUncaughtErrors(); - exports._quit = true; + _quit = true; } /** @@ -494,6 +493,12 @@ function _execute_test() { // _TEST_FILE is dynamically defined by . _load_files(_TEST_FILE); + // Tack Assert.jsm methods to the current scope. + this.Assert = Assert; + for (let func in Assert) { + this[func] = Assert[func].bind(Assert); + } + try { do_test_pending("MAIN run_test"); // Check if run_test() is defined. If defined, run it. @@ -508,12 +513,12 @@ function _execute_test() { _do_main(); } catch (e) { _passed = false; - // do_check failures are already logged and set exports._quit to true and throw + // do_check failures are already logged and set _quit to true and throw // NS_ERROR_ABORT. If both of those are true it is likely this exception // has already been logged so there is no need to log it again. It's // possible that this will mask an NS_ERROR_ABORT that happens after a // do_check failure though. - if (!exports._quit || e != Components.results.NS_ERROR_ABORT) { + if (!_quit || e != Components.results.NS_ERROR_ABORT) { let extra = {}; if (e.fileName) { extra.source_file = e.fileName; @@ -647,12 +652,12 @@ function do_execute_soon(callback, aName) { try { callback(); } catch (e) { - // do_check failures are already logged and set exports._quit to true and throw + // do_check failures are already logged and set _quit to true and throw // NS_ERROR_ABORT. If both of those are true it is likely this exception // has already been logged so there is no need to log it again. It's // possible that this will mask an NS_ERROR_ABORT that happens after a // do_check failure though. - if (!exports._quit || e != Components.results.NS_ERROR_ABORT) { + if (!_quit || e != Components.results.NS_ERROR_ABORT) { let stack = e.stack ? _format_stack(e.stack) : null; _testLogger.testStatus(_TEST_NAME, funcName, @@ -1493,58 +1498,3 @@ try { prefs.deleteBranch("browser.devedition.theme.enabled"); } } catch (e) { } - -exports._execute_test = _execute_test; -exports._setupDebuggerServer = _setupDebuggerServer; -exports._quit = _quit; -exports._testLogger = _testLogger; -exports._wrap_with_quotes_if_necessary = _wrap_with_quotes_if_necessary; -exports.Assert = Assert; -exports.add_task = add_task; -exports.add_test = add_test; -exports.do_await_remote_message = do_await_remote_message; -exports.do_check_eq = do_check_eq; -exports.do_check_false = do_check_false; -exports.do_check_instanceof = do_check_instanceof; -exports.do_check_matches = do_check_matches; -exports.do_check_neq = do_check_neq; -exports.do_check_null = do_check_null; -exports.do_check_throws_nsIException = do_check_throws_nsIException; -exports.do_check_true = do_check_true; -exports.do_execute_soon = do_execute_soon; -exports.do_get_cwd = do_get_cwd; -exports.do_get_file = do_get_file; -exports.do_get_idle = do_get_idle; -exports.do_get_minidumpdir = do_get_minidumpdir; -exports.do_get_profile = do_get_profile; -exports.do_get_tempdir = do_get_tempdir; -exports.do_load_child_test_harness = do_load_child_test_harness; -exports.do_load_manifest = do_load_manifest; -exports.do_note_exception = do_note_exception; -exports.do_parse_document = do_parse_document; -exports.do_print = do_print; -exports.do_register_cleanup = do_register_cleanup; -exports.do_report_result = do_report_result; -exports.do_report_unexpected_exception = do_report_unexpected_exception; -exports.do_send_remote_message = do_send_remote_message; -exports.do_test_finished = do_test_finished; -exports.do_test_pending = do_test_pending; -exports.do_throw = do_throw; -exports.do_timeout = do_timeout; -exports.legible_exception = legible_exception; -exports.run_next_test = run_next_test; -exports.run_test_in_child = run_test_in_child; -exports.runningInParent = runningInParent; -exports.todo_check_eq = todo_check_eq; -exports.todo_check_false = todo_check_false; -exports.todo_check_instanceof = todo_check_instanceof; -exports.todo_check_neq = todo_check_neq; -exports.todo_check_null = todo_check_null; -exports.todo_check_true = todo_check_true; - -// Tack Assert.jsm methods to the current scope. -exports.Assert = Assert; -for (let func in Assert) { - exports[func] = Assert[func].bind(Assert); -} -} From b66ef4e88caa1e3f2d8657b047def7dc5bec084b Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 25 May 2015 17:53:07 +0100 Subject: [PATCH 072/107] Bug 1155761 - User Timing API in Workers, r=ehsan --- dom/base/PerformanceEntry.cpp | 9 +- dom/base/PerformanceEntry.h | 11 +- dom/base/PerformanceMark.cpp | 12 +- dom/base/PerformanceMark.h | 5 +- dom/base/PerformanceMeasure.cpp | 7 +- dom/base/PerformanceMeasure.h | 2 +- dom/base/nsDOMNavigationTiming.cpp | 3 +- dom/base/nsPerformance.cpp | 631 ++++++++++-------- dom/base/nsPerformance.h | 155 +++-- dom/base/test/mochitest.ini | 1 + .../test/test_performance_user_timing.html | 268 +------- dom/base/test/test_performance_user_timing.js | 272 ++++++++ dom/webidl/Performance.webidl | 24 +- dom/webidl/PerformanceEntry.webidl | 1 + dom/webidl/PerformanceMark.webidl | 1 + dom/webidl/PerformanceMeasure.webidl | 1 + dom/workers/Performance.cpp | 48 +- dom/workers/Performance.h | 32 +- dom/workers/WorkerPrivate.cpp | 7 +- dom/workers/WorkerPrivate.h | 12 + dom/workers/test/mochitest.ini | 4 + .../test_serviceworker_interfaces.js | 6 + .../sharedworker_performance_user_timing.js | 30 + .../test/test_performance_user_timing.html | 27 + ..._sharedWorker_performance_user_timing.html | 30 + dom/workers/test/test_worker_interfaces.js | 6 + .../test/worker_performance_user_timing.js | 24 + 27 files changed, 996 insertions(+), 633 deletions(-) create mode 100644 dom/base/test/test_performance_user_timing.js create mode 100644 dom/workers/test/sharedworker_performance_user_timing.js create mode 100644 dom/workers/test/test_performance_user_timing.html create mode 100644 dom/workers/test/test_sharedWorker_performance_user_timing.html create mode 100644 dom/workers/test/worker_performance_user_timing.js diff --git a/dom/base/PerformanceEntry.cpp b/dom/base/PerformanceEntry.cpp index 0f6fc9c1d0f..3b9b15cefa9 100644 --- a/dom/base/PerformanceEntry.cpp +++ b/dom/base/PerformanceEntry.cpp @@ -10,7 +10,7 @@ using namespace mozilla::dom; -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceEntry, mPerformance) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceEntry, mParent) NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceEntry) NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceEntry) @@ -20,14 +20,15 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceEntry) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END -PerformanceEntry::PerformanceEntry(nsPerformance* aPerformance, +PerformanceEntry::PerformanceEntry(nsISupports* aParent, const nsAString& aName, const nsAString& aEntryType) -: mPerformance(aPerformance), +: mParent(aParent), mName(aName), mEntryType(aEntryType) { - MOZ_ASSERT(aPerformance, "Parent performance object should be provided"); + // mParent is null in workers. + MOZ_ASSERT(mParent || !NS_IsMainThread()); } PerformanceEntry::~PerformanceEntry() diff --git a/dom/base/PerformanceEntry.h b/dom/base/PerformanceEntry.h index 9efdb571334..4489fb8a0ac 100644 --- a/dom/base/PerformanceEntry.h +++ b/dom/base/PerformanceEntry.h @@ -7,9 +7,10 @@ #ifndef mozilla_dom_PerformanceEntry_h___ #define mozilla_dom_PerformanceEntry_h___ -#include "nsPerformance.h" #include "nsDOMNavigationTiming.h" +class nsISupports; + namespace mozilla { namespace dom { @@ -21,7 +22,7 @@ protected: virtual ~PerformanceEntry(); public: - PerformanceEntry(nsPerformance* aPerformance, + PerformanceEntry(nsISupports* aParent, const nsAString& aName, const nsAString& aEntryType); @@ -30,9 +31,9 @@ public: virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - nsPerformance* GetParentObject() const + nsISupports* GetParentObject() const { - return mPerformance; + return mParent; } void GetName(nsAString& aName) const @@ -76,7 +77,7 @@ public: } protected: - nsRefPtr mPerformance; + nsCOMPtr mParent; nsString mName; nsString mEntryType; }; diff --git a/dom/base/PerformanceMark.cpp b/dom/base/PerformanceMark.cpp index 451cbcce426..908793c58ef 100644 --- a/dom/base/PerformanceMark.cpp +++ b/dom/base/PerformanceMark.cpp @@ -9,12 +9,14 @@ using namespace mozilla::dom; -PerformanceMark::PerformanceMark(nsPerformance* aPerformance, - const nsAString& aName) -: PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("mark")) +PerformanceMark::PerformanceMark(nsISupports* aParent, + const nsAString& aName, + DOMHighResTimeStamp aStartTime) + : PerformanceEntry(aParent, aName, NS_LITERAL_STRING("mark")) + , mStartTime(aStartTime) { - MOZ_ASSERT(aPerformance, "Parent performance object should be provided"); - mStartTime = aPerformance->GetDOMTiming()->TimeStampToDOMHighRes(mozilla::TimeStamp::Now()); + // mParent is null in workers. + MOZ_ASSERT(mParent || !NS_IsMainThread()); } PerformanceMark::~PerformanceMark() diff --git a/dom/base/PerformanceMark.h b/dom/base/PerformanceMark.h index 69c2e06d64c..52ad49805c0 100644 --- a/dom/base/PerformanceMark.h +++ b/dom/base/PerformanceMark.h @@ -16,8 +16,9 @@ namespace dom { class PerformanceMark final : public PerformanceEntry { public: - PerformanceMark(nsPerformance* aPerformance, - const nsAString& aName); + PerformanceMark(nsISupports* aParent, + const nsAString& aName, + DOMHighResTimeStamp aStartTime); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; diff --git a/dom/base/PerformanceMeasure.cpp b/dom/base/PerformanceMeasure.cpp index 83555db656c..615f992d9ce 100644 --- a/dom/base/PerformanceMeasure.cpp +++ b/dom/base/PerformanceMeasure.cpp @@ -9,15 +9,16 @@ using namespace mozilla::dom; -PerformanceMeasure::PerformanceMeasure(nsPerformance* aPerformance, +PerformanceMeasure::PerformanceMeasure(nsISupports* aParent, const nsAString& aName, DOMHighResTimeStamp aStartTime, DOMHighResTimeStamp aEndTime) -: PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("measure")), +: PerformanceEntry(aParent, aName, NS_LITERAL_STRING("measure")), mStartTime(aStartTime), mDuration(aEndTime - aStartTime) { - MOZ_ASSERT(aPerformance, "Parent performance object should be provided"); + // mParent is null in workers. + MOZ_ASSERT(mParent || !NS_IsMainThread()); } PerformanceMeasure::~PerformanceMeasure() diff --git a/dom/base/PerformanceMeasure.h b/dom/base/PerformanceMeasure.h index dc975e2e342..241ec0d5b9f 100644 --- a/dom/base/PerformanceMeasure.h +++ b/dom/base/PerformanceMeasure.h @@ -16,7 +16,7 @@ namespace dom { class PerformanceMeasure final : public PerformanceEntry { public: - PerformanceMeasure(nsPerformance* aPerformance, + PerformanceMeasure(nsISupports* aParent, const nsAString& aName, DOMHighResTimeStamp aStartTime, DOMHighResTimeStamp aEndTime); diff --git a/dom/base/nsDOMNavigationTiming.cpp b/dom/base/nsDOMNavigationTiming.cpp index 9904d89b1d7..92140cb4016 100644 --- a/dom/base/nsDOMNavigationTiming.cpp +++ b/dom/base/nsDOMNavigationTiming.cpp @@ -57,7 +57,8 @@ nsDOMNavigationTiming::TimeStampToDOM(mozilla::TimeStamp aStamp) const return GetNavigationStart() + static_cast(duration.ToMilliseconds()); } -DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart(){ +DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart() +{ return TimeStampToDOM(mozilla::TimeStamp::Now()); } diff --git a/dom/base/nsPerformance.cpp b/dom/base/nsPerformance.cpp index 104523ec0e3..f6da11eaf29 100644 --- a/dom/base/nsPerformance.cpp +++ b/dom/base/nsPerformance.cpp @@ -23,9 +23,12 @@ #include "mozilla/dom/PerformanceBinding.h" #include "mozilla/dom/PerformanceTimingBinding.h" #include "mozilla/dom/PerformanceNavigationBinding.h" +#include "mozilla/Preferences.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/TimeStamp.h" #include "js/HeapAPI.h" +#include "WorkerPrivate.h" +#include "WorkerRunnable.h" #ifdef MOZ_WIDGET_GONK #define PERFLOG(msg, ...) __android_log_print(ANDROID_LOG_INFO, "PerformanceTiming", msg, ##__VA_ARGS__) @@ -35,6 +38,7 @@ using namespace mozilla; using namespace mozilla::dom; +using namespace mozilla::dom::workers; NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPerformanceTiming, mPerformance) @@ -398,40 +402,36 @@ nsPerformanceNavigation::WrapObject(JSContext *cx, JS::Handle aGivenP NS_IMPL_CYCLE_COLLECTION_CLASS(nsPerformance) -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsPerformance, DOMEventTargetHelper) -NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow, mTiming, - mNavigation, mUserEntries, - mResourceEntries, +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsPerformance, PerformanceBase) +NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming, + mNavigation, mParentPerformance) tmp->mMozMemory = nullptr; mozilla::DropJSObjects(this); NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsPerformance, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow, mTiming, - mNavigation, mUserEntries, - mResourceEntries, +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsPerformance, PerformanceBase) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming, + mNavigation, mParentPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsPerformance, DOMEventTargetHelper) +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsPerformance, PerformanceBase) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMozMemory) NS_IMPL_CYCLE_COLLECTION_TRACE_END -NS_IMPL_ADDREF_INHERITED(nsPerformance, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(nsPerformance, DOMEventTargetHelper) +NS_IMPL_ADDREF_INHERITED(nsPerformance, PerformanceBase) +NS_IMPL_RELEASE_INHERITED(nsPerformance, PerformanceBase) nsPerformance::nsPerformance(nsPIDOMWindow* aWindow, nsDOMNavigationTiming* aDOMTiming, nsITimedChannel* aChannel, nsPerformance* aParentPerformance) - : DOMEventTargetHelper(aWindow), - mWindow(aWindow), + : PerformanceBase(aWindow), mDOMTiming(aDOMTiming), mChannel(aChannel), - mParentPerformance(aParentPerformance), - mResourceTimingBufferSize(kDefaultResourceTimingBufferSize) + mParentPerformance(aParentPerformance) { MOZ_ASSERT(aWindow, "Parent window object should be provided"); } @@ -499,7 +499,7 @@ nsPerformance::Navigation() } DOMHighResTimeStamp -nsPerformance::Now() +nsPerformance::Now() const { return GetDOMTiming()->TimeStampToDOMHighRes(TimeStamp::Now()); } @@ -510,90 +510,6 @@ nsPerformance::WrapObject(JSContext *cx, JS::Handle aGivenProto) return PerformanceBinding::Wrap(cx, this, aGivenProto); } -void -nsPerformance::GetEntries(nsTArray>& retval) -{ - MOZ_ASSERT(NS_IsMainThread()); - - retval = mResourceEntries; - retval.AppendElements(mUserEntries); - retval.Sort(PerformanceEntryComparator()); -} - -void -nsPerformance::GetEntriesByType(const nsAString& entryType, - nsTArray>& retval) -{ - MOZ_ASSERT(NS_IsMainThread()); - - retval.Clear(); - if (entryType.EqualsLiteral("resource")) { - retval = mResourceEntries; - } else if (entryType.EqualsLiteral("mark") || - entryType.EqualsLiteral("measure")) { - for (PerformanceEntry* entry : mUserEntries) { - if (entry->GetEntryType().Equals(entryType)) { - retval.AppendElement(entry); - } - } - } -} - -void -nsPerformance::GetEntriesByName(const nsAString& name, - const Optional& entryType, - nsTArray>& retval) -{ - MOZ_ASSERT(NS_IsMainThread()); - - retval.Clear(); - for (PerformanceEntry* entry : mResourceEntries) { - if (entry->GetName().Equals(name) && - (!entryType.WasPassed() || - entry->GetEntryType().Equals(entryType.Value()))) { - retval.AppendElement(entry); - } - } - for (PerformanceEntry* entry : mUserEntries) { - if (entry->GetName().Equals(name) && - (!entryType.WasPassed() || - entry->GetEntryType().Equals(entryType.Value()))) { - retval.AppendElement(entry); - } - } - retval.Sort(PerformanceEntryComparator()); -} - -void -nsPerformance::ClearUserEntries(const Optional& aEntryName, - const nsAString& aEntryType) -{ - for (uint32_t i = 0; i < mUserEntries.Length();) { - if ((!aEntryName.WasPassed() || - mUserEntries[i]->GetName().Equals(aEntryName.Value())) && - (aEntryType.IsEmpty() || - mUserEntries[i]->GetEntryType().Equals(aEntryType))) { - mUserEntries.RemoveElementAt(i); - } else { - ++i; - } - } -} - -void -nsPerformance::ClearResourceTimings() -{ - MOZ_ASSERT(NS_IsMainThread()); - mResourceEntries.Clear(); -} - -void -nsPerformance::SetResourceTimingBufferSize(uint64_t maxSize) -{ - MOZ_ASSERT(NS_IsMainThread()); - mResourceTimingBufferSize = maxSize; -} - /** * An entry should be added only after the resource is loaded. * This method is not thread safe and can only be called on the main thread. @@ -609,7 +525,7 @@ nsPerformance::AddEntry(nsIHttpChannel* channel, } // Don't add the entry if the buffer is full - if (mResourceEntries.Length() >= mResourceTimingBufferSize) { + if (IsResourceEntryLimitReached()) { return; } @@ -652,174 +568,6 @@ nsPerformance::AddEntry(nsIHttpChannel* channel, } } -bool -nsPerformance::PerformanceEntryComparator::Equals( - const PerformanceEntry* aElem1, - const PerformanceEntry* aElem2) const -{ - MOZ_ASSERT(aElem1 && aElem2, - "Trying to compare null performance entries"); - return aElem1->StartTime() == aElem2->StartTime(); -} - -bool -nsPerformance::PerformanceEntryComparator::LessThan( - const PerformanceEntry* aElem1, - const PerformanceEntry* aElem2) const -{ - MOZ_ASSERT(aElem1 && aElem2, - "Trying to compare null performance entries"); - return aElem1->StartTime() < aElem2->StartTime(); -} - -void -nsPerformance::InsertResourceEntry(PerformanceEntry* aEntry) -{ - MOZ_ASSERT(aEntry); - MOZ_ASSERT(mResourceEntries.Length() < mResourceTimingBufferSize); - if (mResourceEntries.Length() >= mResourceTimingBufferSize) { - return; - } - mResourceEntries.InsertElementSorted(aEntry, - PerformanceEntryComparator()); - if (mResourceEntries.Length() == mResourceTimingBufferSize) { - // call onresourcetimingbufferfull - DispatchBufferFullEvent(); - } -} - -void -nsPerformance::InsertUserEntry(PerformanceEntry* aEntry) -{ - if (nsContentUtils::IsUserTimingLoggingEnabled()) { - nsAutoCString uri; - nsresult rv = mWindow->GetDocumentURI()->GetHost(uri); - if(NS_FAILED(rv)) { - // If we have no URI, just put in "none". - uri.AssignLiteral("none"); - } - PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64 "\n", - uri.get(), - NS_ConvertUTF16toUTF8(aEntry->GetEntryType()).get(), - NS_ConvertUTF16toUTF8(aEntry->GetName()).get(), - aEntry->StartTime(), - aEntry->Duration(), - static_cast(PR_Now() / PR_USEC_PER_MSEC)); - } - mUserEntries.InsertElementSorted(aEntry, - PerformanceEntryComparator()); -} - -void -nsPerformance::Mark(const nsAString& aName, ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - // Don't add the entry if the buffer is full. XXX should be removed by bug 1159003. - if (mUserEntries.Length() >= mResourceTimingBufferSize) { - return; - } - if (IsPerformanceTimingAttribute(aName)) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return; - } - nsRefPtr performanceMark = - new PerformanceMark(this, aName); - InsertUserEntry(performanceMark); -} - -void -nsPerformance::ClearMarks(const Optional& aName) -{ - MOZ_ASSERT(NS_IsMainThread()); - ClearUserEntries(aName, NS_LITERAL_STRING("mark")); -} - -DOMHighResTimeStamp -nsPerformance::ResolveTimestampFromName(const nsAString& aName, - ErrorResult& aRv) -{ - nsAutoTArray, 1> arr; - DOMHighResTimeStamp ts; - Optional typeParam; - nsAutoString str; - str.AssignLiteral("mark"); - typeParam = &str; - GetEntriesByName(aName, typeParam, arr); - if (!arr.IsEmpty()) { - return arr.LastElement()->StartTime(); - } - if (!IsPerformanceTimingAttribute(aName)) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return 0; - } - ts = GetPerformanceTimingFromString(aName); - if (!ts) { - aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); - return 0; - } - return ConvertDOMMilliSecToHighRes(ts); -} - -void -nsPerformance::Measure(const nsAString& aName, - const Optional& aStartMark, - const Optional& aEndMark, - ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - // Don't add the entry if the buffer is full. XXX should be removed by bug 1159003. - if (mUserEntries.Length() >= mResourceTimingBufferSize) { - return; - } - DOMHighResTimeStamp startTime; - DOMHighResTimeStamp endTime; - - if (IsPerformanceTimingAttribute(aName)) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return; - } - - if (aStartMark.WasPassed()) { - startTime = ResolveTimestampFromName(aStartMark.Value(), aRv); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - } else { - // Navigation start is used in this case, but since DOMHighResTimeStamp is - // in relation to navigation start, this will be zero if a name is not - // passed. - startTime = 0; - } - if (aEndMark.WasPassed()) { - endTime = ResolveTimestampFromName(aEndMark.Value(), aRv); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - } else { - endTime = Now(); - } - nsRefPtr performanceMeasure = - new PerformanceMeasure(this, aName, startTime, endTime); - InsertUserEntry(performanceMeasure); -} - -void -nsPerformance::ClearMeasures(const Optional& aName) -{ - MOZ_ASSERT(NS_IsMainThread()); - ClearUserEntries(aName, NS_LITERAL_STRING("measure")); -} - -DOMHighResTimeStamp -nsPerformance::ConvertDOMMilliSecToHighRes(DOMTimeMilliSec aTime) { - // If the time we're trying to convert is equal to zero, it hasn't been set - // yet so just return 0. - if (aTime == 0) { - return 0; - } - return aTime - GetDOMTiming()->GetNavigationStart(); -} - // To be removed once bug 1124165 lands bool nsPerformance::IsPerformanceTimingAttribute(const nsAString& aName) @@ -841,7 +589,7 @@ nsPerformance::IsPerformanceTimingAttribute(const nsAString& aName) return false; } -DOMTimeMilliSec +DOMHighResTimeStamp nsPerformance::GetPerformanceTimingFromString(const nsAString& aProperty) { if (!IsPerformanceTimingAttribute(aProperty)) { @@ -913,3 +661,346 @@ nsPerformance::GetPerformanceTimingFromString(const nsAString& aProperty) return 0; } +namespace { + +// Helper classes +class MOZ_STACK_CLASS PerformanceEntryComparator final +{ +public: + bool Equals(const PerformanceEntry* aElem1, + const PerformanceEntry* aElem2) const + { + MOZ_ASSERT(aElem1 && aElem2, + "Trying to compare null performance entries"); + return aElem1->StartTime() == aElem2->StartTime(); + } + + bool LessThan(const PerformanceEntry* aElem1, + const PerformanceEntry* aElem2) const + { + MOZ_ASSERT(aElem1 && aElem2, + "Trying to compare null performance entries"); + return aElem1->StartTime() < aElem2->StartTime(); + } +}; + +class PrefEnabledRunnable final : public WorkerMainThreadRunnable +{ +public: + explicit PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate) + : WorkerMainThreadRunnable(aWorkerPrivate) + , mEnabled(false) + { } + + bool MainThreadRun() override + { + MOZ_ASSERT(NS_IsMainThread()); + mEnabled = Preferences::GetBool("dom.enable_user_timing", false); + return true; + } + + bool IsEnabled() const + { + return mEnabled; + } + +private: + bool mEnabled; +}; + +} // anonymous namespace + +/* static */ bool +nsPerformance::IsEnabled(JSContext* aCx, JSObject* aGlobal) +{ + if (NS_IsMainThread()) { + return Preferences::GetBool("dom.enable_user_timing", false); + } + + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + workerPrivate->AssertIsOnWorkerThread(); + + nsRefPtr runnable = + new PrefEnabledRunnable(workerPrivate); + runnable->Dispatch(workerPrivate->GetJSContext()); + + return runnable->IsEnabled(); +} + +void +nsPerformance::InsertUserEntry(PerformanceEntry* aEntry) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (nsContentUtils::IsUserTimingLoggingEnabled()) { + nsAutoCString uri; + nsresult rv = GetOwner()->GetDocumentURI()->GetHost(uri); + if(NS_FAILED(rv)) { + // If we have no URI, just put in "none". + uri.AssignLiteral("none"); + } + PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64 "\n", + uri.get(), + NS_ConvertUTF16toUTF8(aEntry->GetEntryType()).get(), + NS_ConvertUTF16toUTF8(aEntry->GetName()).get(), + aEntry->StartTime(), + aEntry->Duration(), + static_cast(PR_Now() / PR_USEC_PER_MSEC)); + } + + PerformanceBase::InsertUserEntry(aEntry); +} + +DOMHighResTimeStamp +nsPerformance::DeltaFromNavigationStart(DOMHighResTimeStamp aTime) +{ + // If the time we're trying to convert is equal to zero, it hasn't been set + // yet so just return 0. + if (aTime == 0) { + return 0; + } + return aTime - GetDOMTiming()->GetNavigationStart(); +} + +// PerformanceBase + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PerformanceBase) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_CYCLE_COLLECTION_INHERITED(PerformanceBase, + DOMEventTargetHelper, + mUserEntries, + mResourceEntries); + +NS_IMPL_ADDREF_INHERITED(PerformanceBase, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(PerformanceBase, DOMEventTargetHelper) + +PerformanceBase::PerformanceBase() + : mResourceTimingBufferSize(kDefaultResourceTimingBufferSize) +{ + MOZ_ASSERT(!NS_IsMainThread()); +} + +PerformanceBase::PerformanceBase(nsPIDOMWindow* aWindow) + : DOMEventTargetHelper(aWindow) + , mResourceTimingBufferSize(kDefaultResourceTimingBufferSize) +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +PerformanceBase::~PerformanceBase() +{} + +void +PerformanceBase::GetEntries(nsTArray>& aRetval) +{ + aRetval = mResourceEntries; + aRetval.AppendElements(mUserEntries); + aRetval.Sort(PerformanceEntryComparator()); +} + +void +PerformanceBase::GetEntriesByType(const nsAString& aEntryType, + nsTArray>& aRetval) +{ + if (aEntryType.EqualsLiteral("resource")) { + aRetval = mResourceEntries; + return; + } + + aRetval.Clear(); + + if (aEntryType.EqualsLiteral("mark") || + aEntryType.EqualsLiteral("measure")) { + for (PerformanceEntry* entry : mUserEntries) { + if (entry->GetEntryType().Equals(aEntryType)) { + aRetval.AppendElement(entry); + } + } + } +} + +void +PerformanceBase::GetEntriesByName(const nsAString& aName, + const Optional& aEntryType, + nsTArray>& aRetval) +{ + aRetval.Clear(); + + for (PerformanceEntry* entry : mResourceEntries) { + if (entry->GetName().Equals(aName) && + (!aEntryType.WasPassed() || + entry->GetEntryType().Equals(aEntryType.Value()))) { + aRetval.AppendElement(entry); + } + } + + for (PerformanceEntry* entry : mUserEntries) { + if (entry->GetName().Equals(aName) && + (!aEntryType.WasPassed() || + entry->GetEntryType().Equals(aEntryType.Value()))) { + aRetval.AppendElement(entry); + } + } + + aRetval.Sort(PerformanceEntryComparator()); +} + +void +PerformanceBase::ClearUserEntries(const Optional& aEntryName, + const nsAString& aEntryType) +{ + for (uint32_t i = 0; i < mUserEntries.Length();) { + if ((!aEntryName.WasPassed() || + mUserEntries[i]->GetName().Equals(aEntryName.Value())) && + (aEntryType.IsEmpty() || + mUserEntries[i]->GetEntryType().Equals(aEntryType))) { + mUserEntries.RemoveElementAt(i); + } else { + ++i; + } + } +} + +void +PerformanceBase::ClearResourceTimings() +{ + MOZ_ASSERT(NS_IsMainThread()); + mResourceEntries.Clear(); +} + +void +PerformanceBase::Mark(const nsAString& aName, ErrorResult& aRv) +{ + // Don't add the entry if the buffer is full. XXX should be removed by bug 1159003. + if (mUserEntries.Length() >= mResourceTimingBufferSize) { + return; + } + + if (IsPerformanceTimingAttribute(aName)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; + } + + nsRefPtr performanceMark = + new PerformanceMark(GetAsISupports(), aName, Now()); + InsertUserEntry(performanceMark); +} + +void +PerformanceBase::ClearMarks(const Optional& aName) +{ + ClearUserEntries(aName, NS_LITERAL_STRING("mark")); +} + +DOMHighResTimeStamp +PerformanceBase::ResolveTimestampFromName(const nsAString& aName, + ErrorResult& aRv) +{ + nsAutoTArray, 1> arr; + DOMHighResTimeStamp ts; + Optional typeParam; + nsAutoString str; + str.AssignLiteral("mark"); + typeParam = &str; + GetEntriesByName(aName, typeParam, arr); + if (!arr.IsEmpty()) { + return arr.LastElement()->StartTime(); + } + + if (!IsPerformanceTimingAttribute(aName)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return 0; + } + + ts = GetPerformanceTimingFromString(aName); + if (!ts) { + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return 0; + } + + return DeltaFromNavigationStart(ts); +} + +void +PerformanceBase::Measure(const nsAString& aName, + const Optional& aStartMark, + const Optional& aEndMark, + ErrorResult& aRv) +{ + // Don't add the entry if the buffer is full. XXX should be removed by bug + // 1159003. + if (mUserEntries.Length() >= mResourceTimingBufferSize) { + return; + } + + DOMHighResTimeStamp startTime; + DOMHighResTimeStamp endTime; + + if (IsPerformanceTimingAttribute(aName)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; + } + + if (aStartMark.WasPassed()) { + startTime = ResolveTimestampFromName(aStartMark.Value(), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + } else { + // Navigation start is used in this case, but since DOMHighResTimeStamp is + // in relation to navigation start, this will be zero if a name is not + // passed. + startTime = 0; + } + + if (aEndMark.WasPassed()) { + endTime = ResolveTimestampFromName(aEndMark.Value(), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + } else { + endTime = Now(); + } + + nsRefPtr performanceMeasure = + new PerformanceMeasure(GetAsISupports(), aName, startTime, endTime); + InsertUserEntry(performanceMeasure); +} + +void +PerformanceBase::ClearMeasures(const Optional& aName) +{ + ClearUserEntries(aName, NS_LITERAL_STRING("measure")); +} + +void +PerformanceBase::InsertUserEntry(PerformanceEntry* aEntry) +{ + mUserEntries.InsertElementSorted(aEntry, + PerformanceEntryComparator()); +} + +void +PerformanceBase::SetResourceTimingBufferSize(uint64_t aMaxSize) +{ + mResourceTimingBufferSize = aMaxSize; +} + +void +PerformanceBase::InsertResourceEntry(PerformanceEntry* aEntry) +{ + MOZ_ASSERT(aEntry); + MOZ_ASSERT(mResourceEntries.Length() < mResourceTimingBufferSize); + if (mResourceEntries.Length() >= mResourceTimingBufferSize) { + return; + } + + mResourceEntries.InsertElementSorted(aEntry, + PerformanceEntryComparator()); + if (mResourceEntries.Length() == mResourceTimingBufferSize) { + // call onresourcetimingbufferfull + DispatchBufferFullEvent(); + } +} diff --git a/dom/base/nsPerformance.h b/dom/base/nsPerformance.h index b727653c118..c39f8e1934f 100644 --- a/dom/base/nsPerformance.h +++ b/dom/base/nsPerformance.h @@ -288,18 +288,90 @@ private: nsRefPtr mPerformance; }; -// Script "performance" object -class nsPerformance final : public mozilla::DOMEventTargetHelper +// Base class for main-thread and worker Performance API +class PerformanceBase : public mozilla::DOMEventTargetHelper { public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PerformanceBase, + DOMEventTargetHelper) + + PerformanceBase(); + explicit PerformanceBase(nsPIDOMWindow* aWindow); + typedef mozilla::dom::PerformanceEntry PerformanceEntry; + + void GetEntries(nsTArray>& aRetval); + void GetEntriesByType(const nsAString& aEntryType, + nsTArray>& aRetval); + void GetEntriesByName(const nsAString& aName, + const mozilla::dom::Optional& aEntryType, + nsTArray>& aRetval); + void ClearResourceTimings(); + + virtual DOMHighResTimeStamp Now() const = 0; + + void Mark(const nsAString& aName, mozilla::ErrorResult& aRv); + void ClearMarks(const mozilla::dom::Optional& aName); + void Measure(const nsAString& aName, + const mozilla::dom::Optional& aStartMark, + const mozilla::dom::Optional& aEndMark, + mozilla::ErrorResult& aRv); + void ClearMeasures(const mozilla::dom::Optional& aName); + + void SetResourceTimingBufferSize(uint64_t aMaxSize); + +protected: + virtual ~PerformanceBase(); + + virtual void InsertUserEntry(PerformanceEntry* aEntry); + void InsertResourceEntry(PerformanceEntry* aEntry); + + void ClearUserEntries(const mozilla::dom::Optional& aEntryName, + const nsAString& aEntryType); + + DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName, + mozilla::ErrorResult& aRv); + + virtual nsISupports* GetAsISupports() = 0; + + virtual void DispatchBufferFullEvent() = 0; + + virtual DOMHighResTimeStamp + DeltaFromNavigationStart(DOMHighResTimeStamp aTime) = 0; + + virtual bool IsPerformanceTimingAttribute(const nsAString& aName) = 0; + + virtual DOMHighResTimeStamp + GetPerformanceTimingFromString(const nsAString& aTimingName) = 0; + + bool IsResourceEntryLimitReached() const + { + return mResourceEntries.Length() >= mResourceTimingBufferSize; + } + +private: + nsTArray> mUserEntries; + nsTArray> mResourceEntries; + + uint64_t mResourceTimingBufferSize; + static const uint64_t kDefaultResourceTimingBufferSize = 150; +}; + +// Script "performance" object +class nsPerformance final : public PerformanceBase +{ +public: nsPerformance(nsPIDOMWindow* aWindow, nsDOMNavigationTiming* aDOMTiming, nsITimedChannel* aChannel, nsPerformance* aParentPerformance); NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsPerformance, DOMEventTargetHelper) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsPerformance, + PerformanceBase) + + static bool IsEnabled(JSContext* aCx, JSObject* aGlobal); nsDOMNavigationTiming* GetDOMTiming() const { @@ -316,35 +388,28 @@ public: return mParentPerformance; } - nsPIDOMWindow* GetParentObject() const - { - return mWindow.get(); - } - - virtual JSObject* WrapObject(JSContext *cx, JS::Handle aGivenProto) override; + JSObject* WrapObject(JSContext *cx, + JS::Handle aGivenProto) override; // Performance WebIDL methods - DOMHighResTimeStamp Now(); + DOMHighResTimeStamp Now() const override; + nsPerformanceTiming* Timing(); nsPerformanceNavigation* Navigation(); - void GetEntries(nsTArray>& retval); - void GetEntriesByType(const nsAString& entryType, - nsTArray>& retval); - void GetEntriesByName(const nsAString& name, - const mozilla::dom::Optional< nsAString >& entryType, - nsTArray>& retval); void AddEntry(nsIHttpChannel* channel, nsITimedChannel* timedChannel); - void ClearResourceTimings(); - void SetResourceTimingBufferSize(uint64_t maxSize); - void Mark(const nsAString& aName, mozilla::ErrorResult& aRv); - void ClearMarks(const mozilla::dom::Optional& aName); - void Measure(const nsAString& aName, - const mozilla::dom::Optional& aStartMark, - const mozilla::dom::Optional& aEndMark, - mozilla::ErrorResult& aRv); - void ClearMeasures(const mozilla::dom::Optional& aName); + + using PerformanceBase::GetEntries; + using PerformanceBase::GetEntriesByType; + using PerformanceBase::GetEntriesByName; + using PerformanceBase::ClearResourceTimings; + + using PerformanceBase::Mark; + using PerformanceBase::ClearMarks; + using PerformanceBase::Measure; + using PerformanceBase::ClearMeasures; + using PerformanceBase::SetResourceTimingBufferSize; void GetMozMemory(JSContext *aCx, JS::MutableHandle aObj); @@ -352,36 +417,30 @@ public: private: ~nsPerformance(); - bool IsPerformanceTimingAttribute(const nsAString& aName); - DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName, mozilla::ErrorResult& aRv); - DOMTimeMilliSec GetPerformanceTimingFromString(const nsAString& aTimingName); - DOMHighResTimeStamp ConvertDOMMilliSecToHighRes(const DOMTimeMilliSec aTime); - void DispatchBufferFullEvent(); + + nsISupports* GetAsISupports() override + { + return this; + } + void InsertUserEntry(PerformanceEntry* aEntry); - void ClearUserEntries(const mozilla::dom::Optional& aEntryName, - const nsAString& aEntryType); - void InsertResourceEntry(PerformanceEntry* aEntry); - nsCOMPtr mWindow; + + bool IsPerformanceTimingAttribute(const nsAString& aName) override; + + DOMHighResTimeStamp + DeltaFromNavigationStart(DOMHighResTimeStamp aTime) override; + + DOMHighResTimeStamp + GetPerformanceTimingFromString(const nsAString& aTimingName) override; + + void DispatchBufferFullEvent() override; + nsRefPtr mDOMTiming; nsCOMPtr mChannel; nsRefPtr mTiming; nsRefPtr mNavigation; - nsTArray> mResourceEntries; - nsTArray> mUserEntries; nsRefPtr mParentPerformance; - uint64_t mResourceTimingBufferSize; JS::Heap mMozMemory; - - static const uint64_t kDefaultResourceTimingBufferSize = 150; - - // Helper classes - class PerformanceEntryComparator { - public: - bool Equals(const PerformanceEntry* aElem1, - const PerformanceEntry* aElem2) const; - bool LessThan(const PerformanceEntry* aElem1, - const PerformanceEntry* aElem2) const; - }; }; inline nsDOMNavigationTiming* diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 3d5611e6a47..ac03e628a98 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -243,6 +243,7 @@ support-files = wholeTexty-helper.xml file_nonascii_blob_url.html referrerHelper.js + test_performance_user_timing.js [test_anonymousContent_api.html] [test_anonymousContent_append_after_reflow.html] diff --git a/dom/base/test/test_performance_user_timing.html b/dom/base/test/test_performance_user_timing.html index 1666daf8186..e302b38560e 100644 --- a/dom/base/test/test_performance_user_timing.html +++ b/dom/base/test/test_performance_user_timing.html @@ -7,6 +7,7 @@ Test for Bug 782751 + Mozilla Bug 782751 - User Timing API @@ -15,271 +16,6 @@
             
+  
+  
+    
+  
+
diff --git a/dom/workers/test/test_sharedWorker_performance_user_timing.html b/dom/workers/test/test_sharedWorker_performance_user_timing.html
new file mode 100644
index 00000000000..c9ae26b0c12
--- /dev/null
+++ b/dom/workers/test/test_sharedWorker_performance_user_timing.html
@@ -0,0 +1,30 @@
+
+
+
+  
+    Test for worker performance timing API
+    
+    
+  
+  
+    
+  
+
diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js
index 5c7b4d20e30..17ad12c5bc4 100644
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -144,6 +144,12 @@ var interfaceNamesInGlobalScope =
     "MessagePort",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Performance",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceEntry",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceMark",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceMeasure",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
diff --git a/dom/workers/test/worker_performance_user_timing.js b/dom/workers/test/worker_performance_user_timing.js
new file mode 100644
index 00000000000..8e1656426ab
--- /dev/null
+++ b/dom/workers/test/worker_performance_user_timing.js
@@ -0,0 +1,24 @@
+function ok(a, msg) {
+  dump("OK: " + !!a + "  =>  " + a + " " + msg + "\n");
+  postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
+}
+
+function is(a, b, msg) {
+  dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+  postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
+}
+
+function isnot(a, b, msg) {
+  dump("ISNOT: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+  postMessage({type: 'status', status: a != b, msg: a + " != " + b + ": " + msg });
+}
+
+importScripts(['../../../dom/base/test/test_performance_user_timing.js']);
+
+for (var i = 0; i < steps.length; ++i) {
+  performance.clearMarks();
+  performance.clearMeasures();
+  steps[i]();
+}
+
+postMessage({type: 'finish'});

From c3d731247ca83fdf373154cf68ee2250f9d41439 Mon Sep 17 00:00:00 2001
From: Nicolas Silva 
Date: Fri, 22 May 2015 13:38:13 +0200
Subject: [PATCH 073/107] Bug 1150549 - Simplify TiledContentHost. r=jrmuizel

---
 gfx/layers/Effects.h                      |   8 +-
 gfx/layers/TiledLayerBuffer.h             |  81 ++-
 gfx/layers/client/TiledContentClient.cpp  |  29 +-
 gfx/layers/client/TiledContentClient.h    |  12 +-
 gfx/layers/composite/TextureHost.h        |   1 +
 gfx/layers/composite/TiledContentHost.cpp | 600 ++++++++++------------
 gfx/layers/composite/TiledContentHost.h   |  71 ++-
 gfx/layers/ipc/LayersMessages.ipdlh       |   3 +
 8 files changed, 407 insertions(+), 398 deletions(-)

diff --git a/gfx/layers/Effects.h b/gfx/layers/Effects.h
index f32098d9a3d..128b84ada3b 100644
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -285,8 +285,14 @@ CreateTexturedEffect(TextureSource* aSource,
 {
   MOZ_ASSERT(aSource);
   if (aSourceOnWhite) {
+    if ((aSource->GetFormat() != gfx::SurfaceFormat::R8G8B8X8 &&
+         aSource->GetFormat() != gfx::SurfaceFormat::B8G8R8X8) ||
+        aSource->GetFormat() != aSourceOnWhite->GetFormat()) {
+      printf_stderr("XXXX - source %i - on white %i\n", (int)aSource->GetFormat(), (int)aSourceOnWhite->GetFormat());
+    }
     MOZ_ASSERT(aSource->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
-               aSourceOnWhite->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+               aSource->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+    MOZ_ASSERT(aSource->GetFormat() == aSourceOnWhite->GetFormat());
     return MakeAndAddRef(aSource, aSourceOnWhite, aFilter);
   }
 
diff --git a/gfx/layers/TiledLayerBuffer.h b/gfx/layers/TiledLayerBuffer.h
index 717676b92ef..c38a4bffcd9 100644
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -94,7 +94,9 @@ class TiledLayerBuffer
 {
 public:
   TiledLayerBuffer()
-    : mRetainedWidth(0)
+    : mFirstTileX(0)
+    , mFirstTileY(0)
+    , mRetainedWidth(0)
     , mRetainedHeight(0)
     , mResolution(1)
     , mTileSize(gfxPlatform::GetPlatform()->GetTileWidth(), gfxPlatform::GetPlatform()->GetTileHeight())
@@ -108,13 +110,21 @@ public:
   //       (aTileOrigin.x, aTileOrigin.y,
   //        GetScaledTileSize().width, GetScaledTileSize().height)
   //       and GetValidRegion() to get the area of the tile that is valid.
-  Tile GetTile(const nsIntPoint& aTileOrigin) const;
-
+  Tile& GetTile(const gfx::IntPoint& aTileOrigin);
   // Given a tile x, y relative to the top left of the layer, this function
   // will return the tile for
   // (x*GetScaledTileSize().width, y*GetScaledTileSize().height,
   //  GetScaledTileSize().width, GetScaledTileSize().height)
-  Tile GetTile(int x, int y) const;
+  Tile& GetTile(int x, int y);
+
+  int TileIndex(const gfx::IntPoint& aTileOrigin) const;
+  int TileIndex(int x, int y) const { return x * mRetainedHeight + y; }
+
+  bool HasTile(int index) const { return index >= 0 && index < (int)mRetainedTiles.Length(); }
+  bool HasTile(const gfx::IntPoint& aTileOrigin) const;
+  bool HasTile(int x, int y) const {
+    return x >= 0 && x < mRetainedWidth && y >= 0 && y < mRetainedHeight;
+  }
 
   const gfx::IntSize& GetTileSize() const { return mTileSize; }
 
@@ -155,14 +165,6 @@ public:
   // individual tile's rect in relation to the valid region.
   // Setting the resolution will invalidate the buffer.
   float GetResolution() const { return mResolution; }
-  void SetResolution(float aResolution) {
-    if (mResolution == aResolution) {
-      return;
-    }
-
-    Update(nsIntRegion(), nsIntRegion());
-    mResolution = aResolution;
-  }
   bool IsLowPrecision() const { return mResolution < 1; }
 
   typedef Tile* Iterator;
@@ -178,6 +180,10 @@ protected:
   // to the implementor.
   void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
 
+  // Return a reference to this tile in GetTile when the requested tile offset
+  // does not exist.
+  Tile mPlaceHolderTile;
+
   nsIntRegion     mValidRegion;
   nsIntRegion     mPaintedRegion;
 
@@ -190,6 +196,8 @@ protected:
    * tiles is scaled by mResolution.
    */
   nsTArray  mRetainedTiles;
+  int             mFirstTileX;
+  int             mFirstTileY;
   int             mRetainedWidth;  // in tiles
   int             mRetainedHeight; // in tiles
   float           mResolution;
@@ -249,24 +257,39 @@ static inline int floor_div(int a, int b)
   }
 }
 
-template Tile
-TiledLayerBuffer::GetTile(const nsIntPoint& aTileOrigin) const
+template bool
+TiledLayerBuffer::HasTile(const gfx::IntPoint& aTileOrigin) const {
+  gfx::IntSize scaledTileSize = GetScaledTileSize();
+  return HasTile(floor_div(aTileOrigin.x, scaledTileSize.width) - mFirstTileX,
+                 floor_div(aTileOrigin.y, scaledTileSize.height) - mFirstTileY);
+}
+
+template Tile&
+TiledLayerBuffer::GetTile(const nsIntPoint& aTileOrigin)
+{
+  if (HasTile(aTileOrigin)) {
+    return mRetainedTiles[TileIndex(aTileOrigin)];
+  }
+  return mPlaceHolderTile;
+}
+
+template int
+TiledLayerBuffer::TileIndex(const gfx::IntPoint& aTileOrigin) const
 {
-  // TODO Cache firstTileOriginX/firstTileOriginY
   // Find the tile x/y of the first tile and the target tile relative to the (0, 0)
   // origin, the difference is the tile x/y relative to the start of the tile buffer.
   gfx::IntSize scaledTileSize = GetScaledTileSize();
-  int firstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
-  int firstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
-  return GetTile(floor_div(aTileOrigin.x, scaledTileSize.width) - firstTileX,
-                 floor_div(aTileOrigin.y, scaledTileSize.height) - firstTileY);
+  return TileIndex(floor_div(aTileOrigin.x, scaledTileSize.width) - mFirstTileX,
+                   floor_div(aTileOrigin.y, scaledTileSize.height) - mFirstTileY);
 }
 
-template Tile
-TiledLayerBuffer::GetTile(int x, int y) const
+template Tile&
+TiledLayerBuffer::GetTile(int x, int y)
 {
-  int index = x * mRetainedHeight + y;
-  return mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
+  if (HasTile(x, y)) {
+    return mRetainedTiles[TileIndex(x, y)];
+  }
+  return mPlaceHolderTile;
 }
 
 template void
@@ -282,15 +305,15 @@ TiledLayerBuffer::Dump(std::stringstream& aStream,
 
     for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
       int32_t tileStartY = GetTileStart(y, scaledTileSize.height);
-      Tile tileTexture =
-        GetTile(nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
-                           RoundDownToTileEdge(y, scaledTileSize.height)));
+      nsIntPoint tileOrigin = nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
+                                         RoundDownToTileEdge(y, scaledTileSize.height));
+      Tile& tileTexture = GetTile(tileOrigin);
       int32_t h = scaledTileSize.height - tileStartY;
 
       aStream << "\n" << aPrefix << "Tile (x=" <<
         RoundDownToTileEdge(x, scaledTileSize.width) << ", y=" <<
         RoundDownToTileEdge(y, scaledTileSize.height) << "): ";
-      if (tileTexture != AsDerived().GetPlaceholderTile()) {
+      if (!tileTexture.IsPlaceholderTile()) {
         tileTexture.DumpTexture(aStream);
       } else {
         aStream << "empty tile";
@@ -597,6 +620,10 @@ TiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
 
   mRetainedTiles = newRetainedTiles;
   mValidRegion = newValidRegion;
+
+  mFirstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
+  mFirstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
+
   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp
index 0c07cd3865d..26314d9663f 100644
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -374,7 +374,7 @@ int32_t
 gfxMemorySharedReadLock::ReadUnlock()
 {
   int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount);
-  NS_ASSERTION(readCount >= 0, "ReadUnlock called without ReadLock.");
+  MOZ_ASSERT(readCount >= 0);
 
   return readCount;
 }
@@ -424,7 +424,7 @@ gfxShmSharedReadLock::ReadUnlock() {
   }
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
-  NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock.");
+  MOZ_ASSERT(readCount >= 0);
   if (readCount <= 0) {
     mAllocator->FreeShmemSection(mShmemSection);
   }
@@ -520,6 +520,7 @@ TileClient::TileClient(const TileClient& o)
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
+  mUpdateRect = o.mUpdateRect;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
@@ -539,6 +540,7 @@ TileClient::operator=(const TileClient& o)
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
+  mUpdateRect = o.mUpdateRect;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
@@ -609,6 +611,8 @@ CopyFrontToBack(TextureClient* aFront,
 
   gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
   aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
+
+  aFront->Unlock();
   return true;
 }
 
@@ -705,9 +709,9 @@ TileClient::DiscardBackBuffer()
        mManager->ReportClientLost(*mBackBufferOnWhite);
      }
     } else {
-      mManager->ReturnTextureClient(*mBackBuffer);
+      mManager->ReturnTextureClientDeferred(*mBackBuffer);
       if (mBackBufferOnWhite) {
-        mManager->ReturnTextureClient(*mBackBufferOnWhite);
+        mManager->ReturnTextureClientDeferred(*mBackBufferOnWhite);
       }
     }
     mBackLock->ReadUnlock();
@@ -816,23 +820,17 @@ TileClient::GetTileDescriptor()
   if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) {
     return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
                                   mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
+                                  mUpdateRect,
                                   TileLock(uintptr_t(mFrontLock.get())));
   } else {
     gfxShmSharedReadLock *lock = static_cast(mFrontLock.get());
     return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
                                   mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
+                                  mUpdateRect,
                                   TileLock(lock->GetShmemSection()));
   }
 }
 
-void
-ClientTiledLayerBuffer::ReadUnlock() {
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    if (mRetainedTiles[i].IsPlaceholderTile()) continue;
-    mRetainedTiles[i].ReadUnlock();
-  }
-}
-
 void
 ClientTiledLayerBuffer::ReadLock() {
   for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
@@ -873,9 +871,12 @@ ClientTiledLayerBuffer::GetSurfaceDescriptorTiles()
       tileDesc = mRetainedTiles[i].GetTileDescriptor();
     }
     tiles.AppendElement(tileDesc);
+    mRetainedTiles[i].mUpdateRect = IntRect();
   }
   return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion,
-                                tiles, mRetainedWidth, mRetainedHeight,
+                                tiles,
+                                mFirstTileX, mFirstTileY,
+                                mRetainedWidth, mRetainedHeight,
                                 mResolution, mFrameResolution.xScale,
                                 mFrameResolution.yScale);
 }
@@ -1136,6 +1137,8 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
                         &createdTextureClient, extraPainted,
                         &backBufferOnWhite);
 
+  aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
+
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
   mPaintedRegion.Or(mPaintedRegion, extraPainted);
diff --git a/gfx/layers/client/TiledContentClient.h b/gfx/layers/client/TiledContentClient.h
index 80a1a4b2f57..6dfb20e1c0a 100644
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -270,6 +270,7 @@ struct TileClient
   RefPtr mBackLock;
   RefPtr mFrontLock;
   RefPtr mManager;
+  gfx::IntRect mUpdateRect;
   CompositableClient* mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   TimeStamp        mLastUpdate;
@@ -418,8 +419,6 @@ public:
                    LayerManager::DrawPaintedLayerCallback aCallback,
                    void* aCallbackData);
 
-  void ReadUnlock();
-
   void ReadLock();
 
   void Release();
@@ -445,6 +444,15 @@ public:
 
   SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
 
+  void SetResolution(float aResolution) {
+    if (mResolution == aResolution) {
+      return;
+    }
+
+    Update(nsIntRegion(), nsIntRegion());
+    mResolution = aResolution;
+  }
+
 protected:
   TileClient ValidateTile(TileClient aTile,
                           const nsIntPoint& aTileRect,
diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h
index 68e8093385d..d842808c23a 100644
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -326,6 +326,7 @@ protected:
   virtual ~TextureHost();
 
 public:
+
   /**
    * Factory method.
    */
diff --git a/gfx/layers/composite/TiledContentHost.cpp b/gfx/layers/composite/TiledContentHost.cpp
index c21e94c7fe7..bf28b7d029e 100644
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -29,198 +29,28 @@ class Layer;
 
 TiledLayerBufferComposite::TiledLayerBufferComposite()
   : mFrameResolution()
-  , mHasDoubleBufferedTiles(false)
-  , mIsValid(false)
 {}
 
+TiledLayerBufferComposite::~TiledLayerBufferComposite()
+{
+  Clear();
+}
+
 /* static */ void
 TiledLayerBufferComposite::RecycleCallback(TextureHost* textureHost, void* aClosure)
 {
   textureHost->CompositorRecycle();
 }
 
-TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
-                                                     const SurfaceDescriptorTiles& aDescriptor,
-                                                     const nsIntRegion& aOldPaintedRegion,
-                                                     Compositor* aCompositor)
-{
-  mIsValid = true;
-  mHasDoubleBufferedTiles = false;
-  mValidRegion = aDescriptor.validRegion();
-  mPaintedRegion = aDescriptor.paintedRegion();
-  mRetainedWidth = aDescriptor.retainedWidth();
-  mRetainedHeight = aDescriptor.retainedHeight();
-  mResolution = aDescriptor.resolution();
-  mFrameResolution = CSSToParentLayerScale2D(aDescriptor.frameXResolution(),
-                                             aDescriptor.frameYResolution());
-  if (mResolution == 0 || IsNaN(mResolution)) {
-    // There are divisions by mResolution so this protects the compositor process
-    // against malicious content processes and fuzzing.
-    mIsValid = false;
-    return;
-  }
-
-  // Combine any valid content that wasn't already uploaded
-  nsIntRegion oldPaintedRegion(aOldPaintedRegion);
-  oldPaintedRegion.And(oldPaintedRegion, mValidRegion);
-  mPaintedRegion.Or(mPaintedRegion, oldPaintedRegion);
-
-  bool isSameProcess = aAllocator->IsSameProcess();
-
-  const InfallibleTArray& tiles = aDescriptor.tiles();
-  for(size_t i = 0; i < tiles.Length(); i++) {
-    CompositableTextureHostRef texture;
-    CompositableTextureHostRef textureOnWhite;
-    const TileDescriptor& tileDesc = tiles[i];
-    switch (tileDesc.type()) {
-      case TileDescriptor::TTexturedTileDescriptor : {
-        texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent());
-        MaybeTexture onWhite = tileDesc.get_TexturedTileDescriptor().textureOnWhite();
-        if (onWhite.type() == MaybeTexture::TPTextureParent) {
-          textureOnWhite = TextureHost::AsTextureHost(onWhite.get_PTextureParent());
-        }
-        const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock();
-        nsRefPtr sharedLock;
-        if (ipcLock.type() == TileLock::TShmemSection) {
-          sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
-        } else {
-          if (!isSameProcess) {
-            // Trying to use a memory based lock instead of a shmem based one in
-            // the cross-process case is a bad security violation.
-            NS_ERROR("A client process may be trying to peek at the host's address space!");
-            // This tells the TiledContentHost that deserialization failed so that
-            // it can propagate the error.
-            mIsValid = false;
-
-            mRetainedTiles.Clear();
-            return;
-          }
-          sharedLock = reinterpret_cast(ipcLock.get_uintptr_t());
-          if (sharedLock) {
-            // The corresponding AddRef is in TiledClient::GetTileDescriptor
-            sharedLock.get()->Release();
-          }
-        }
-
-        CompositableTextureSourceRef textureSource;
-        CompositableTextureSourceRef textureSourceOnWhite;
-        if (texture) {
-          texture->SetCompositor(aCompositor);
-          texture->PrepareTextureSource(textureSource);
-        }
-        if (textureOnWhite) {
-          textureOnWhite->SetCompositor(aCompositor);
-          textureOnWhite->PrepareTextureSource(textureSourceOnWhite);
-        }
-        mRetainedTiles.AppendElement(TileHost(sharedLock,
-                                              texture.get(),
-                                              textureOnWhite.get(),
-                                              textureSource.get(),
-                                              textureSourceOnWhite.get()));
-        break;
-      }
-      default:
-        NS_WARNING("Unrecognised tile descriptor type");
-        // Fall through
-      case TileDescriptor::TPlaceholderTileDescriptor :
-        mRetainedTiles.AppendElement(GetPlaceholderTile());
-        break;
-    }
-    if (texture && !texture->HasInternalBuffer()) {
-      mHasDoubleBufferedTiles = true;
-    }
-  }
-}
-
-void
-TiledLayerBufferComposite::ReadUnlock()
-{
-  if (!IsValid()) {
-    return;
-  }
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    mRetainedTiles[i].ReadUnlock();
-  }
-}
-
-void
-TiledLayerBufferComposite::ReleaseTextureHosts()
-{
-  if (!IsValid()) {
-    return;
-  }
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    mRetainedTiles[i].mTextureHost = nullptr;
-    mRetainedTiles[i].mTextureHostOnWhite = nullptr;
-    mRetainedTiles[i].mTextureSource = nullptr;
-    mRetainedTiles[i].mTextureSourceOnWhite = nullptr;
-  }
-}
-
-void
-TiledLayerBufferComposite::Upload()
-{
-  if(!IsValid()) {
-    return;
-  }
-  // The TextureClients were created with the TextureFlags::IMMEDIATE_UPLOAD flag,
-  // so calling Update on all the texture hosts will perform the texture upload.
-  Update(mValidRegion, mPaintedRegion);
-  ClearPaintedRegion();
-}
-
-TileHost
-TiledLayerBufferComposite::ValidateTile(TileHost aTile,
-                                        const IntPoint& aTileOrigin,
-                                        const nsIntRegion& aDirtyRect)
-{
-  if (aTile.IsPlaceholderTile()) {
-    NS_WARNING("Placeholder tile encountered in painted region");
-    return aTile;
-  }
-
-#ifdef GFX_TILEDLAYER_PREF_WARNINGS
-  printf_stderr("Upload tile %i, %i\n", aTileOrigin.x, aTileOrigin.y);
-  long start = PR_IntervalNow();
-#endif
-
-  MOZ_ASSERT(aTile.mTextureHost->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD);
-
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-  MOZ_ASSERT(!aTile.mTextureHostOnWhite);
-  // We possibly upload the entire texture contents here. This is a purposeful
-  // decision, as sub-image upload can often be slow and/or unreliable, but
-  // we may want to reevaluate this in the future.
-  // For !HasInternalBuffer() textures, this is likely a no-op.
-  aTile.mTextureHost->Updated(nullptr);
-#else
-  nsIntRegion tileUpdated = aDirtyRect.MovedBy(-aTileOrigin);
-  aTile.mTextureHost->Updated(&tileUpdated);
-  if (aTile.mTextureHostOnWhite) {
-    aTile.mTextureHostOnWhite->Updated(&tileUpdated);
-  }
-#endif
-
-#ifdef GFX_TILEDLAYER_PREF_WARNINGS
-  if (PR_IntervalNow() - start > 1) {
-    printf_stderr("Tile Time to upload %i\n", PR_IntervalNow() - start);
-  }
-#endif
-  return aTile;
-}
-
 void
 TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor)
 {
   MOZ_ASSERT(aCompositor);
-  if (!IsValid()) {
-    return;
-  }
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    if (mRetainedTiles[i].IsPlaceholderTile()) continue;
-    mRetainedTiles[i].mTextureHost->SetCompositor(aCompositor);
-    if (mRetainedTiles[i].mTextureHostOnWhite) {
-      mRetainedTiles[i].mTextureHostOnWhite->SetCompositor(aCompositor);
+  for (TileHost& tile : mRetainedTiles) {
+    if (tile.IsPlaceholderTile()) continue;
+    tile.mTextureHost->SetCompositor(aCompositor);
+    if (tile.mTextureHostOnWhite) {
+      tile.mTextureHostOnWhite->SetCompositor(aCompositor);
     }
   }
 }
@@ -229,10 +59,6 @@ TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
   : ContentHost(aTextureInfo)
   , mTiledBuffer(TiledLayerBufferComposite())
   , mLowPrecisionTiledBuffer(TiledLayerBufferComposite())
-  , mOldTiledBuffer(TiledLayerBufferComposite())
-  , mOldLowPrecisionTiledBuffer(TiledLayerBufferComposite())
-  , mPendingUpload(false)
-  , mPendingLowPrecisionUpload(false)
 {
   MOZ_COUNT_CTOR(TiledContentHost);
 }
@@ -240,28 +66,6 @@ TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
 TiledContentHost::~TiledContentHost()
 {
   MOZ_COUNT_DTOR(TiledContentHost);
-
-  // Unlock any buffers that may still be locked. If we have a pending upload,
-  // we will need to unlock the buffer that was about to be uploaded.
-  // If a buffer that was being composited had double-buffered tiles, we will
-  // need to unlock that buffer too.
-  if (mPendingUpload) {
-    mTiledBuffer.ReadUnlock();
-    if (mOldTiledBuffer.HasDoubleBufferedTiles()) {
-      mOldTiledBuffer.ReadUnlock();
-    }
-  } else if (mTiledBuffer.HasDoubleBufferedTiles()) {
-    mTiledBuffer.ReadUnlock();
-  }
-
-  if (mPendingLowPrecisionUpload) {
-    mLowPrecisionTiledBuffer.ReadUnlock();
-    if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-      mOldLowPrecisionTiledBuffer.ReadUnlock();
-    }
-  } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-    mLowPrecisionTiledBuffer.ReadUnlock();
-  }
 }
 
 void
@@ -277,33 +81,10 @@ TiledContentHost::Detach(Layer* aLayer,
                          AttachFlags aFlags /* = NO_FLAGS */)
 {
   if (!mKeepAttached || aLayer == mLayer || aFlags & FORCE_DETACH) {
-
-    // Unlock any buffers that may still be locked. If we have a pending upload,
-    // we will need to unlock the buffer that was about to be uploaded.
-    // If a buffer that was being composited had double-buffered tiles, we will
-    // need to unlock that buffer too.
-    if (mPendingUpload) {
-      mTiledBuffer.ReadUnlock();
-      if (mOldTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldTiledBuffer.ReadUnlock();
-      }
-    } else if (mTiledBuffer.HasDoubleBufferedTiles()) {
-      mTiledBuffer.ReadUnlock();
-    }
-
-    if (mPendingLowPrecisionUpload) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-      if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldLowPrecisionTiledBuffer.ReadUnlock();
-      }
-    } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-    }
-
-    mTiledBuffer = TiledLayerBufferComposite();
-    mLowPrecisionTiledBuffer = TiledLayerBufferComposite();
-    mOldTiledBuffer = TiledLayerBufferComposite();
-    mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
+    // Clear the TiledLayerBuffers, which will take care of releasing the
+    // copy-on-write locks.
+    mTiledBuffer.Clear();
+    mLowPrecisionTiledBuffer.Clear();
   }
   CompositableHost::Detach(aLayer,aFlags);
 }
@@ -313,60 +94,280 @@ TiledContentHost::UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                                       const SurfaceDescriptorTiles& aTiledDescriptor)
 {
   if (aTiledDescriptor.resolution() < 1) {
-    if (mPendingLowPrecisionUpload) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-    } else {
-      mPendingLowPrecisionUpload = true;
-      // If the old buffer has double-buffered tiles, hang onto it so we can
-      // unlock it after we've composited the new buffer.
-      // We only need to hang onto the locks, but not the textures.
-      // Releasing the textures here can help prevent a memory spike in the
-      // situation that the client starts rendering new content before we get
-      // to composite the new buffer.
-      if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldLowPrecisionTiledBuffer = mLowPrecisionTiledBuffer;
-        mOldLowPrecisionTiledBuffer.ReleaseTextureHosts();
-      }
-    }
-    mLowPrecisionTiledBuffer =
-      TiledLayerBufferComposite(aAllocator,
-                                aTiledDescriptor,
-                                mLowPrecisionTiledBuffer.GetPaintedRegion(),
-                                mCompositor);
-    if (!mLowPrecisionTiledBuffer.IsValid()) {
-      // Something bad happened. Stop here, return false (kills the child process),
-      // and do as little work as possible on the received data as it appears
-      // to be corrupted.
-      mPendingLowPrecisionUpload = false;
-      mPendingUpload = false;
+    if (!mLowPrecisionTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
       return false;
     }
   } else {
-    if (mPendingUpload) {
-      mTiledBuffer.ReadUnlock();
-    } else {
-      mPendingUpload = true;
-      if (mTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldTiledBuffer = mTiledBuffer;
-        mOldTiledBuffer.ReleaseTextureHosts();
-      }
-    }
-    mTiledBuffer = TiledLayerBufferComposite(aAllocator,
-                                             aTiledDescriptor,
-                                             mTiledBuffer.GetPaintedRegion(),
-                                             mCompositor);
-    if (!mTiledBuffer.IsValid()) {
-      // Something bad happened. Stop here, return false (kills the child process),
-      // and do as little work as possible on the received data as it appears
-      // to be corrupted.
-      mPendingLowPrecisionUpload = false;
-      mPendingUpload = false;
+    if (!mTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
       return false;
     }
   }
   return true;
 }
 
+void
+UseTileTexture(CompositableTextureHostRef& aTexture,
+               CompositableTextureSourceRef& aTextureSource,
+               const IntRect& aUpdateRect,
+               TextureHost* aNewTexture,
+               Compositor* aCompositor)
+{
+  if (aTexture && aTexture->GetFormat() != aNewTexture->GetFormat()) {
+    // Only reuse textures if their format match the new texture's.
+    aTextureSource = nullptr;
+    aTexture = nullptr;
+  }
+  aTexture = aNewTexture;
+  if (aTexture) {
+    if (aCompositor) {
+      aTexture->SetCompositor(aCompositor);
+    }
+
+    if (!aUpdateRect.IsEmpty()) {
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+      aTexture->Updated(nullptr);
+#else
+      // We possibly upload the entire texture contents here. This is a purposeful
+      // decision, as sub-image upload can often be slow and/or unreliable, but
+      // we may want to reevaluate this in the future.
+      // For !HasInternalBuffer() textures, this is likely a no-op.
+      nsIntRegion region = aUpdateRect;
+      aTexture->Updated(®ion);
+#endif
+    }
+    aTexture->PrepareTextureSource(aTextureSource);
+  }
+}
+
+bool
+GetCopyOnWriteLock(const TileLock& ipcLock, TileHost& aTile, ISurfaceAllocator* aAllocator) {
+  MOZ_ASSERT(aAllocator);
+
+  nsRefPtr sharedLock;
+  if (ipcLock.type() == TileLock::TShmemSection) {
+    sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
+  } else {
+    if (!aAllocator->IsSameProcess()) {
+      // Trying to use a memory based lock instead of a shmem based one in
+      // the cross-process case is a bad security violation.
+      NS_ERROR("A client process may be trying to peek at the host's address space!");
+      return false;
+    }
+    sharedLock = reinterpret_cast(ipcLock.get_uintptr_t());
+    if (sharedLock) {
+      // The corresponding AddRef is in TiledClient::GetTileDescriptor
+      sharedLock.get()->Release();
+    }
+  }
+  aTile.mSharedLock = sharedLock;
+  return true;
+}
+
+bool
+TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles,
+                                    Compositor* aCompositor,
+                                    ISurfaceAllocator* aAllocator)
+{
+  if (mResolution != aTiles.resolution()) {
+    Clear();
+  }
+  MOZ_ASSERT(aAllocator);
+  MOZ_ASSERT(aCompositor);
+  if (!aAllocator || !aCompositor) {
+    return false;
+  }
+
+  if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) {
+    // There are divisions by mResolution so this protects the compositor process
+    // against malicious content processes and fuzzing.
+    return false;
+  }
+
+  int newFirstTileX = aTiles.firstTileX();
+  int newFirstTileY = aTiles.firstTileY();
+  int oldFirstTileX = mFirstTileX;
+  int oldFirstTileY = mFirstTileY;
+  int newRetainedWidth = aTiles.retainedWidth();
+  int newRetainedHeight = aTiles.retainedHeight();
+  int oldRetainedWidth = mRetainedWidth;
+  int oldRetainedHeight = mRetainedHeight;
+
+  const InfallibleTArray& tileDescriptors = aTiles.tiles();
+
+  nsTArray oldTiles;
+  mRetainedTiles.SwapElements(oldTiles);
+  mRetainedTiles.SetLength(tileDescriptors.Length());
+
+  // Step 1, we need to unlock tiles that don't have an internal buffer after the
+  // next frame where they are replaced.
+  // Since we are about to replace the tiles' textures, we need to keep their locks
+  // somewhere (in mPreviousSharedLock) until we composite the layer.
+  for (size_t i = 0; i < oldTiles.Length(); ++i) {
+    TileHost& tile = oldTiles[i];
+    // It can happen that we still have a previous lock at this point,
+    // if we changed a tile's front buffer (causing mSharedLock to
+    // go into mPreviousSharedLock, and then did not composite that tile until
+    // the next transaction, either because the tile is offscreen or because the
+    // two transactions happened with no composition in between (over-production).
+    tile.ReadUnlockPrevious();
+
+    if (tile.mTextureHost && !tile.mTextureHost->HasInternalBuffer()) {
+      MOZ_ASSERT(tile.mSharedLock);
+      int tileX = i % oldRetainedWidth + oldFirstTileX;
+      int tileY = i / oldRetainedWidth + oldFirstTileY;
+
+      if (tileX >= newFirstTileX && tileY >= newFirstTileY &&
+          tileX < (newFirstTileX + newRetainedWidth) &&
+          tileY < (newFirstTileY + newRetainedHeight)) {
+        // This tile still exist in the new buffer
+        tile.mPreviousSharedLock = tile.mSharedLock;
+        tile.mSharedLock = nullptr;
+      } else {
+        // This tile does not exist anymore in the new buffer because the size
+        // changed.
+        tile.ReadUnlock();
+      }
+    }
+
+    // By now we should not have anything in mSharedLock.
+    MOZ_ASSERT(!tile.mSharedLock);
+  }
+
+  // Step 2, move the tiles in mRetainedTiles at places that correspond to where
+  // they should be with the new retained with and height rather than the
+  // old one.
+  for (size_t i = 0; i < tileDescriptors.Length(); i++) {
+    int tileX = i % newRetainedWidth + newFirstTileX;
+    int tileY = i / newRetainedWidth + newFirstTileY;
+
+    // First, get the already existing tiles to the right place in the array,
+    // and use placeholders where there was no tiles.
+    if (tileX < oldFirstTileX || tileY < oldFirstTileY ||
+        tileX >= (oldFirstTileX + oldRetainedWidth) ||
+        tileY >= (oldFirstTileY + oldRetainedHeight)) {
+      mRetainedTiles[i] = GetPlaceholderTile();
+    } else {
+      mRetainedTiles[i] = oldTiles[(tileY - oldFirstTileY) * oldRetainedWidth +
+                                   (tileX - oldFirstTileX)];
+      // If we hit this assertion it means we probably mixed something up in the
+      // logic that tries to reuse tiles on the compositor side. It is most likely
+      // benign, but we are missing some fast paths so let's try to make it not happen.
+      MOZ_ASSERT(tileX == mRetainedTiles[i].x && tileY == mRetainedTiles[i].y);
+    }
+  }
+
+  // It is important to remove the duplicated reference to tiles before calling
+  // TextureHost::PrepareTextureSource, etc. because depending on the textures
+  // ref counts we may or may not get some of the fast paths.
+  oldTiles.Clear();
+
+  // Step 3, handle the texture updates and release the copy-on-write locks.
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    const TileDescriptor& tileDesc = tileDescriptors[i];
+
+    TileHost& tile = mRetainedTiles[i];
+
+    switch (tileDesc.type()) {
+      case TileDescriptor::TTexturedTileDescriptor: {
+        const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+
+        const TileLock& ipcLock = texturedDesc.sharedLock();
+        if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) {
+          return false;
+        }
+
+        RefPtr textureHost = TextureHost::AsTextureHost(
+          texturedDesc.textureParent()
+        );
+
+        RefPtr textureOnWhite = nullptr;
+        if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+          textureOnWhite = TextureHost::AsTextureHost(
+            texturedDesc.textureOnWhite().get_PTextureParent()
+          );
+        }
+
+        UseTileTexture(tile.mTextureHost,
+                       tile.mTextureSource,
+                       texturedDesc.updateRect(),
+                       textureHost,
+                       aCompositor);
+
+        if (textureOnWhite) {
+          UseTileTexture(tile.mTextureHostOnWhite,
+                         tile.mTextureSourceOnWhite,
+                         texturedDesc.updateRect(),
+                         textureOnWhite,
+                         aCompositor);
+        } else {
+          // We could still have component alpha textures from a previous frame.
+          tile.mTextureSourceOnWhite = nullptr;
+          tile.mTextureHostOnWhite = nullptr;
+        }
+
+        if (textureHost->HasInternalBuffer()) {
+          // Now that we did the texture upload (in UseTileTexture), we can release
+          // the lock.
+          tile.ReadUnlock();
+        }
+
+        break;
+      }
+      default:
+        NS_WARNING("Unrecognised tile descriptor type");
+      case TileDescriptor::TPlaceholderTileDescriptor: {
+
+        if (tile.mTextureHost) {
+          tile.mTextureHost->UnbindTextureSource();
+          tile.mTextureSource = nullptr;
+        }
+        if (tile.mTextureHostOnWhite) {
+          tile.mTextureHostOnWhite->UnbindTextureSource();
+          tile.mTextureSourceOnWhite = nullptr;
+        }
+        // we may have a previous lock, and are about to loose our reference to it.
+        // It is okay to unlock it because we just destroyed the texture source.
+        tile.ReadUnlockPrevious();
+        tile = GetPlaceholderTile();
+
+        break;
+      }
+    }
+
+    tile.x = i % newRetainedWidth + newFirstTileX;
+    tile.y = i / newRetainedWidth + newFirstTileY;
+  }
+
+  mFirstTileX = newFirstTileX;
+  mFirstTileY = newFirstTileY;
+  mRetainedWidth = newRetainedWidth;
+  mRetainedHeight = newRetainedHeight;
+  mValidRegion = aTiles.validRegion();
+
+  mResolution = aTiles.resolution();
+  mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(),
+                                             aTiles.frameYResolution());
+
+  return true;
+}
+
+void
+TiledLayerBufferComposite::Clear()
+{
+  for (TileHost& tile : mRetainedTiles) {
+    tile.ReadUnlock();
+    tile.ReadUnlockPrevious();
+  }
+  mRetainedTiles.Clear();
+  mFirstTileX = 0;
+  mFirstTileY = 0;
+  mRetainedWidth = 0;
+  mRetainedHeight = 0;
+  mValidRegion = nsIntRegion();
+  mPaintedRegion = nsIntRegion();
+  mResolution = 1.0;
+}
+
 void
 TiledContentHost::Composite(EffectChain& aEffectChain,
                             float aOpacity,
@@ -376,25 +377,6 @@ TiledContentHost::Composite(EffectChain& aEffectChain,
                             const nsIntRegion* aVisibleRegion /* = nullptr */)
 {
   MOZ_ASSERT(mCompositor);
-  if (mPendingUpload) {
-    mTiledBuffer.SetCompositor(mCompositor);
-    mTiledBuffer.Upload();
-
-    // For a single-buffered tiled buffer, Upload will upload the shared memory
-    // surface to texture memory and we no longer need to read from them.
-    if (!mTiledBuffer.HasDoubleBufferedTiles()) {
-      mTiledBuffer.ReadUnlock();
-    }
-  }
-  if (mPendingLowPrecisionUpload) {
-    mLowPrecisionTiledBuffer.SetCompositor(mCompositor);
-    mLowPrecisionTiledBuffer.Upload();
-
-    if (!mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-    }
-  }
-
   // Reduce the opacity of the low-precision buffer to make it a
   // little more subtle and less jarring. In particular, text
   // rendered at low-resolution and scaled tends to look pretty
@@ -440,24 +422,11 @@ TiledContentHost::Composite(EffectChain& aEffectChain,
                     aFilter, aClipRect, *renderRegion, aTransform);
   RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aFilter,
                     aClipRect, *renderRegion, aTransform);
-
-  // Now release the old buffer if it had double-buffered tiles, as we can
-  // guarantee that they're no longer on the screen (and so any locks that may
-  // have been held have been released).
-  if (mPendingUpload && mOldTiledBuffer.HasDoubleBufferedTiles()) {
-    mOldTiledBuffer.ReadUnlock();
-    mOldTiledBuffer = TiledLayerBufferComposite();
-  }
-  if (mPendingLowPrecisionUpload && mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-    mOldLowPrecisionTiledBuffer.ReadUnlock();
-    mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
-  }
-  mPendingUpload = mPendingLowPrecisionUpload = false;
 }
 
 
 void
-TiledContentHost::RenderTile(const TileHost& aTile,
+TiledContentHost::RenderTile(TileHost& aTile,
                              const gfxRGBA* aBackgroundColor,
                              EffectChain& aEffectChain,
                              float aOpacity,
@@ -524,6 +493,7 @@ TiledContentHost::RenderTile(const TileHost& aTile,
   }
   mCompositor->DrawDiagnostics(flags,
                                aScreenRegion, aClipRect, aTransform, mFlashCounter);
+  aTile.ReadUnlockPrevious();
 }
 
 void
@@ -591,10 +561,10 @@ TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
         h = visibleRect.y + visibleRect.height - y;
       }
 
-      TileHost tileTexture = aLayerBuffer.
-        GetTile(IntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width),
-                         aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height)));
-      if (tileTexture != aLayerBuffer.GetPlaceholderTile()) {
+      nsIntPoint tileOrigin = nsIntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width),
+                                         aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height));
+      TileHost& tileTexture = aLayerBuffer.GetTile(tileOrigin);
+      if (!tileTexture.IsPlaceholderTile()) {
         nsIntRegion tileDrawRegion;
         tileDrawRegion.And(IntRect(x, y, w, h), aLayerBuffer.GetValidRegion());
         tileDrawRegion.And(tileDrawRegion, aVisibleRegion);
diff --git a/gfx/layers/composite/TiledContentHost.h b/gfx/layers/composite/TiledContentHost.h
index 13a89a20797..2647c6f1557 100644
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -52,6 +52,8 @@ public:
   // essentially, this is a sentinel used to represent an invalid or blank
   // tile.
   TileHost()
+  : x(-1)
+  , y(-1)
   {}
 
   // Constructs a TileHost from a gfxSharedReadLock and TextureHost.
@@ -65,6 +67,8 @@ public:
     , mTextureHostOnWhite(aTextureHostOnWhite)
     , mTextureSource(aSource)
     , mTextureSourceOnWhite(aSourceOnWhite)
+    , x(-1)
+    , y(-1)
   {}
 
   TileHost(const TileHost& o) {
@@ -73,6 +77,9 @@ public:
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
+    mPreviousSharedLock = o.mPreviousSharedLock;
+    x = o.x;
+    y = o.y;
   }
   TileHost& operator=(const TileHost& o) {
     if (this == &o) {
@@ -83,6 +90,9 @@ public:
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
+    mPreviousSharedLock = o.mPreviousSharedLock;
+    x = o.x;
+    y = o.y;
     return *this;
   }
 
@@ -98,6 +108,14 @@ public:
   void ReadUnlock() {
     if (mSharedLock) {
       mSharedLock->ReadUnlock();
+      mSharedLock = nullptr;
+    }
+  }
+
+  void ReadUnlockPrevious() {
+    if (mPreviousSharedLock) {
+      mPreviousSharedLock->ReadUnlock();
+      mPreviousSharedLock = nullptr;
     }
   }
 
@@ -111,10 +129,14 @@ public:
   }
 
   RefPtr mSharedLock;
+  RefPtr mPreviousSharedLock;
   CompositableTextureHostRef mTextureHost;
   CompositableTextureHostRef mTextureHostOnWhite;
   mutable CompositableTextureSourceRef mTextureSource;
   mutable CompositableTextureSourceRef mTextureSourceOnWhite;
+  // This is not strictly necessary but makes debugging whole lot easier.
+  int x;
+  int y;
 };
 
 class TiledLayerBufferComposite
@@ -123,13 +145,14 @@ class TiledLayerBufferComposite
   friend class TiledLayerBuffer;
 
 public:
-  typedef TiledLayerBuffer::Iterator Iterator;
-
   TiledLayerBufferComposite();
-  TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
-                            const SurfaceDescriptorTiles& aDescriptor,
-                            const nsIntRegion& aOldPaintedRegion,
-                            Compositor* aCompositor);
+  ~TiledLayerBufferComposite();
+
+  bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors,
+                Compositor* aCompositor,
+                ISurfaceAllocator* aAllocator);
+
+  void Clear();
 
   TileHost GetPlaceholderTile() const { return TileHost(); }
 
@@ -137,43 +160,16 @@ public:
   // by the sum of the resolutions of all parent layers' FrameMetrics.
   const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
 
-  void ReadUnlock();
-
-  void ReleaseTextureHosts();
-
-  /**
-   * This will synchronously upload any necessary texture contents, making the
-   * sources immediately available for compositing. For texture hosts that
-   * don't have an internal buffer, this is unlikely to actually do anything.
-   */
-  void Upload();
-
   void SetCompositor(Compositor* aCompositor);
 
-  bool HasDoubleBufferedTiles() { return mHasDoubleBufferedTiles; }
-
-  bool IsValid() const { return mIsValid; }
-
   // Recycle callback for TextureHost.
   // Used when TiledContentClient is present in client side.
   static void RecycleCallback(TextureHost* textureHost, void* aClosure);
 
 protected:
-  TileHost ValidateTile(TileHost aTile,
-                        const gfx::IntPoint& aTileRect,
-                        const nsIntRegion& dirtyRect);
-
-  // do nothing, the desctructor in the texture host takes care of releasing resources
-  void ReleaseTile(TileHost aTile) {}
-
   void SwapTiles(TileHost& aTileA, TileHost& aTileB) { std::swap(aTileA, aTileB); }
 
-  void UnlockTile(TileHost aTile) {}
-  void PostValidate(const nsIntRegion& aPaintRegion) {}
-private:
   CSSToParentLayerScale2D mFrameResolution;
-  bool mHasDoubleBufferedTiles;
-  bool mIsValid;
 };
 
 /**
@@ -233,11 +229,10 @@ public:
 
   virtual void SetCompositor(Compositor* aCompositor) override
   {
+    MOZ_ASSERT(aCompositor);
     CompositableHost::SetCompositor(aCompositor);
     mTiledBuffer.SetCompositor(aCompositor);
     mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
-    mOldTiledBuffer.SetCompositor(aCompositor);
-    mOldLowPrecisionTiledBuffer.SetCompositor(aCompositor);
   }
 
   virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
@@ -279,7 +274,7 @@ private:
                          gfx::Matrix4x4 aTransform);
 
   // Renders a single given tile.
-  void RenderTile(const TileHost& aTile,
+  void RenderTile(TileHost& aTile,
                   const gfxRGBA* aBackgroundColor,
                   EffectChain& aEffectChain,
                   float aOpacity,
@@ -294,10 +289,6 @@ private:
 
   TiledLayerBufferComposite    mTiledBuffer;
   TiledLayerBufferComposite    mLowPrecisionTiledBuffer;
-  TiledLayerBufferComposite    mOldTiledBuffer;
-  TiledLayerBufferComposite    mOldLowPrecisionTiledBuffer;
-  bool                         mPendingUpload;
-  bool                         mPendingLowPrecisionUpload;
 };
 
 }
diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh
index c1a0a12e078..22fdd827bd3 100644
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -315,6 +315,7 @@ union MaybeTexture {
 struct TexturedTileDescriptor {
   PTexture texture;
   MaybeTexture textureOnWhite;
+  IntRect updateRect;
   TileLock sharedLock;
 };
 
@@ -330,6 +331,8 @@ struct SurfaceDescriptorTiles {
   nsIntRegion validRegion;
   nsIntRegion paintedRegion;
   TileDescriptor[] tiles;
+  int         firstTileX;
+  int         firstTileY;
   int         retainedWidth;
   int         retainedHeight;
   float       resolution;

From 27abceb0ecc9f971afa3b1616faf50414a93f53b Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Sun, 24 May 2015 22:05:28 -0700
Subject: [PATCH 074/107] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/a582a6b7a583
Author: autolander 
Desc: Bug 1167117 - merge pull request #30218 from huchengtw-moz:bug-1167117-update-smart-modal-dialog to mozilla-b2g:master

========

https://hg.mozilla.org/integration/gaia-central/rev/64e11cf73a12
Author: John Hu 
Desc: Bug 1167117 - [building block] update smart-system to have latest smart modal dialog
---
 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 ea0e5c4a505..f77ba6022f0 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "5bcc08a732163087999251b523e3643db397412c", 
+        "git_revision": "5dc3c3db26af23ed7aa02edb0f3fa2d3ffe38c4c", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "66ad152ea50938f799bf7edd4bc2088f47b85c00", 
+    "revision": "a582a6b7a583abd86a36d97983071d0915648cf9", 
     "repo_path": "integration/gaia-central"
 }

From e10d09c80357c82731f56fa244aabcc122c06310 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Sun, 24 May 2015 22:07:22 -0700
Subject: [PATCH 075/107] 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 9f9a6391bb4..f7a454d3d75 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 b5452a3b9f0..ff7b7367cd5 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 53194b9e19c..13e44ed6591 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 1c6c519db68..1ee8543fde9 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 b639a383e8b..804c0bbe611 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 02b0b922ee5..e79f93671ba 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 53194b9e19c..13e44ed6591 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 8b21180e3b5..12c98c0041c 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 0ace2017853..6b3c95343e3 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 50d1c80a473..c43834920b9 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From 95dd0acec76d41a31f88cfd8b91ed595baa3e11b Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 00:01:27 -0700
Subject: [PATCH 076/107] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/e701c654a55b
Author: Ian Liu 
Desc: Merge pull request #30190 from ian-liu/bluetooth/bug1166636_remove_paired_device_from_found_device_list_while_inline_pairing_via_bluetooth_app

Bug 1166636 - remove device paired from found device list of background app while it is paired via the foreground Bluetooth/Settings app

========

https://hg.mozilla.org/integration/gaia-central/rev/7f046514354f
Author: ian-liu 
Desc: Bug 1166636 - remove device paired from found device list of background app while it is paired via the foreground Bluetooth/Settings app
---
 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 f77ba6022f0..95cdf6bc33c 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "5dc3c3db26af23ed7aa02edb0f3fa2d3ffe38c4c", 
+        "git_revision": "348f579664428ed95e949d8d4baf13b7a0ba13d8", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "a582a6b7a583abd86a36d97983071d0915648cf9", 
+    "revision": "e701c654a55bac62499f41759e68fc8bdb2ad6c5", 
     "repo_path": "integration/gaia-central"
 }

From e5a5eba239df1282f0b1796703448f7b25dd2513 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 00:03:23 -0700
Subject: [PATCH 077/107] 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 f7a454d3d75..e87644cc842 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 ff7b7367cd5..f316f282190 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 13e44ed6591..f025a885dc1 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 1ee8543fde9..9948d1d2b8e 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 804c0bbe611..2cbc702d725 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 e79f93671ba..a0728f60ded 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 13e44ed6591..f025a885dc1 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 12c98c0041c..19fe2f743af 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 6b3c95343e3..e179d5fc350 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 c43834920b9..adf3ccc019c 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From d5969abf2b442d3e87b2f185d274d3b4c1536f98 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 01:06:26 -0700
Subject: [PATCH 078/107] 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/47f559f1c21f
Author: Rudy Lu 
Desc: Merge pull request #30174 from mantaroh/master

Bug 1167050 - Remove container hidden code when choose datetime/datet…
r=rudylu

========

https://hg.mozilla.org/integration/gaia-central/rev/52c70b3ef385
Author: Mantaroh Yoshinaga 
Desc: Bug 1167050 - Remove container hidden code when choose datetime/datetime-local picker. r=alive
---
 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 95cdf6bc33c..920e57a7608 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "348f579664428ed95e949d8d4baf13b7a0ba13d8", 
+        "git_revision": "3f38f429cfd5d1260362d7fb3fbf52576bbec83b", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "e701c654a55bac62499f41759e68fc8bdb2ad6c5", 
+    "revision": "47f559f1c21fd5415a89d52b3e3f9939dbe32fa6", 
     "repo_path": "integration/gaia-central"
 }

From a4cb4740127af3790cbe6a301aa7ed0ea8566526 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 01:08:22 -0700
Subject: [PATCH 079/107] 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 e87644cc842..5deb1f6c355 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 f316f282190..6f44d97be54 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 f025a885dc1..5112296f4aa 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 9948d1d2b8e..eff4b7e1473 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 2cbc702d725..8ba48e96f8c 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 a0728f60ded..e090a9d29ee 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 f025a885dc1..5112296f4aa 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 19fe2f743af..1afea2aa7c7 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 e179d5fc350..44dd8958605 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 adf3ccc019c..622f26669ac 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From 94d65412a42d5e14c0e972a8c1e090f347736d5f Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 02:15:29 -0700
Subject: [PATCH 080/107] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/3b3fd6b45055
Author: Rex KM Lee 
Desc: Merge pull request #30195 from rexboy7/1166684-embed-weather-widget

Bug 1166684 - [Stingray][Dashboard] embed weather widget into dashboard. r=johnhu, yifan

========

https://hg.mozilla.org/integration/gaia-central/rev/4b9c23d69a86
Author: Rex Lee 
Desc: Bug 1166684 - [Stingray][Dashboard] embed weather widget into dashboard.
---
 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 920e57a7608..3ace9ece6d5 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "3f38f429cfd5d1260362d7fb3fbf52576bbec83b", 
+        "git_revision": "bf4453575705e1fd37d1e18eac7f16b4957b0d63", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "47f559f1c21fd5415a89d52b3e3f9939dbe32fa6", 
+    "revision": "3b3fd6b450553cd2edbb503ebe74933daaade836", 
     "repo_path": "integration/gaia-central"
 }

From cc77271aff5d9a8d32234dadf9752059577dc169 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 02:17:24 -0700
Subject: [PATCH 081/107] 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 5deb1f6c355..57f24260188 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 6f44d97be54..d19a2a2f61b 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 5112296f4aa..12505bc0697 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 eff4b7e1473..f64254c3d61 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 8ba48e96f8c..2624bf54203 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 e090a9d29ee..c91e84f403f 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 5112296f4aa..12505bc0697 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 1afea2aa7c7..47e07b88bc8 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 44dd8958605..600bdc7ca4d 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 622f26669ac..9485834eac7 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From f4861a62d9ec12e4e6a5b2f31504c89dfeb455cb Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 03:05:26 -0700
Subject: [PATCH 082/107] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/422bc5bc6c23
Author: Sung Chiu 
Desc: Merge pull request #30187 from sean2449/Bug-1163493-epg-navigation

Bug 1163492 - [Stingray][EPG] Navigation support for TV programs, r=johnhu

========

https://hg.mozilla.org/integration/gaia-central/rev/4b1c08095b8d
Author: Sung Chiu 
Desc: Bug 1163492 - [Stingray][EPG] Navigation support for programs
  * Support up/down/left/right navigation
  * Add unit test
  * Add integration test
---
 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 3ace9ece6d5..8ee78e63fc6 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "bf4453575705e1fd37d1e18eac7f16b4957b0d63", 
+        "git_revision": "0206e1eef9a1eb8346d3ef75be889510f4a586b9", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "3b3fd6b450553cd2edbb503ebe74933daaade836", 
+    "revision": "422bc5bc6c23bc1058cad579dac78e6d964535d4", 
     "repo_path": "integration/gaia-central"
 }

From 0e0188ac717498deb4e4ebab9e1323e78ef78228 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 03:07:22 -0700
Subject: [PATCH 083/107] 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 57f24260188..b7919e0857b 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 d19a2a2f61b..2eaa26cb329 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 12505bc0697..a0a5cda9585 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 f64254c3d61..5035d3cb96b 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 2624bf54203..c6514500345 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 c91e84f403f..a0bb32141a7 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 12505bc0697..a0a5cda9585 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 47e07b88bc8..92c704a9a5f 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 600bdc7ca4d..18e0b7c0675 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 9485834eac7..da521884932 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From ab80eaeb7cdae9c61116bd3e829a9887ff417715 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 03:20:11 -0700
Subject: [PATCH 084/107] Bumping gaia.json for 6 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/a234938b009f
Author: John Hu 
Desc: Merge pull request #30194 from huchengtw-moz/bug-1167517-browser-context-menu-cannot-open-twice

Bug 1167517 - pick patches from bug 1098338 and bug 1136275, r=rexboy

========

https://hg.mozilla.org/integration/gaia-central/rev/69f4e8ee6daa
Author: John Hu 
Desc: Bug 1167517 - pick patches from bug 1098338 and bug 1136275

========

https://hg.mozilla.org/integration/gaia-central/rev/6d21e940eaaa
Author: Luke Chang 
Desc: Merge pull request #30077 from luke-chang/1156723_add_system_remote

Bug 1156723 - Add "system-remote" entry to system app along with a simplified window manager, r=alive

========

https://hg.mozilla.org/integration/gaia-central/rev/f10231ab8d72
Author: Luke Chang 
Desc: Bug 1156723 - Add "system-remote" entry to system app along with a simplified window manager

========

https://hg.mozilla.org/integration/gaia-central/rev/25912e576572
Author: Sung Chiu 
Desc: Merge pull request #30193 from sean2449/Bug-1167525

Bug 1167525 - [Stingray][Home] Hide home cards when home is not visible, r=rexboy

========

https://hg.mozilla.org/integration/gaia-central/rev/d6ed7ff8822b
Author: Sung Chiu 
Desc: Bug 1167525 - [Stingray][Home] Hide home cards when home is not visible
---
 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 8ee78e63fc6..5b1748a45cc 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "0206e1eef9a1eb8346d3ef75be889510f4a586b9", 
+        "git_revision": "ec259a0a6abc65d819b36bcf7693575530d4a243", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "422bc5bc6c23bc1058cad579dac78e6d964535d4", 
+    "revision": "a234938b009f766de6d33c534fbb005f36388789", 
     "repo_path": "integration/gaia-central"
 }

From 7852cc2d847ba3779ac7a26670d754475b8a5c76 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 03:22:12 -0700
Subject: [PATCH 085/107] 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 b7919e0857b..6246b69e458 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 2eaa26cb329..d2afe330d2c 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 a0a5cda9585..1c335ffa37a 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 5035d3cb96b..51ccf25343c 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 c6514500345..eb2665aa118 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 a0bb32141a7..015898986f2 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 a0a5cda9585..1c335ffa37a 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 92c704a9a5f..fa70cc219f7 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 18e0b7c0675..0741571b5ce 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 da521884932..39ff67080e3 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From 5b3832775f000dceaa602611c0f41f86ce104b67 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 03:40:32 -0700
Subject: [PATCH 086/107] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/6a8700549a81
Author: Luke Chang 
Desc: Merge pull request #29908 from luke-chang/1156650_multiscreen_display_dialog

Bug 1156650 - Show display dialog when launching app from homescreen, r=alive

========

https://hg.mozilla.org/integration/gaia-central/rev/76a5f8e98044
Author: Luke Chang 
Desc: Bug 1156650 - Show display dialog when launching app from homescreen
---
 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 5b1748a45cc..ddda712c491 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "ec259a0a6abc65d819b36bcf7693575530d4a243", 
+        "git_revision": "12b6953f90fe175415d99c9a55886807d5026317", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "a234938b009f766de6d33c534fbb005f36388789", 
+    "revision": "6a8700549a81fcff4e0925aeecb002966cfa0148", 
     "repo_path": "integration/gaia-central"
 }

From 1357b1fd3d27afbd439c4ed3ff5d39d2d14421e9 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 03:42:55 -0700
Subject: [PATCH 087/107] 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 6246b69e458..63a157caf2d 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 d2afe330d2c..dfb31641295 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 1c335ffa37a..2ab7d2d01ce 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 51ccf25343c..2b3c76d32bb 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 eb2665aa118..6324ccfa30f 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 015898986f2..9db63812724 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 1c335ffa37a..2ab7d2d01ce 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 fa70cc219f7..a67806c5a8d 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 0741571b5ce..42dbdc620f1 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 39ff67080e3..d6f5475354c 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From 74f027c54fb95bf3717e6b5191e120273f96db63 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 05:25:12 -0700
Subject: [PATCH 088/107] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/1de13bf3055d
Author: Rob Wood 
Desc: Merge pull request #30179 from rwood-moz/bug1163088

Bug 1163088 - Run raptor suite 6x right off, use median results

========

https://hg.mozilla.org/integration/gaia-central/rev/b3185ab0db72
Author: Rob Wood 
Desc: Bug 1163088 - Run raptor suite 6x right off, use median results
---
 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 ddda712c491..d404f011a39 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "12b6953f90fe175415d99c9a55886807d5026317", 
+        "git_revision": "ea07a22e99979fbce2df447df73caf76c351909e", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "6a8700549a81fcff4e0925aeecb002966cfa0148", 
+    "revision": "1de13bf3055d37a4870230b818c2a63ffdcc6d60", 
     "repo_path": "integration/gaia-central"
 }

From d56c466c8cb033203ad94933e9a8d4d723da80f2 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 05:27:06 -0700
Subject: [PATCH 089/107] 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 63a157caf2d..294d4646926 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 dfb31641295..73ef2e3db0e 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 2ab7d2d01ce..0aa4f71df86 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 2b3c76d32bb..092dac081b9 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 6324ccfa30f..f2adf4e288b 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 9db63812724..8757fea2655 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 2ab7d2d01ce..0aa4f71df86 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 a67806c5a8d..8e94dc478c8 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 42dbdc620f1..508d82fd115 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 d6f5475354c..c562b435695 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From 097ca222f8de78b5fa36bb7e9dc6c4eeb92b68ed Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 06:42:32 -0700
Subject: [PATCH 090/107] Bumping gaia.json for 3 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/7c8fece4dd8f
Author: Dale Harvey 
Desc: Bug 1159411 - Add ability to preload webapps into customisation. r=rickychien

========

https://hg.mozilla.org/integration/gaia-central/rev/0e694b55f081
Author: Kevin Grandon 
Desc: Merge pull request #30217 from MichaelKohler/bug1138064

Bug 1138064 - Timezone change Cancun as of February 1 2015

========

https://hg.mozilla.org/integration/gaia-central/rev/5f4eda18bc11
Author: Michael Kohler 
Desc: Bug 1138064 - Timezone change Cancun as of February 1 2015
---
 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 d404f011a39..9fbba244194 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "ea07a22e99979fbce2df447df73caf76c351909e", 
+        "git_revision": "a68746f8fdce1d4c37751f72140dd9f2694debca", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "1de13bf3055d37a4870230b818c2a63ffdcc6d60", 
+    "revision": "7c8fece4dd8f7ddd61dbad1930e3ea661c784f90", 
     "repo_path": "integration/gaia-central"
 }

From c7f9cc167f935e0ed04e4371a9e6860506586e25 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 06:44:28 -0700
Subject: [PATCH 091/107] 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 294d4646926..6cd6465ddf7 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 73ef2e3db0e..5fa81b12672 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 0aa4f71df86..33916d64c69 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 092dac081b9..0601f285dec 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 f2adf4e288b..389becf7f1f 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 8757fea2655..b1b28df8ec6 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 0aa4f71df86..33916d64c69 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 8e94dc478c8..be6f81ad3d6 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 508d82fd115..c39d726f197 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 c562b435695..612114bcdf0 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From 160a111f222686a0c55620f35075551583091567 Mon Sep 17 00:00:00 2001
From: Roger Yang 
Date: Sun, 24 May 2015 11:36:00 -0400
Subject: [PATCH 092/107] Bug 1124338 - Fix possible camera cached parameters
 invalidation from underlying driver modification. r=aosmond

---
 dom/camera/GonkCameraControl.cpp              | 10 +++++--
 .../test/test_camera_fake_parameters.html     | 27 +++++++++++++++++++
 2 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp
index 090b1a65bc7..7b31e0ab3a5 100644
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -179,7 +179,7 @@ nsGonkCameraControl::Initialize()
   mCurrentConfiguration.mRecorderProfile.Truncate();
 
   // Initialize our camera configuration database.
-  PullParametersImpl();
+  mCameraHw->PullParameters(mParams);
 
   // Set preferred preview frame format.
   mParams.Set(CAMERA_PARAM_PREVIEWFORMAT, NS_LITERAL_STRING("yuv420sp"));
@@ -1127,7 +1127,13 @@ nsGonkCameraControl::PullParametersImpl()
   DOM_CAMERA_LOGI("Pulling camera parameters\n");
   RETURN_IF_NO_CAMERA_HW();
 
-  return mCameraHw->PullParameters(mParams);
+  nsresult rv = mCameraHw->PullParameters(mParams);
+  mParams.Get(CAMERA_PARAM_THUMBNAILSIZE, mLastThumbnailSize);
+  mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mCurrentConfiguration.mPictureSize);
+  mParams.Get(CAMERA_PARAM_PREVIEWSIZE, mCurrentConfiguration.mPreviewSize);
+  mParams.Get(CAMERA_PARAM_VIDEOSIZE, mLastRecorderSize);
+
+  return rv;
 }
 
 nsresult
diff --git a/dom/camera/test/test_camera_fake_parameters.html b/dom/camera/test/test_camera_fake_parameters.html
index da45ce7b013..df98f2fce86 100644
--- a/dom/camera/test/test_camera_fake_parameters.html
+++ b/dom/camera/test/test_camera_fake_parameters.html
@@ -527,6 +527,33 @@ suite.test('bug-1052851', function() {
     .then(resolve, suite.rejectGetCamera);
 });
 
+suite.test('bug-1124338', function() {
+  function triggerAutoFocus(p) {
+    var sync = new Promise(function(resolve, reject) {
+      function onEvent(e) {
+        suite.camera.removeEventListener('focus', onEvent);
+        var thumbnailSize = suite.camera.getThumbnailSize();
+        ok(thumbnailSize.width == 640 && thumbnailSize.height == 480, 'thumbnail size reset with auto focus');
+        resolve();
+      }
+      suite.camera.addEventListener('focus', onEvent);
+    });
+
+    var initThumbnailSize = suite.camera.getThumbnailSize();
+    ok(initThumbnailSize.width == 320 && initThumbnailSize.height == 240, 'initial thumbnail size incorrect');
+    suite.hw.params['jpeg-thumbnail-width'] = '640';
+    suite.hw.params['jpeg-thumbnail-height'] = '480';
+    suite.hw.fireAutoFocusComplete(false);
+    return sync;
+  }
+ 
+  suite.hw.params['jpeg-thumbnail-size-values'] = '320x240,640x480';
+  suite.hw.params['jpeg-thumbnail-width'] = '320';
+  suite.hw.params['jpeg-thumbnail-height'] = '240';
+  return suite.getCamera()
+    .then(triggerAutoFocus)
+});
+
 suite.setup()
   .then(suite.run);
 

From 56224435d60ca2ac263358bea993b9e09cac4c2a Mon Sep 17 00:00:00 2001
From: Jocelyn Liu 
Date: Fri, 22 May 2015 20:07:00 -0400
Subject: [PATCH 093/107] Bug 1167070 - Overwrite BLE related flags to enable
 BLE functionalities in bluedroid for both bluetooth1 and bluetooth2. r=shuang

---
 dom/bluetooth/bluedroid/b2g_bdroid_buildcfg.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/dom/bluetooth/bluedroid/b2g_bdroid_buildcfg.h b/dom/bluetooth/bluedroid/b2g_bdroid_buildcfg.h
index c51b2f90bdd..5bb186f2ebd 100644
--- a/dom/bluetooth/bluedroid/b2g_bdroid_buildcfg.h
+++ b/dom/bluetooth/bluedroid/b2g_bdroid_buildcfg.h
@@ -41,4 +41,9 @@
 /* SDP AVRCP 1.5 feature */
 #define SDP_AVRCP_1_5 FALSE
 
+/* BLE Feature */
+#define BTA_GATT_INCLUDED  TRUE
+#define BLE_INCLUDED       TRUE
+#define SMP_INCLUDED       TRUE
+
 #endif /* B2G_BDROID_BUILDCFG_H */

From 8beab86d475f2dd1851f695c61a46f5f650b8f5c Mon Sep 17 00:00:00 2001
From: JerryShih 
Date: Fri, 22 May 2015 11:20:00 -0400
Subject: [PATCH 094/107] Bug 1167535 - Fix fanotify 4g size limit. r=fabrice

---
 hal/gonk/GonkDiskSpaceWatcher.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hal/gonk/GonkDiskSpaceWatcher.cpp b/hal/gonk/GonkDiskSpaceWatcher.cpp
index ef3359525ef..6caf13fb88f 100644
--- a/hal/gonk/GonkDiskSpaceWatcher.cpp
+++ b/hal/gonk/GonkDiskSpaceWatcher.cpp
@@ -177,7 +177,7 @@ GonkDiskSpaceWatcher::DoStart()
   NS_ASSERTION(XRE_GetIOMessageLoop() == MessageLoopForIO::current(),
                "Not on the correct message loop");
 
-  mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC);
+  mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC | O_LARGEFILE);
   if (mFd == -1) {
     if (errno == ENOSYS) {
       NS_WARNING("Warning: No fanotify support in this device's kernel.\n");

From 7696876034cee18b4a012351642334bd0a44a4b6 Mon Sep 17 00:00:00 2001
From: Nicolas Silva 
Date: Mon, 25 May 2015 18:59:22 +0200
Subject: [PATCH 095/107] Bug 1150549 - Remove debugging code accidentally left
 in the previous patch

---
 gfx/layers/Effects.h | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/gfx/layers/Effects.h b/gfx/layers/Effects.h
index 128b84ada3b..d5cae98a29e 100644
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -285,11 +285,6 @@ CreateTexturedEffect(TextureSource* aSource,
 {
   MOZ_ASSERT(aSource);
   if (aSourceOnWhite) {
-    if ((aSource->GetFormat() != gfx::SurfaceFormat::R8G8B8X8 &&
-         aSource->GetFormat() != gfx::SurfaceFormat::B8G8R8X8) ||
-        aSource->GetFormat() != aSourceOnWhite->GetFormat()) {
-      printf_stderr("XXXX - source %i - on white %i\n", (int)aSource->GetFormat(), (int)aSourceOnWhite->GetFormat());
-    }
     MOZ_ASSERT(aSource->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
                aSource->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
     MOZ_ASSERT(aSource->GetFormat() == aSourceOnWhite->GetFormat());

From bbcbe5cc8b337aafacb8a4fe3911cb071bd6bb45 Mon Sep 17 00:00:00 2001
From: Ryan VanderMeulen 
Date: Mon, 25 May 2015 13:15:06 -0400
Subject: [PATCH 096/107] Backed out 4 changesets (bug 1155493) for Android
 test_browserElement_inproc_CopyPaste.html timeouts/crashes.

Backed out changeset 3bd7adb9f591 (bug 1155493)
Backed out changeset 0380b1684e6b (bug 1155493)
Backed out changeset 58b7c1eaf3c8 (bug 1155493)
Backed out changeset 896beb5088a7 (bug 1155493)
---
 b2g/chrome/content/shell.js                   | 28 ------
 dom/browser-element/BrowserElementChild.js    | 13 +--
 .../BrowserElementCopyPaste.js                | 90 ------------------
 dom/browser-element/BrowserElementParent.js   | 35 -------
 .../mochitest/browserElementTestHelpers.js    |  4 -
 .../mochitest/browserElement_CopyPaste.js     | 34 +++----
 dom/ipc/jar.mn                                |  1 -
 dom/ipc/preload.js                            |  1 -
 dom/webidl/CaretStateChangedEvent.webidl      | 32 -------
 dom/webidl/moz.build                          |  1 -
 layout/base/AccessibleCaretManager.cpp        | 92 +------------------
 layout/base/AccessibleCaretManager.h          |  5 -
 12 files changed, 17 insertions(+), 319 deletions(-)
 delete mode 100644 dom/browser-element/BrowserElementCopyPaste.js
 delete mode 100644 dom/webidl/CaretStateChangedEvent.webidl

diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js
index abb0f45fd13..eb2607ddeea 100644
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -350,7 +350,6 @@ var shell = {
     this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.addEventListener('mozbrowserselectionstatechanged', this, true);
     this.contentBrowser.addEventListener('mozbrowserscrollviewchange', this, true);
-    this.contentBrowser.addEventListener('mozbrowsercaretstatechanged', this);
 
     CustomEventManager.init();
     WebappsHelper.init();
@@ -381,7 +380,6 @@ var shell = {
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.removeEventListener('mozbrowserselectionstatechanged', this, true);
     this.contentBrowser.removeEventListener('mozbrowserscrollviewchange', this, true);
-    this.contentBrowser.removeEventListener('mozbrowsercaretstatechanged', this);
     ppmm.removeMessageListener("content-handler", this);
 
     UserAgentOverrides.uninit();
@@ -492,28 +490,6 @@ var shell = {
           detail: data,
         });
         break;
-      case 'mozbrowsercaretstatechanged':
-        {
-          let elt = evt.target;
-          let win = elt.ownerDocument.defaultView;
-          let offsetX = win.mozInnerScreenX - window.mozInnerScreenX;
-          let offsetY = win.mozInnerScreenY - window.mozInnerScreenY;
-
-          let rect = elt.getBoundingClientRect();
-          offsetX += rect.left;
-          offsetY += rect.top;
-
-          let data = evt.detail;
-          data.offsetX = offsetX;
-          data.offsetY = offsetY;
-          data.sendDoCommandMsg = null;
-
-          shell.sendChromeEvent({
-            type: 'caretstatechanged',
-            detail: data,
-          });
-        }
-        break;
 
       case 'MozApplicationManifest':
         try {
@@ -742,10 +718,6 @@ var CustomEventManager = {
       case 'do-command':
         DoCommandHelper.handleEvent(detail.cmd);
         break;
-      case 'copypaste-do-command':
-        Services.obs.notifyObservers({ wrappedJSObject: shell.contentBrowser },
-                                     'ask-children-to-execute-copypaste-command', detail.cmd);
-        break;
     }
   }
 }
diff --git a/dom/browser-element/BrowserElementChild.js b/dom/browser-element/BrowserElementChild.js
index 653c5de604c..6bf1c5f80b3 100644
--- a/dom/browser-element/BrowserElementChild.js
+++ b/dom/browser-element/BrowserElementChild.js
@@ -34,15 +34,12 @@ function isTopBrowserElement(docShell) {
 }
 
 if (!('BrowserElementIsPreloaded' in this)) {
-  if (isTopBrowserElement(docShell)) {
-    if (Services.prefs.getBoolPref("dom.mozInputMethod.enabled")) {
-      try {
-        Services.scriptloader.loadSubScript("chrome://global/content/forms.js");
-      } catch (e) {
-      }
+  if (isTopBrowserElement(docShell) &&
+      Services.prefs.getBoolPref("dom.mozInputMethod.enabled")) {
+    try {
+      Services.scriptloader.loadSubScript("chrome://global/content/forms.js");
+    } catch (e) {
     }
-
-    Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js");
   }
 
   if (Services.prefs.getIntPref("dom.w3c_touch_events.enabled") == 1) {
diff --git a/dom/browser-element/BrowserElementCopyPaste.js b/dom/browser-element/BrowserElementCopyPaste.js
deleted file mode 100644
index bfe4f71bc2f..00000000000
--- a/dom/browser-element/BrowserElementCopyPaste.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
-/* 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";
-
-dump("###################################### BrowserElementCopyPaste.js loaded\n");
-
-let CopyPasteAssistent = {
-  COMMAND_MAP: {
-    'cut': 'cmd_cut',
-    'copy': 'cmd_copyAndCollapseToEnd',
-    'paste': 'cmd_paste',
-    'selectall': 'cmd_selectAll'
-  },
-
-  init: function() {
-    addEventListener('mozcaretstatechanged',
-                     this._caretStateChangedHandler.bind(this),
-                     /* useCapture = */ true,
-                     /* wantsUntrusted = */ false);
-    addMessageListener('browser-element-api:call', this._browserAPIHandler.bind(this));
-  },
-
-  _browserAPIHandler: function(e) {
-    switch (e.data.msg_name) {
-      case 'copypaste-do-command':
-        if (this._isCommandEnabled(e.data.command)) {
-          docShell.doCommand(COMMAND_MAP[e.data.command]);
-        }
-        break;
-    }
-  },
-
-  _isCommandEnabled: function(cmd) {
-    let command = this.COMMAND_MAP[cmd];
-    if (!command) {
-      return false;
-    }
-
-    return docShell.isCommandEnabled(command);
-  },
-
-  _caretStateChangedHandler: function(e) {
-    e.stopPropagation();
-
-    let boundingClientRect = e.boundingClientRect;
-    let canPaste = this._isCommandEnabled("paste");
-    let zoomFactor = content.innerWidth == 0 ? 1 : content.screen.width / content.innerWidth;
-
-    let detail = {
-      rect: {
-        width: boundingClientRect ? boundingClientRect.width : 0,
-        height: boundingClientRect ? boundingClientRect.height : 0,
-        top: boundingClientRect ? boundingClientRect.top : 0,
-        bottom: boundingClientRect ? boundingClientRect.bottom : 0,
-        left: boundingClientRect ? boundingClientRect.left : 0,
-        right: boundingClientRect ? boundingClientRect.right : 0,
-      },
-      commands: {
-        canSelectAll: this._isCommandEnabled("selectall"),
-        canCut: this._isCommandEnabled("cut"),
-        canCopy: this._isCommandEnabled("copy"),
-        canPaste: this._isCommandEnabled("paste"),
-      },
-      zoomFactor: zoomFactor,
-      reason: e.reason,
-      collapsed: e.collapsed,
-      caretVisible: e.caretVisible,
-      selectionVisible: e.selectionVisible
-    };
-
-    // Get correct geometry information if we have nested iframe.
-    let currentWindow = e.target.defaultView;
-    while (currentWindow.realFrameElement) {
-      let currentRect = currentWindow.realFrameElement.getBoundingClientRect();
-      detail.rect.top += currentRect.top;
-      detail.rect.bottom += currentRect.top;
-      detail.rect.left += currentRect.left;
-      detail.rect.right += currentRect.left;
-      currentWindow = currentWindow.realFrameElement.ownerDocument.defaultView;
-    }
-
-    sendAsyncMsg('caretstatechanged', detail);
-  },
-};
-
-CopyPasteAssistent.init();
diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js
index 46b775e3cc8..7d99cd141c7 100644
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -87,7 +87,6 @@ function BrowserElementParent() {
 
   Services.obs.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true);
   Services.obs.addObserver(this, 'copypaste-docommand', /* ownsWeak = */ true);
-  Services.obs.addObserver(this, 'ask-children-to-execute-copypaste-command', /* ownsWeak = */ true);
 }
 
 BrowserElementParent.prototype = {
@@ -204,7 +203,6 @@ BrowserElementParent.prototype = {
       "got-set-input-method-active": this._gotDOMRequestResult,
       "selectionstatechanged": this._handleSelectionStateChanged,
       "scrollviewchange": this._handleScrollViewChange,
-      "caretstatechanged": this._handleCaretStateChanged,
     };
 
     let mmSecuritySensitiveCalls = {
@@ -440,34 +438,6 @@ BrowserElementParent.prototype = {
     this._frameElement.dispatchEvent(evt);
   },
 
-  // Called when state of accessible caret in child has changed.
-  // The fields of data is as following:
-  //  - rect: Contains bounding rectangle of selection, Include width, height,
-  //          top, bottom, left and right.
-  //  - commands: Describe what commands can be executed in child. Include canSelectAll,
-  //              canCut, canCopy and canPaste. For example: if we want to check if cut
-  //              command is available, using following code, if (data.commands.canCut) {}.
-  //  - zoomFactor: Current zoom factor in child frame.
-  //  - reason: The reason causes the state changed. Include "visibilitychange",
-  //            "updateposition", "longpressonemptycontent", "taponcaret", "presscaret",
-  //            "releasecaret".
-  //  - collapsed: Indicate current selection is collapsed or not.
-  //  - caretVisible: Indicate the caret visiibility.
-  //  - selectionVisible: Indicate current selection is visible or not.
-  _handleCaretStateChanged: function(data) {
-    let evt = this._createEvent('caretstatechanged', data.json,
-                                /* cancelable = */ false);
-
-    let self = this;
-    function sendDoCommandMsg(cmd) {
-      let data = { command: cmd };
-      self._sendAsyncMsg('copypaste-do-command', data);
-    }
-    Cu.exportFunction(sendDoCommandMsg, evt.detail, { defineAs: 'sendDoCommandMsg' });
-
-    this._frameElement.dispatchEvent(evt);
-  },
-
   _handleScrollViewChange: function(data) {
     let evt = this._createEvent("scrollviewchange", data.json,
                                 /* cancelable = */ false);
@@ -1009,11 +979,6 @@ BrowserElementParent.prototype = {
         this._sendAsyncMsg('do-command', { command: data });
       }
       break;
-    case 'ask-children-to-execute-copypaste-command':
-      if (this._isAlive() && this._frameElement == subject.wrappedJSObject) {
-        this._sendAsyncMsg('copypaste-do-command', { command: data });
-      }
-      break;
     default:
       debug('Unknown topic: ' + topic);
       break;
diff --git a/dom/browser-element/mochitest/browserElementTestHelpers.js b/dom/browser-element/mochitest/browserElementTestHelpers.js
index f557517de47..007b0300ccc 100644
--- a/dom/browser-element/mochitest/browserElementTestHelpers.js
+++ b/dom/browser-element/mochitest/browserElementTestHelpers.js
@@ -65,10 +65,6 @@ const browserElementTestHelpers = {
     this._setPref('selectioncaret.enabled', value);
   },
 
-  setAccessibleCaretEnabledPref: function(value) {
-    this._setPref('layout.accessiblecaret.enabled', value);
-  },
-
   getOOPByDefaultPref: function() {
     return this._getBoolPref("dom.ipc.browser_frames.oop_by_default");
   },
diff --git a/dom/browser-element/mochitest/browserElement_CopyPaste.js b/dom/browser-element/mochitest/browserElement_CopyPaste.js
index 99f18274755..762b792507c 100644
--- a/dom/browser-element/mochitest/browserElement_CopyPaste.js
+++ b/dom/browser-element/mochitest/browserElement_CopyPaste.js
@@ -21,7 +21,6 @@ var defaultData;
 var pasteData;
 var focusScript;
 var createEmbededFrame = false;
-var testAccessibleCaret = false;
 
 function copyToClipboard(str) {
   gTextarea.value = str;
@@ -161,24 +160,16 @@ function dispatchTest(e) {
       break;
     default:
       if (createEmbededFrame || browserElementTestHelpers.getOOPByDefaultPref()) {
-        if (testAccessibleCaret) {
-          SimpleTest.finish();
-          return;
-        } else {
-          testAccessibleCaret = true;
-          createEmbededFrame = false;
-          browserElementTestHelpers.setSelectionChangeEnabledPref(false);
-          browserElementTestHelpers.setAccessibleCaretEnabledPref(true);
-        }
+        SimpleTest.finish();
       } else {
         createEmbededFrame = true;
-      }
 
-      // clean up and run test again.
-      document.body.removeChild(iframeOuter);
-      document.body.removeChild(gTextarea);
-      state = 0;
-      runTest();
+        // clean up and run test again.
+        document.body.removeChild(iframeOuter);
+        document.body.removeChild(gTextarea);
+        state = 0;
+        runTest();
+      }
       break;
   }
 }
@@ -192,17 +183,14 @@ function isChildProcess() {
 function testSelectAll(e) {
   // Skip mozbrowser test if we're at child process.
   if (!isChildProcess()) {
-    let eventName = testAccessibleCaret ? "mozbrowsercaretstatechanged" : "mozbrowserselectionstatechanged";
-    iframeOuter.addEventListener(eventName, function selectchangeforselectall(e) {
-      if (!e.detail.states || e.detail.states.indexOf('selectall') == 0) {
-        iframeOuter.removeEventListener(eventName, selectchangeforselectall, true);
+    iframeOuter.addEventListener("mozbrowserselectionstatechanged", function selectchangeforselectall(e) {
+      if (e.detail.states.indexOf('selectall') == 0) {
+        iframeOuter.removeEventListener("mozbrowserselectionstatechanged", selectchangeforselectall, true);
         ok(true, "got mozbrowserselectionstatechanged event." + stateMeaning);
         ok(e.detail, "event.detail is not null." + stateMeaning);
         ok(e.detail.width != 0, "event.detail.width is not zero" + stateMeaning);
         ok(e.detail.height != 0, "event.detail.height is not zero" + stateMeaning);
-        if (!testAccessibleCaret) {
-          ok(e.detail.states, "event.detail.state " + e.detail.states);
-        }
+        ok(e.detail.states, "event.detail.state " + e.detail.states);
         SimpleTest.executeSoon(function() { testCopy1(e); });
       }
     }, true);
diff --git a/dom/ipc/jar.mn b/dom/ipc/jar.mn
index 57395c39c22..b3f1bc88e0e 100644
--- a/dom/ipc/jar.mn
+++ b/dom/ipc/jar.mn
@@ -7,7 +7,6 @@ toolkit.jar:
         content/global/remote-test-ipc.js (remote-test.js)
         content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
         content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js)
-        content/global/BrowserElementCopyPaste.js (../browser-element/BrowserElementCopyPaste.js)
         content/global/BrowserElementPanning.js (../browser-element/BrowserElementPanning.js)
 *       content/global/BrowserElementPanningAPZDisabled.js (../browser-element/BrowserElementPanningAPZDisabled.js)
         content/global/manifestMessages.js (manifestMessages.js)
diff --git a/dom/ipc/preload.js b/dom/ipc/preload.js
index 165933e924c..7e613cffd7f 100644
--- a/dom/ipc/preload.js
+++ b/dom/ipc/preload.js
@@ -103,7 +103,6 @@ const BrowserElementIsPreloaded = true;
     Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanning.js", global);
   }
 
-  Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js", global);
   Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js", global);
 
   Services.io.getProtocolHandler("app");
diff --git a/dom/webidl/CaretStateChangedEvent.webidl b/dom/webidl/CaretStateChangedEvent.webidl
deleted file mode 100644
index 45a4e38d301..00000000000
--- a/dom/webidl/CaretStateChangedEvent.webidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: IDL; 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/.
- */
-
-enum CaretChangedReason {
-  "visibilitychange",
-  "updateposition",
-  "longpressonemptycontent",
-  "taponcaret",
-  "presscaret",
-  "releasecaret"
-};
-
-dictionary CaretStateChangedEventInit : EventInit {
-  boolean collapsed = true;
-  DOMRectReadOnly? boundingClientRect = null;
-  CaretChangedReason reason = "visibilitychange";
-  boolean caretVisible = false;
-  boolean selectionVisible = false;
-};
-
-[Constructor(DOMString type, optional CaretStateChangedEventInit eventInit),
- ChromeOnly]
-interface CaretStateChangedEvent : Event {
-  readonly attribute boolean collapsed;
-  readonly attribute DOMRectReadOnly? boundingClientRect;
-  readonly attribute CaretChangedReason reason;
-  readonly attribute boolean caretVisible;
-  readonly attribute boolean selectionVisible;
-};
diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build
index 780e95eed27..a53e50c862c 100644
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -725,7 +725,6 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'CameraConfigurationEvent.webidl',
     'CameraFacesDetectedEvent.webidl',
     'CameraStateChangeEvent.webidl',
-    'CaretStateChangedEvent.webidl',
     'CFStateChangeEvent.webidl',
     'CloseEvent.webidl',
     'CSSFontFaceLoadEvent.webidl',
diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp
index 00c3f661b40..9b818f74587 100644
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -90,7 +90,6 @@ AccessibleCaretManager::HideCarets()
     AC_LOG("%s", __FUNCTION__);
     mFirstCaret->SetAppearance(Appearance::None);
     mSecondCaret->SetAppearance(Appearance::None);
-    DispatchCaretStateChangedEvent(CaretChangedReason::Visibilitychange);
     CancelCaretTimeoutTimer();
   }
 }
@@ -155,8 +154,7 @@ AccessibleCaretManager::UpdateCaretsForCursorMode()
   // No need to consider whether the caret's position is out of scrollport.
   // According to the spec, we need to explicitly hide it after the scrolling is
   // ended.
-  bool oldSecondCaretVisible = mSecondCaret->IsLogicallyVisible();
-  PositionChangedResult caretResult = mFirstCaret->SetPosition(frame, offset);
+  mFirstCaret->SetPosition(frame, offset);
   mFirstCaret->SetSelectionBarEnabled(false);
   if (nsContentUtils::HasNonEmptyTextContent(
         editingHost, nsContentUtils::eRecurseIntoChildren)) {
@@ -166,11 +164,6 @@ AccessibleCaretManager::UpdateCaretsForCursorMode()
     mFirstCaret->SetAppearance(Appearance::NormalNotShown);
   }
   mSecondCaret->SetAppearance(Appearance::None);
-
-  if ((caretResult == PositionChangedResult::Changed ||
-      oldSecondCaretVisible) && !mActiveCaret) {
-    DispatchCaretStateChangedEvent(CaretChangedReason::Updateposition);
-  }
 }
 
 void
@@ -221,14 +214,6 @@ AccessibleCaretManager::UpdateCaretsForSelectionMode()
   }
 
   UpdateCaretsForTilt();
-
-  if ((firstCaretResult == PositionChangedResult::Changed ||
-       secondCaretResult == PositionChangedResult::Changed ||
-       firstCaretResult == PositionChangedResult::Invisible ||
-       secondCaretResult == PositionChangedResult::Invisible) &&
-      !mActiveCaret) {
-    DispatchCaretStateChangedEvent(CaretChangedReason::Updateposition);
-  }
 }
 
 void
@@ -268,7 +253,6 @@ AccessibleCaretManager::PressCaret(const nsPoint& aPoint)
     mOffsetYToCaretLogicalPosition =
       mActiveCaret->LogicalPosition().y - aPoint.y;
     SetSelectionDragState(true);
-    DispatchCaretStateChangedEvent(CaretChangedReason::Presscaret);
     CancelCaretTimeoutTimer();
     rv = NS_OK;
   }
@@ -295,7 +279,6 @@ AccessibleCaretManager::ReleaseCaret()
 
   mActiveCaret = nullptr;
   SetSelectionDragState(false);
-  DispatchCaretStateChangedEvent(CaretChangedReason::Releasecaret);
   LaunchCaretTimeoutTimer();
   return NS_OK;
 }
@@ -308,7 +291,6 @@ AccessibleCaretManager::TapCaret(const nsPoint& aPoint)
   nsresult rv = NS_ERROR_FAILURE;
 
   if (GetCaretMode() == CaretMode::Cursor) {
-    DispatchCaretStateChangedEvent(CaretChangedReason::Taponcaret);
     rv = NS_OK;
   }
 
@@ -349,7 +331,6 @@ AccessibleCaretManager::SelectWordOrShortcut(const nsPoint& aPoint)
                          editingHost, nsContentUtils::eRecurseIntoChildren))) {
     // Content is empty. No need to select word.
     AC_LOG("%s, Cannot select word bacause content is empty", __FUNCTION__);
-    DispatchCaretStateChangedEvent(CaretChangedReason::Longpressonemptycontent);
     return NS_OK;
   }
 
@@ -885,75 +866,4 @@ AccessibleCaretManager::CancelCaretTimeoutTimer()
   }
 }
 
-void
-AccessibleCaretManager::DispatchCaretStateChangedEvent(CaretChangedReason aReason) const
-{
-  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
-  // Holding PresShell to prevent AccessibleCaretManager to be destroyed.
-  nsCOMPtr presShell = mPresShell;
-  // XXX: Do we need to flush layout?
-  presShell->FlushPendingNotifications(Flush_Layout);
-  if (presShell->IsDestroying()) {
-    return;
-  }
-
-  Selection* sel = GetSelection();
-  if (!sel) {
-    return;
-  }
-
-  nsIDocument* doc = mPresShell->GetDocument();
-  MOZ_ASSERT(doc);
-
-  CaretStateChangedEventInit init;
-  init.mBubbles = true;
-
-  const nsRange* range = sel->GetAnchorFocusRange();
-  nsINode* commonAncestorNode = nullptr;
-  if (range) {
-    commonAncestorNode = range->GetCommonAncestor();
-  }
-
-  if (!commonAncestorNode) {
-    commonAncestorNode = sel->GetFrameSelection()->GetAncestorLimiter();
-  }
-
-  nsRefPtr domRect = new DOMRect(ToSupports(doc));
-  nsRect rect = nsContentUtils::GetSelectionBoundingRect(sel);
-
-  nsIFrame* commonAncestorFrame = nullptr;
-  nsIFrame* rootFrame = mPresShell->GetRootFrame();
-
-  if (commonAncestorNode && commonAncestorNode->IsContent()) {
-    commonAncestorFrame = commonAncestorNode->AsContent()->GetPrimaryFrame();
-  }
-
-  if (commonAncestorFrame && rootFrame) {
-    nsLayoutUtils::TransformRect(rootFrame, commonAncestorFrame, rect);
-    nsRect clampedRect = nsLayoutUtils::ClampRectToScrollFrames(commonAncestorFrame,
-                                                                rect);
-    nsLayoutUtils::TransformRect(commonAncestorFrame, rootFrame, clampedRect);
-    domRect->SetLayoutRect(clampedRect);
-    init.mSelectionVisible = !clampedRect.IsEmpty();
-    init.mBoundingClientRect = domRect;
-  } else {
-    domRect->SetLayoutRect(rect);
-    init.mSelectionVisible = true;
-  }
-
-  init.mBoundingClientRect = domRect;
-  init.mReason = aReason;
-  init.mCollapsed = sel->IsCollapsed();
-  init.mCaretVisible = mFirstCaret->IsLogicallyVisible() ||
-                       mSecondCaret->IsLogicallyVisible();
-
-  nsRefPtr event =
-    CaretStateChangedEvent::Constructor(doc, NS_LITERAL_STRING("mozcaretstatechanged"), init);
-
-  event->SetTrusted(true);
-  event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
-  bool ret;
-  doc->DispatchEvent(event, &ret);
-}
-
 } // namespace mozilla
diff --git a/layout/base/AccessibleCaretManager.h b/layout/base/AccessibleCaretManager.h
index a03c6c48925..102788f3f8d 100644
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -13,7 +13,6 @@
 #include "nsISelectionListener.h"
 #include "nsRefPtr.h"
 #include "nsWeakReference.h"
-#include "mozilla/dom/CaretStateChangedEvent.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
@@ -133,10 +132,6 @@ protected:
   already_AddRefed GetFrameSelection() const;
   nsIContent* GetFocusedContent() const;
 
-  // This function will call FlushPendingNotifications. So caller must ensure
-  // everything exists after calling this method.
-  void DispatchCaretStateChangedEvent(dom::CaretChangedReason aReason) const;
-
   // If we're dragging the first caret, we do not want to drag it over the
   // previous character of the second caret. Same as the second caret. So we
   // check if content offset exceeds the previous/next character of second/first

From 6c7ae855325568901c4e12a4f7408098bc69d329 Mon Sep 17 00:00:00 2001
From: Tom Schuster 
Date: Mon, 25 May 2015 19:31:46 +0200
Subject: [PATCH 097/107] Bug 1166950 - Introduce a new FunctionKind for
 class-constructors. r=efaust

---
 js/src/jsfriendapi.h |  2 +-
 js/src/jsfun.h       | 24 +++++++++++-------------
 2 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index a930c339d63..1e4cf89c9a6 100644
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2442,7 +2442,7 @@ FunctionObjectToShadowFunction(JSObject* fun)
 }
 
 /* Statically asserted in jsfun.h. */
-static const unsigned JS_FUNCTION_INTERPRETED_BITS = 0x401;
+static const unsigned JS_FUNCTION_INTERPRETED_BITS = 0x0201;
 
 // Return whether the given function object is native.
 static MOZ_ALWAYS_INLINE bool
diff --git a/js/src/jsfun.h b/js/src/jsfun.h
index 7f026948f44..0b228cb96d3 100644
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -32,6 +32,7 @@ class JSFunction : public js::NativeObject
         NormalFunction = 0,
         Arrow,                      /* ES6 '(args) => body' syntax */
         Method,                     /* ES6 MethodDefinition */
+        ClassConstructor,
         Getter,
         Setter,
         AsmJS,                      /* function is an asm.js module or exported function */
@@ -52,18 +53,18 @@ class JSFunction : public js::NativeObject
                                        function-statement) */
         SELF_HOSTED      = 0x0080,  /* function is self-hosted builtin and must not be
                                        decompilable nor constructible. */
-        // Free bit
-        HAS_REST         = 0x0200,  /* function has a rest (...) parameter */
-        INTERPRETED_LAZY = 0x0400,  /* function is interpreted but doesn't have a script yet */
-        RESOLVED_LENGTH  = 0x0800,  /* f.length has been resolved (see fun_resolve). */
-        RESOLVED_NAME    = 0x1000,  /* f.name has been resolved (see fun_resolve). */
+        HAS_REST         = 0x0100,  /* function has a rest (...) parameter */
+        INTERPRETED_LAZY = 0x0200,  /* function is interpreted but doesn't have a script yet */
+        RESOLVED_LENGTH  = 0x0400,  /* f.length has been resolved (see fun_resolve). */
+        RESOLVED_NAME    = 0x0800,  /* f.name has been resolved (see fun_resolve). */
 
-        FUNCTION_KIND_SHIFT = 13,
-        FUNCTION_KIND_MASK  = 0x7 << FUNCTION_KIND_SHIFT,
+        FUNCTION_KIND_SHIFT = 12,
+        FUNCTION_KIND_MASK  = 0xf << FUNCTION_KIND_SHIFT,
 
         ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT,
         ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT,
         METHOD_KIND = Method << FUNCTION_KIND_SHIFT,
+        CLASSCONSTRUCTOR_KIND = ClassConstructor << FUNCTION_KIND_SHIFT,
         GETTER_KIND = Getter << FUNCTION_KIND_SHIFT,
         SETTER_KIND = Setter << FUNCTION_KIND_SHIFT,
 
@@ -73,7 +74,7 @@ class JSFunction : public js::NativeObject
         ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR,
         ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA,
         INTERPRETED_METHOD = INTERPRETED | METHOD_KIND,
-        INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | METHOD_KIND | CONSTRUCTOR,
+        INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR,
         INTERPRETED_GETTER = INTERPRETED | GETTER_KIND,
         INTERPRETED_SETTER = INTERPRETED | SETTER_KIND,
         INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR,
@@ -165,15 +166,12 @@ class JSFunction : public js::NativeObject
     // Arrow functions store their lexical |this| in the first extended slot.
     bool isArrow()                  const { return kind() == Arrow; }
     // Every class-constructor is also a method.
-    bool isMethod()                 const { return kind() == Method; }
+    bool isMethod()                 const { return kind() == Method || kind() == ClassConstructor; }
+    bool isClassConstructor()       const { return kind() == ClassConstructor; }
 
     bool isGetter()                 const { return kind() == Getter; }
     bool isSetter()                 const { return kind() == Setter; }
 
-    bool isClassConstructor() const {
-        return kind() == Method && isConstructor();
-    }
-
     bool allowSuperProperty() const {
         return isMethod() || isGetter() || isSetter();
     }

From b0ceda53f3c3d4e8c215c27952e21599e1d3a268 Mon Sep 17 00:00:00 2001
From: Tom Schuster 
Date: Mon, 25 May 2015 19:31:46 +0200
Subject: [PATCH 098/107] Bug 1166950 - Make generator methods constructors.
 r=efaust

---
 js/src/frontend/Parser.cpp               | 13 +++++++++----
 js/src/frontend/Parser.h                 |  3 ++-
 js/src/jsfun.h                           |  1 +
 js/src/tests/ecma_6/Class/methDefnGen.js |  9 +++++++++
 4 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 8d520af769a..1c80fe4aaf7 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1210,7 +1210,8 @@ struct BindData
 
 template 
 JSFunction*
-Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, HandleObject proto)
+Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
+                                  GeneratorKind generatorKind, HandleObject proto)
 {
     MOZ_ASSERT_IF(kind == Statement, atom != nullptr);
 
@@ -1227,7 +1228,11 @@ Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, Hand
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case Method:
-        flags = JSFunction::INTERPRETED_METHOD;
+        MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator);
+        if (generatorKind == NotGenerator)
+            flags = JSFunction::INTERPRETED_METHOD;
+        else
+            flags = JSFunction::INTERPRETED_METHOD_GENERATOR;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case ClassConstructor:
@@ -2193,7 +2198,7 @@ Parser::functionDef(InHandling inHandling, YieldHandling yieldHand
         if (!proto)
             return null();
     }
-    RootedFunction fun(context, newFunction(funName, kind, proto));
+    RootedFunction fun(context, newFunction(funName, kind, generatorKind, proto));
     if (!fun)
         return null();
 
@@ -7340,7 +7345,7 @@ Parser::generatorComprehensionLambda(GeneratorKind comprehensionKi
             return null();
     }
 
-    RootedFunction fun(context, newFunction(/* atom = */ nullptr, Expression, proto));
+    RootedFunction fun(context, newFunction(/* atom = */ nullptr, Expression, comprehensionKind, proto));
     if (!fun)
         return null();
 
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
index 85eff296624..f864be99c85 100644
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -445,7 +445,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
      * Create a new function object given a name (which is optional if this is
      * a function expression).
      */
-    JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind, HandleObject proto);
+    JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                            HandleObject proto);
 
     void trace(JSTracer* trc);
 
diff --git a/js/src/jsfun.h b/js/src/jsfun.h
index 0b228cb96d3..c583267fcd9 100644
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -74,6 +74,7 @@ class JSFunction : public js::NativeObject
         ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR,
         ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA,
         INTERPRETED_METHOD = INTERPRETED | METHOD_KIND,
+        INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND | CONSTRUCTOR,
         INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR,
         INTERPRETED_GETTER = INTERPRETED | GETTER_KIND,
         INTERPRETED_SETTER = INTERPRETED | SETTER_KIND,
diff --git a/js/src/tests/ecma_6/Class/methDefnGen.js b/js/src/tests/ecma_6/Class/methDefnGen.js
index 30f96db03f6..596d55477f5 100644
--- a/js/src/tests/ecma_6/Class/methDefnGen.js
+++ b/js/src/tests/ecma_6/Class/methDefnGen.js
@@ -72,4 +72,13 @@ assertEq(a.b(1).next().value, 1);
 a = {*["b"](c){"use strict";return c;}};
 assertEq(a.b(1).next().value, 1);
 
+// Constructing
+a = {*g() { yield 1; }}
+it = new a.g;
+next = it.next();
+assertEq(next.done, false);
+assertEq(next.value, 1);
+next = it.next();
+assertEq(next.done, true);
+
 reportCompare(0, 0, "ok");

From edf9a1b4cc1de910e93ac354aa754eef1bf26870 Mon Sep 17 00:00:00 2001
From: Tom Schuster 
Date: Mon, 25 May 2015 19:31:46 +0200
Subject: [PATCH 099/107] Bug 1166950 - Only give constructor functions a
 prototype. r=efaust

---
 js/src/jsfun.cpp                              | 13 +++---
 js/src/tests/ecma_6/Class/methDefn.js         |  4 ++
 js/src/tests/ecma_6/Class/methDefnGen.js      |  3 ++
 js/src/tests/ecma_6/Class/methodsPrototype.js | 45 +++++++++++++++++++
 4 files changed, 60 insertions(+), 5 deletions(-)
 create mode 100644 js/src/tests/ecma_6/Class/methodsPrototype.js

diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index 27201a70275..9cfe32c87f2 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -443,16 +443,19 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
          * or (Object.prototype, Function.prototype, etc.) have that property
          * created eagerly.
          *
-         * ES5 15.3.4: the non-native function object named Function.prototype
-         * does not have a .prototype property.
-         *
          * ES5 15.3.4.5: bound functions don't have a prototype property. The
          * isBuiltin() test covers this case because bound functions are native
          * (and thus built-in) functions by definition/construction.
          *
-         * ES6 19.2.4.3: arrow functions also don't have a prototype property.
+         * In ES6 9.2.8 MakeConstructor the .prototype property is only assigned
+         * to constructors.
+         *
+         * Thus all of the following don't get a .prototype property:
+         * - Methods (that are not class-constructors or generators)
+         * - Arrow functions
+         * - Function.prototype
          */
-        if (fun->isBuiltin() || fun->isArrow() || fun->isFunctionPrototype())
+        if (fun->isBuiltin() || !fun->isConstructor())
             return true;
 
         if (!ResolveInterpretedFunctionPrototype(cx, fun))
diff --git a/js/src/tests/ecma_6/Class/methDefn.js b/js/src/tests/ecma_6/Class/methDefn.js
index 50a09280a14..bbce2f2789f 100644
--- a/js/src/tests/ecma_6/Class/methDefn.js
+++ b/js/src/tests/ecma_6/Class/methDefn.js
@@ -118,6 +118,10 @@ assertEq(b.enumerable, true);
 assertEq(b.writable, true);
 assertEq(b.value(), 4);
 
+// prototype property
+assertEq(a.b.prototype, undefined);
+assertEq(a.b.hasOwnProperty("prototype"), false);
+
 // Defining several methods using eval.
 var code = "({";
 for (i = 0; i < 1000; i++)
diff --git a/js/src/tests/ecma_6/Class/methDefnGen.js b/js/src/tests/ecma_6/Class/methDefnGen.js
index 596d55477f5..8afd94198a8 100644
--- a/js/src/tests/ecma_6/Class/methDefnGen.js
+++ b/js/src/tests/ecma_6/Class/methDefnGen.js
@@ -66,6 +66,9 @@ assertEq(next.done, true);
 assertEq(next.value.hello, 2);
 assertEq(next.value.world, 3);
 
+// prototype property
+assertEq(b.g.hasOwnProperty("prototype"), true);
+
 // Strict mode
 a = {*b(c){"use strict";yield c;}};
 assertEq(a.b(1).next().value, 1);
diff --git a/js/src/tests/ecma_6/Class/methodsPrototype.js b/js/src/tests/ecma_6/Class/methodsPrototype.js
new file mode 100644
index 00000000000..65d5f465d77
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/methodsPrototype.js
@@ -0,0 +1,45 @@
+var test = `
+class TestClass {
+    constructor() { }
+    method() { }
+    get getter() { }
+    set setter(x) { }
+    *generator() { }
+    static staticMethod() { }
+    static get staticGetter() { }
+    static set staticSetter(x) { }
+    static *staticGenerator() { }
+}
+
+var test = new TestClass();
+
+var hasPrototype = [
+    test.constructor,
+    test.generator,
+    TestClass.staticGenerator
+]
+
+for (var fun of hasPrototype) {
+    assertEq(fun.hasOwnProperty('prototype'), true);
+}
+
+var hasNoPrototype = [
+    test.method,
+    Object.getOwnPropertyDescriptor(test.__proto__, 'getter').get,
+    Object.getOwnPropertyDescriptor(test.__proto__, 'setter').set,
+    TestClass.staticMethod,
+    Object.getOwnPropertyDescriptor(TestClass, 'staticGetter').get,
+    Object.getOwnPropertyDescriptor(TestClass, 'staticSetter').set,
+]
+
+for (var fun of hasNoPrototype) {
+    assertEq(fun.hasOwnProperty('prototype'), false);
+}
+
+`;
+
+if (classesEnabled())
+    eval(test);
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0, "OK");

From 5d21775961c90567029d0087f6bc8beb37ea65c7 Mon Sep 17 00:00:00 2001
From: Ryan VanderMeulen 
Date: Mon, 25 May 2015 14:05:05 -0400
Subject: [PATCH 100/107] Backed out changesets 4b6aa5c0a1bf and fdf38a41d92b
 (bug 1150549) for Mulet crashes. CLOSED TREE

---
 gfx/layers/Effects.h                      |   3 +-
 gfx/layers/TiledLayerBuffer.h             |  81 +--
 gfx/layers/client/TiledContentClient.cpp  |  29 +-
 gfx/layers/client/TiledContentClient.h    |  12 +-
 gfx/layers/composite/TextureHost.h        |   1 -
 gfx/layers/composite/TiledContentHost.cpp | 604 ++++++++++++----------
 gfx/layers/composite/TiledContentHost.h   |  71 +--
 gfx/layers/ipc/LayersMessages.ipdlh       |   3 -
 8 files changed, 400 insertions(+), 404 deletions(-)

diff --git a/gfx/layers/Effects.h b/gfx/layers/Effects.h
index d5cae98a29e..f32098d9a3d 100644
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -286,8 +286,7 @@ CreateTexturedEffect(TextureSource* aSource,
   MOZ_ASSERT(aSource);
   if (aSourceOnWhite) {
     MOZ_ASSERT(aSource->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
-               aSource->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
-    MOZ_ASSERT(aSource->GetFormat() == aSourceOnWhite->GetFormat());
+               aSourceOnWhite->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
     return MakeAndAddRef(aSource, aSourceOnWhite, aFilter);
   }
 
diff --git a/gfx/layers/TiledLayerBuffer.h b/gfx/layers/TiledLayerBuffer.h
index c38a4bffcd9..717676b92ef 100644
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -94,9 +94,7 @@ class TiledLayerBuffer
 {
 public:
   TiledLayerBuffer()
-    : mFirstTileX(0)
-    , mFirstTileY(0)
-    , mRetainedWidth(0)
+    : mRetainedWidth(0)
     , mRetainedHeight(0)
     , mResolution(1)
     , mTileSize(gfxPlatform::GetPlatform()->GetTileWidth(), gfxPlatform::GetPlatform()->GetTileHeight())
@@ -110,21 +108,13 @@ public:
   //       (aTileOrigin.x, aTileOrigin.y,
   //        GetScaledTileSize().width, GetScaledTileSize().height)
   //       and GetValidRegion() to get the area of the tile that is valid.
-  Tile& GetTile(const gfx::IntPoint& aTileOrigin);
+  Tile GetTile(const nsIntPoint& aTileOrigin) const;
+
   // Given a tile x, y relative to the top left of the layer, this function
   // will return the tile for
   // (x*GetScaledTileSize().width, y*GetScaledTileSize().height,
   //  GetScaledTileSize().width, GetScaledTileSize().height)
-  Tile& GetTile(int x, int y);
-
-  int TileIndex(const gfx::IntPoint& aTileOrigin) const;
-  int TileIndex(int x, int y) const { return x * mRetainedHeight + y; }
-
-  bool HasTile(int index) const { return index >= 0 && index < (int)mRetainedTiles.Length(); }
-  bool HasTile(const gfx::IntPoint& aTileOrigin) const;
-  bool HasTile(int x, int y) const {
-    return x >= 0 && x < mRetainedWidth && y >= 0 && y < mRetainedHeight;
-  }
+  Tile GetTile(int x, int y) const;
 
   const gfx::IntSize& GetTileSize() const { return mTileSize; }
 
@@ -165,6 +155,14 @@ public:
   // individual tile's rect in relation to the valid region.
   // Setting the resolution will invalidate the buffer.
   float GetResolution() const { return mResolution; }
+  void SetResolution(float aResolution) {
+    if (mResolution == aResolution) {
+      return;
+    }
+
+    Update(nsIntRegion(), nsIntRegion());
+    mResolution = aResolution;
+  }
   bool IsLowPrecision() const { return mResolution < 1; }
 
   typedef Tile* Iterator;
@@ -180,10 +178,6 @@ protected:
   // to the implementor.
   void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
 
-  // Return a reference to this tile in GetTile when the requested tile offset
-  // does not exist.
-  Tile mPlaceHolderTile;
-
   nsIntRegion     mValidRegion;
   nsIntRegion     mPaintedRegion;
 
@@ -196,8 +190,6 @@ protected:
    * tiles is scaled by mResolution.
    */
   nsTArray  mRetainedTiles;
-  int             mFirstTileX;
-  int             mFirstTileY;
   int             mRetainedWidth;  // in tiles
   int             mRetainedHeight; // in tiles
   float           mResolution;
@@ -257,39 +249,24 @@ static inline int floor_div(int a, int b)
   }
 }
 
-template bool
-TiledLayerBuffer::HasTile(const gfx::IntPoint& aTileOrigin) const {
-  gfx::IntSize scaledTileSize = GetScaledTileSize();
-  return HasTile(floor_div(aTileOrigin.x, scaledTileSize.width) - mFirstTileX,
-                 floor_div(aTileOrigin.y, scaledTileSize.height) - mFirstTileY);
-}
-
-template Tile&
-TiledLayerBuffer::GetTile(const nsIntPoint& aTileOrigin)
-{
-  if (HasTile(aTileOrigin)) {
-    return mRetainedTiles[TileIndex(aTileOrigin)];
-  }
-  return mPlaceHolderTile;
-}
-
-template int
-TiledLayerBuffer::TileIndex(const gfx::IntPoint& aTileOrigin) const
+template Tile
+TiledLayerBuffer::GetTile(const nsIntPoint& aTileOrigin) const
 {
+  // TODO Cache firstTileOriginX/firstTileOriginY
   // Find the tile x/y of the first tile and the target tile relative to the (0, 0)
   // origin, the difference is the tile x/y relative to the start of the tile buffer.
   gfx::IntSize scaledTileSize = GetScaledTileSize();
-  return TileIndex(floor_div(aTileOrigin.x, scaledTileSize.width) - mFirstTileX,
-                   floor_div(aTileOrigin.y, scaledTileSize.height) - mFirstTileY);
+  int firstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
+  int firstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
+  return GetTile(floor_div(aTileOrigin.x, scaledTileSize.width) - firstTileX,
+                 floor_div(aTileOrigin.y, scaledTileSize.height) - firstTileY);
 }
 
-template Tile&
-TiledLayerBuffer::GetTile(int x, int y)
+template Tile
+TiledLayerBuffer::GetTile(int x, int y) const
 {
-  if (HasTile(x, y)) {
-    return mRetainedTiles[TileIndex(x, y)];
-  }
-  return mPlaceHolderTile;
+  int index = x * mRetainedHeight + y;
+  return mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
 }
 
 template void
@@ -305,15 +282,15 @@ TiledLayerBuffer::Dump(std::stringstream& aStream,
 
     for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
       int32_t tileStartY = GetTileStart(y, scaledTileSize.height);
-      nsIntPoint tileOrigin = nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
-                                         RoundDownToTileEdge(y, scaledTileSize.height));
-      Tile& tileTexture = GetTile(tileOrigin);
+      Tile tileTexture =
+        GetTile(nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
+                           RoundDownToTileEdge(y, scaledTileSize.height)));
       int32_t h = scaledTileSize.height - tileStartY;
 
       aStream << "\n" << aPrefix << "Tile (x=" <<
         RoundDownToTileEdge(x, scaledTileSize.width) << ", y=" <<
         RoundDownToTileEdge(y, scaledTileSize.height) << "): ";
-      if (!tileTexture.IsPlaceholderTile()) {
+      if (tileTexture != AsDerived().GetPlaceholderTile()) {
         tileTexture.DumpTexture(aStream);
       } else {
         aStream << "empty tile";
@@ -620,10 +597,6 @@ TiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
 
   mRetainedTiles = newRetainedTiles;
   mValidRegion = newValidRegion;
-
-  mFirstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
-  mFirstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
-
   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp
index 26314d9663f..0c07cd3865d 100644
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -374,7 +374,7 @@ int32_t
 gfxMemorySharedReadLock::ReadUnlock()
 {
   int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount);
-  MOZ_ASSERT(readCount >= 0);
+  NS_ASSERTION(readCount >= 0, "ReadUnlock called without ReadLock.");
 
   return readCount;
 }
@@ -424,7 +424,7 @@ gfxShmSharedReadLock::ReadUnlock() {
   }
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
-  MOZ_ASSERT(readCount >= 0);
+  NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock.");
   if (readCount <= 0) {
     mAllocator->FreeShmemSection(mShmemSection);
   }
@@ -520,7 +520,6 @@ TileClient::TileClient(const TileClient& o)
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
-  mUpdateRect = o.mUpdateRect;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
@@ -540,7 +539,6 @@ TileClient::operator=(const TileClient& o)
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
-  mUpdateRect = o.mUpdateRect;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
@@ -611,8 +609,6 @@ CopyFrontToBack(TextureClient* aFront,
 
   gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
   aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
-
-  aFront->Unlock();
   return true;
 }
 
@@ -709,9 +705,9 @@ TileClient::DiscardBackBuffer()
        mManager->ReportClientLost(*mBackBufferOnWhite);
      }
     } else {
-      mManager->ReturnTextureClientDeferred(*mBackBuffer);
+      mManager->ReturnTextureClient(*mBackBuffer);
       if (mBackBufferOnWhite) {
-        mManager->ReturnTextureClientDeferred(*mBackBufferOnWhite);
+        mManager->ReturnTextureClient(*mBackBufferOnWhite);
       }
     }
     mBackLock->ReadUnlock();
@@ -820,17 +816,23 @@ TileClient::GetTileDescriptor()
   if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) {
     return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
                                   mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
-                                  mUpdateRect,
                                   TileLock(uintptr_t(mFrontLock.get())));
   } else {
     gfxShmSharedReadLock *lock = static_cast(mFrontLock.get());
     return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
                                   mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
-                                  mUpdateRect,
                                   TileLock(lock->GetShmemSection()));
   }
 }
 
+void
+ClientTiledLayerBuffer::ReadUnlock() {
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    if (mRetainedTiles[i].IsPlaceholderTile()) continue;
+    mRetainedTiles[i].ReadUnlock();
+  }
+}
+
 void
 ClientTiledLayerBuffer::ReadLock() {
   for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
@@ -871,12 +873,9 @@ ClientTiledLayerBuffer::GetSurfaceDescriptorTiles()
       tileDesc = mRetainedTiles[i].GetTileDescriptor();
     }
     tiles.AppendElement(tileDesc);
-    mRetainedTiles[i].mUpdateRect = IntRect();
   }
   return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion,
-                                tiles,
-                                mFirstTileX, mFirstTileY,
-                                mRetainedWidth, mRetainedHeight,
+                                tiles, mRetainedWidth, mRetainedHeight,
                                 mResolution, mFrameResolution.xScale,
                                 mFrameResolution.yScale);
 }
@@ -1137,8 +1136,6 @@ ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
                         &createdTextureClient, extraPainted,
                         &backBufferOnWhite);
 
-  aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
-
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
   mPaintedRegion.Or(mPaintedRegion, extraPainted);
diff --git a/gfx/layers/client/TiledContentClient.h b/gfx/layers/client/TiledContentClient.h
index 6dfb20e1c0a..80a1a4b2f57 100644
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -270,7 +270,6 @@ struct TileClient
   RefPtr mBackLock;
   RefPtr mFrontLock;
   RefPtr mManager;
-  gfx::IntRect mUpdateRect;
   CompositableClient* mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   TimeStamp        mLastUpdate;
@@ -419,6 +418,8 @@ public:
                    LayerManager::DrawPaintedLayerCallback aCallback,
                    void* aCallbackData);
 
+  void ReadUnlock();
+
   void ReadLock();
 
   void Release();
@@ -444,15 +445,6 @@ public:
 
   SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
 
-  void SetResolution(float aResolution) {
-    if (mResolution == aResolution) {
-      return;
-    }
-
-    Update(nsIntRegion(), nsIntRegion());
-    mResolution = aResolution;
-  }
-
 protected:
   TileClient ValidateTile(TileClient aTile,
                           const nsIntPoint& aTileRect,
diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h
index d842808c23a..68e8093385d 100644
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -326,7 +326,6 @@ protected:
   virtual ~TextureHost();
 
 public:
-
   /**
    * Factory method.
    */
diff --git a/gfx/layers/composite/TiledContentHost.cpp b/gfx/layers/composite/TiledContentHost.cpp
index bf28b7d029e..c21e94c7fe7 100644
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -29,28 +29,198 @@ class Layer;
 
 TiledLayerBufferComposite::TiledLayerBufferComposite()
   : mFrameResolution()
+  , mHasDoubleBufferedTiles(false)
+  , mIsValid(false)
 {}
 
-TiledLayerBufferComposite::~TiledLayerBufferComposite()
-{
-  Clear();
-}
-
 /* static */ void
 TiledLayerBufferComposite::RecycleCallback(TextureHost* textureHost, void* aClosure)
 {
   textureHost->CompositorRecycle();
 }
 
+TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
+                                                     const SurfaceDescriptorTiles& aDescriptor,
+                                                     const nsIntRegion& aOldPaintedRegion,
+                                                     Compositor* aCompositor)
+{
+  mIsValid = true;
+  mHasDoubleBufferedTiles = false;
+  mValidRegion = aDescriptor.validRegion();
+  mPaintedRegion = aDescriptor.paintedRegion();
+  mRetainedWidth = aDescriptor.retainedWidth();
+  mRetainedHeight = aDescriptor.retainedHeight();
+  mResolution = aDescriptor.resolution();
+  mFrameResolution = CSSToParentLayerScale2D(aDescriptor.frameXResolution(),
+                                             aDescriptor.frameYResolution());
+  if (mResolution == 0 || IsNaN(mResolution)) {
+    // There are divisions by mResolution so this protects the compositor process
+    // against malicious content processes and fuzzing.
+    mIsValid = false;
+    return;
+  }
+
+  // Combine any valid content that wasn't already uploaded
+  nsIntRegion oldPaintedRegion(aOldPaintedRegion);
+  oldPaintedRegion.And(oldPaintedRegion, mValidRegion);
+  mPaintedRegion.Or(mPaintedRegion, oldPaintedRegion);
+
+  bool isSameProcess = aAllocator->IsSameProcess();
+
+  const InfallibleTArray& tiles = aDescriptor.tiles();
+  for(size_t i = 0; i < tiles.Length(); i++) {
+    CompositableTextureHostRef texture;
+    CompositableTextureHostRef textureOnWhite;
+    const TileDescriptor& tileDesc = tiles[i];
+    switch (tileDesc.type()) {
+      case TileDescriptor::TTexturedTileDescriptor : {
+        texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent());
+        MaybeTexture onWhite = tileDesc.get_TexturedTileDescriptor().textureOnWhite();
+        if (onWhite.type() == MaybeTexture::TPTextureParent) {
+          textureOnWhite = TextureHost::AsTextureHost(onWhite.get_PTextureParent());
+        }
+        const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock();
+        nsRefPtr sharedLock;
+        if (ipcLock.type() == TileLock::TShmemSection) {
+          sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
+        } else {
+          if (!isSameProcess) {
+            // Trying to use a memory based lock instead of a shmem based one in
+            // the cross-process case is a bad security violation.
+            NS_ERROR("A client process may be trying to peek at the host's address space!");
+            // This tells the TiledContentHost that deserialization failed so that
+            // it can propagate the error.
+            mIsValid = false;
+
+            mRetainedTiles.Clear();
+            return;
+          }
+          sharedLock = reinterpret_cast(ipcLock.get_uintptr_t());
+          if (sharedLock) {
+            // The corresponding AddRef is in TiledClient::GetTileDescriptor
+            sharedLock.get()->Release();
+          }
+        }
+
+        CompositableTextureSourceRef textureSource;
+        CompositableTextureSourceRef textureSourceOnWhite;
+        if (texture) {
+          texture->SetCompositor(aCompositor);
+          texture->PrepareTextureSource(textureSource);
+        }
+        if (textureOnWhite) {
+          textureOnWhite->SetCompositor(aCompositor);
+          textureOnWhite->PrepareTextureSource(textureSourceOnWhite);
+        }
+        mRetainedTiles.AppendElement(TileHost(sharedLock,
+                                              texture.get(),
+                                              textureOnWhite.get(),
+                                              textureSource.get(),
+                                              textureSourceOnWhite.get()));
+        break;
+      }
+      default:
+        NS_WARNING("Unrecognised tile descriptor type");
+        // Fall through
+      case TileDescriptor::TPlaceholderTileDescriptor :
+        mRetainedTiles.AppendElement(GetPlaceholderTile());
+        break;
+    }
+    if (texture && !texture->HasInternalBuffer()) {
+      mHasDoubleBufferedTiles = true;
+    }
+  }
+}
+
+void
+TiledLayerBufferComposite::ReadUnlock()
+{
+  if (!IsValid()) {
+    return;
+  }
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    mRetainedTiles[i].ReadUnlock();
+  }
+}
+
+void
+TiledLayerBufferComposite::ReleaseTextureHosts()
+{
+  if (!IsValid()) {
+    return;
+  }
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    mRetainedTiles[i].mTextureHost = nullptr;
+    mRetainedTiles[i].mTextureHostOnWhite = nullptr;
+    mRetainedTiles[i].mTextureSource = nullptr;
+    mRetainedTiles[i].mTextureSourceOnWhite = nullptr;
+  }
+}
+
+void
+TiledLayerBufferComposite::Upload()
+{
+  if(!IsValid()) {
+    return;
+  }
+  // The TextureClients were created with the TextureFlags::IMMEDIATE_UPLOAD flag,
+  // so calling Update on all the texture hosts will perform the texture upload.
+  Update(mValidRegion, mPaintedRegion);
+  ClearPaintedRegion();
+}
+
+TileHost
+TiledLayerBufferComposite::ValidateTile(TileHost aTile,
+                                        const IntPoint& aTileOrigin,
+                                        const nsIntRegion& aDirtyRect)
+{
+  if (aTile.IsPlaceholderTile()) {
+    NS_WARNING("Placeholder tile encountered in painted region");
+    return aTile;
+  }
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+  printf_stderr("Upload tile %i, %i\n", aTileOrigin.x, aTileOrigin.y);
+  long start = PR_IntervalNow();
+#endif
+
+  MOZ_ASSERT(aTile.mTextureHost->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD);
+
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+  MOZ_ASSERT(!aTile.mTextureHostOnWhite);
+  // We possibly upload the entire texture contents here. This is a purposeful
+  // decision, as sub-image upload can often be slow and/or unreliable, but
+  // we may want to reevaluate this in the future.
+  // For !HasInternalBuffer() textures, this is likely a no-op.
+  aTile.mTextureHost->Updated(nullptr);
+#else
+  nsIntRegion tileUpdated = aDirtyRect.MovedBy(-aTileOrigin);
+  aTile.mTextureHost->Updated(&tileUpdated);
+  if (aTile.mTextureHostOnWhite) {
+    aTile.mTextureHostOnWhite->Updated(&tileUpdated);
+  }
+#endif
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+  if (PR_IntervalNow() - start > 1) {
+    printf_stderr("Tile Time to upload %i\n", PR_IntervalNow() - start);
+  }
+#endif
+  return aTile;
+}
+
 void
 TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor)
 {
   MOZ_ASSERT(aCompositor);
-  for (TileHost& tile : mRetainedTiles) {
-    if (tile.IsPlaceholderTile()) continue;
-    tile.mTextureHost->SetCompositor(aCompositor);
-    if (tile.mTextureHostOnWhite) {
-      tile.mTextureHostOnWhite->SetCompositor(aCompositor);
+  if (!IsValid()) {
+    return;
+  }
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    if (mRetainedTiles[i].IsPlaceholderTile()) continue;
+    mRetainedTiles[i].mTextureHost->SetCompositor(aCompositor);
+    if (mRetainedTiles[i].mTextureHostOnWhite) {
+      mRetainedTiles[i].mTextureHostOnWhite->SetCompositor(aCompositor);
     }
   }
 }
@@ -59,6 +229,10 @@ TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
   : ContentHost(aTextureInfo)
   , mTiledBuffer(TiledLayerBufferComposite())
   , mLowPrecisionTiledBuffer(TiledLayerBufferComposite())
+  , mOldTiledBuffer(TiledLayerBufferComposite())
+  , mOldLowPrecisionTiledBuffer(TiledLayerBufferComposite())
+  , mPendingUpload(false)
+  , mPendingLowPrecisionUpload(false)
 {
   MOZ_COUNT_CTOR(TiledContentHost);
 }
@@ -66,6 +240,28 @@ TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
 TiledContentHost::~TiledContentHost()
 {
   MOZ_COUNT_DTOR(TiledContentHost);
+
+  // Unlock any buffers that may still be locked. If we have a pending upload,
+  // we will need to unlock the buffer that was about to be uploaded.
+  // If a buffer that was being composited had double-buffered tiles, we will
+  // need to unlock that buffer too.
+  if (mPendingUpload) {
+    mTiledBuffer.ReadUnlock();
+    if (mOldTiledBuffer.HasDoubleBufferedTiles()) {
+      mOldTiledBuffer.ReadUnlock();
+    }
+  } else if (mTiledBuffer.HasDoubleBufferedTiles()) {
+    mTiledBuffer.ReadUnlock();
+  }
+
+  if (mPendingLowPrecisionUpload) {
+    mLowPrecisionTiledBuffer.ReadUnlock();
+    if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
+      mOldLowPrecisionTiledBuffer.ReadUnlock();
+    }
+  } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
+    mLowPrecisionTiledBuffer.ReadUnlock();
+  }
 }
 
 void
@@ -81,10 +277,33 @@ TiledContentHost::Detach(Layer* aLayer,
                          AttachFlags aFlags /* = NO_FLAGS */)
 {
   if (!mKeepAttached || aLayer == mLayer || aFlags & FORCE_DETACH) {
-    // Clear the TiledLayerBuffers, which will take care of releasing the
-    // copy-on-write locks.
-    mTiledBuffer.Clear();
-    mLowPrecisionTiledBuffer.Clear();
+
+    // Unlock any buffers that may still be locked. If we have a pending upload,
+    // we will need to unlock the buffer that was about to be uploaded.
+    // If a buffer that was being composited had double-buffered tiles, we will
+    // need to unlock that buffer too.
+    if (mPendingUpload) {
+      mTiledBuffer.ReadUnlock();
+      if (mOldTiledBuffer.HasDoubleBufferedTiles()) {
+        mOldTiledBuffer.ReadUnlock();
+      }
+    } else if (mTiledBuffer.HasDoubleBufferedTiles()) {
+      mTiledBuffer.ReadUnlock();
+    }
+
+    if (mPendingLowPrecisionUpload) {
+      mLowPrecisionTiledBuffer.ReadUnlock();
+      if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
+        mOldLowPrecisionTiledBuffer.ReadUnlock();
+      }
+    } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
+      mLowPrecisionTiledBuffer.ReadUnlock();
+    }
+
+    mTiledBuffer = TiledLayerBufferComposite();
+    mLowPrecisionTiledBuffer = TiledLayerBufferComposite();
+    mOldTiledBuffer = TiledLayerBufferComposite();
+    mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
   }
   CompositableHost::Detach(aLayer,aFlags);
 }
@@ -94,280 +313,60 @@ TiledContentHost::UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                                       const SurfaceDescriptorTiles& aTiledDescriptor)
 {
   if (aTiledDescriptor.resolution() < 1) {
-    if (!mLowPrecisionTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
-      return false;
-    }
-  } else {
-    if (!mTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-void
-UseTileTexture(CompositableTextureHostRef& aTexture,
-               CompositableTextureSourceRef& aTextureSource,
-               const IntRect& aUpdateRect,
-               TextureHost* aNewTexture,
-               Compositor* aCompositor)
-{
-  if (aTexture && aTexture->GetFormat() != aNewTexture->GetFormat()) {
-    // Only reuse textures if their format match the new texture's.
-    aTextureSource = nullptr;
-    aTexture = nullptr;
-  }
-  aTexture = aNewTexture;
-  if (aTexture) {
-    if (aCompositor) {
-      aTexture->SetCompositor(aCompositor);
-    }
-
-    if (!aUpdateRect.IsEmpty()) {
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-      aTexture->Updated(nullptr);
-#else
-      // We possibly upload the entire texture contents here. This is a purposeful
-      // decision, as sub-image upload can often be slow and/or unreliable, but
-      // we may want to reevaluate this in the future.
-      // For !HasInternalBuffer() textures, this is likely a no-op.
-      nsIntRegion region = aUpdateRect;
-      aTexture->Updated(®ion);
-#endif
-    }
-    aTexture->PrepareTextureSource(aTextureSource);
-  }
-}
-
-bool
-GetCopyOnWriteLock(const TileLock& ipcLock, TileHost& aTile, ISurfaceAllocator* aAllocator) {
-  MOZ_ASSERT(aAllocator);
-
-  nsRefPtr sharedLock;
-  if (ipcLock.type() == TileLock::TShmemSection) {
-    sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
-  } else {
-    if (!aAllocator->IsSameProcess()) {
-      // Trying to use a memory based lock instead of a shmem based one in
-      // the cross-process case is a bad security violation.
-      NS_ERROR("A client process may be trying to peek at the host's address space!");
-      return false;
-    }
-    sharedLock = reinterpret_cast(ipcLock.get_uintptr_t());
-    if (sharedLock) {
-      // The corresponding AddRef is in TiledClient::GetTileDescriptor
-      sharedLock.get()->Release();
-    }
-  }
-  aTile.mSharedLock = sharedLock;
-  return true;
-}
-
-bool
-TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles,
-                                    Compositor* aCompositor,
-                                    ISurfaceAllocator* aAllocator)
-{
-  if (mResolution != aTiles.resolution()) {
-    Clear();
-  }
-  MOZ_ASSERT(aAllocator);
-  MOZ_ASSERT(aCompositor);
-  if (!aAllocator || !aCompositor) {
-    return false;
-  }
-
-  if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) {
-    // There are divisions by mResolution so this protects the compositor process
-    // against malicious content processes and fuzzing.
-    return false;
-  }
-
-  int newFirstTileX = aTiles.firstTileX();
-  int newFirstTileY = aTiles.firstTileY();
-  int oldFirstTileX = mFirstTileX;
-  int oldFirstTileY = mFirstTileY;
-  int newRetainedWidth = aTiles.retainedWidth();
-  int newRetainedHeight = aTiles.retainedHeight();
-  int oldRetainedWidth = mRetainedWidth;
-  int oldRetainedHeight = mRetainedHeight;
-
-  const InfallibleTArray& tileDescriptors = aTiles.tiles();
-
-  nsTArray oldTiles;
-  mRetainedTiles.SwapElements(oldTiles);
-  mRetainedTiles.SetLength(tileDescriptors.Length());
-
-  // Step 1, we need to unlock tiles that don't have an internal buffer after the
-  // next frame where they are replaced.
-  // Since we are about to replace the tiles' textures, we need to keep their locks
-  // somewhere (in mPreviousSharedLock) until we composite the layer.
-  for (size_t i = 0; i < oldTiles.Length(); ++i) {
-    TileHost& tile = oldTiles[i];
-    // It can happen that we still have a previous lock at this point,
-    // if we changed a tile's front buffer (causing mSharedLock to
-    // go into mPreviousSharedLock, and then did not composite that tile until
-    // the next transaction, either because the tile is offscreen or because the
-    // two transactions happened with no composition in between (over-production).
-    tile.ReadUnlockPrevious();
-
-    if (tile.mTextureHost && !tile.mTextureHost->HasInternalBuffer()) {
-      MOZ_ASSERT(tile.mSharedLock);
-      int tileX = i % oldRetainedWidth + oldFirstTileX;
-      int tileY = i / oldRetainedWidth + oldFirstTileY;
-
-      if (tileX >= newFirstTileX && tileY >= newFirstTileY &&
-          tileX < (newFirstTileX + newRetainedWidth) &&
-          tileY < (newFirstTileY + newRetainedHeight)) {
-        // This tile still exist in the new buffer
-        tile.mPreviousSharedLock = tile.mSharedLock;
-        tile.mSharedLock = nullptr;
-      } else {
-        // This tile does not exist anymore in the new buffer because the size
-        // changed.
-        tile.ReadUnlock();
-      }
-    }
-
-    // By now we should not have anything in mSharedLock.
-    MOZ_ASSERT(!tile.mSharedLock);
-  }
-
-  // Step 2, move the tiles in mRetainedTiles at places that correspond to where
-  // they should be with the new retained with and height rather than the
-  // old one.
-  for (size_t i = 0; i < tileDescriptors.Length(); i++) {
-    int tileX = i % newRetainedWidth + newFirstTileX;
-    int tileY = i / newRetainedWidth + newFirstTileY;
-
-    // First, get the already existing tiles to the right place in the array,
-    // and use placeholders where there was no tiles.
-    if (tileX < oldFirstTileX || tileY < oldFirstTileY ||
-        tileX >= (oldFirstTileX + oldRetainedWidth) ||
-        tileY >= (oldFirstTileY + oldRetainedHeight)) {
-      mRetainedTiles[i] = GetPlaceholderTile();
+    if (mPendingLowPrecisionUpload) {
+      mLowPrecisionTiledBuffer.ReadUnlock();
     } else {
-      mRetainedTiles[i] = oldTiles[(tileY - oldFirstTileY) * oldRetainedWidth +
-                                   (tileX - oldFirstTileX)];
-      // If we hit this assertion it means we probably mixed something up in the
-      // logic that tries to reuse tiles on the compositor side. It is most likely
-      // benign, but we are missing some fast paths so let's try to make it not happen.
-      MOZ_ASSERT(tileX == mRetainedTiles[i].x && tileY == mRetainedTiles[i].y);
-    }
-  }
-
-  // It is important to remove the duplicated reference to tiles before calling
-  // TextureHost::PrepareTextureSource, etc. because depending on the textures
-  // ref counts we may or may not get some of the fast paths.
-  oldTiles.Clear();
-
-  // Step 3, handle the texture updates and release the copy-on-write locks.
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    const TileDescriptor& tileDesc = tileDescriptors[i];
-
-    TileHost& tile = mRetainedTiles[i];
-
-    switch (tileDesc.type()) {
-      case TileDescriptor::TTexturedTileDescriptor: {
-        const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
-
-        const TileLock& ipcLock = texturedDesc.sharedLock();
-        if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) {
-          return false;
-        }
-
-        RefPtr textureHost = TextureHost::AsTextureHost(
-          texturedDesc.textureParent()
-        );
-
-        RefPtr textureOnWhite = nullptr;
-        if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
-          textureOnWhite = TextureHost::AsTextureHost(
-            texturedDesc.textureOnWhite().get_PTextureParent()
-          );
-        }
-
-        UseTileTexture(tile.mTextureHost,
-                       tile.mTextureSource,
-                       texturedDesc.updateRect(),
-                       textureHost,
-                       aCompositor);
-
-        if (textureOnWhite) {
-          UseTileTexture(tile.mTextureHostOnWhite,
-                         tile.mTextureSourceOnWhite,
-                         texturedDesc.updateRect(),
-                         textureOnWhite,
-                         aCompositor);
-        } else {
-          // We could still have component alpha textures from a previous frame.
-          tile.mTextureSourceOnWhite = nullptr;
-          tile.mTextureHostOnWhite = nullptr;
-        }
-
-        if (textureHost->HasInternalBuffer()) {
-          // Now that we did the texture upload (in UseTileTexture), we can release
-          // the lock.
-          tile.ReadUnlock();
-        }
-
-        break;
-      }
-      default:
-        NS_WARNING("Unrecognised tile descriptor type");
-      case TileDescriptor::TPlaceholderTileDescriptor: {
-
-        if (tile.mTextureHost) {
-          tile.mTextureHost->UnbindTextureSource();
-          tile.mTextureSource = nullptr;
-        }
-        if (tile.mTextureHostOnWhite) {
-          tile.mTextureHostOnWhite->UnbindTextureSource();
-          tile.mTextureSourceOnWhite = nullptr;
-        }
-        // we may have a previous lock, and are about to loose our reference to it.
-        // It is okay to unlock it because we just destroyed the texture source.
-        tile.ReadUnlockPrevious();
-        tile = GetPlaceholderTile();
-
-        break;
+      mPendingLowPrecisionUpload = true;
+      // If the old buffer has double-buffered tiles, hang onto it so we can
+      // unlock it after we've composited the new buffer.
+      // We only need to hang onto the locks, but not the textures.
+      // Releasing the textures here can help prevent a memory spike in the
+      // situation that the client starts rendering new content before we get
+      // to composite the new buffer.
+      if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
+        mOldLowPrecisionTiledBuffer = mLowPrecisionTiledBuffer;
+        mOldLowPrecisionTiledBuffer.ReleaseTextureHosts();
       }
     }
-
-    tile.x = i % newRetainedWidth + newFirstTileX;
-    tile.y = i / newRetainedWidth + newFirstTileY;
+    mLowPrecisionTiledBuffer =
+      TiledLayerBufferComposite(aAllocator,
+                                aTiledDescriptor,
+                                mLowPrecisionTiledBuffer.GetPaintedRegion(),
+                                mCompositor);
+    if (!mLowPrecisionTiledBuffer.IsValid()) {
+      // Something bad happened. Stop here, return false (kills the child process),
+      // and do as little work as possible on the received data as it appears
+      // to be corrupted.
+      mPendingLowPrecisionUpload = false;
+      mPendingUpload = false;
+      return false;
+    }
+  } else {
+    if (mPendingUpload) {
+      mTiledBuffer.ReadUnlock();
+    } else {
+      mPendingUpload = true;
+      if (mTiledBuffer.HasDoubleBufferedTiles()) {
+        mOldTiledBuffer = mTiledBuffer;
+        mOldTiledBuffer.ReleaseTextureHosts();
+      }
+    }
+    mTiledBuffer = TiledLayerBufferComposite(aAllocator,
+                                             aTiledDescriptor,
+                                             mTiledBuffer.GetPaintedRegion(),
+                                             mCompositor);
+    if (!mTiledBuffer.IsValid()) {
+      // Something bad happened. Stop here, return false (kills the child process),
+      // and do as little work as possible on the received data as it appears
+      // to be corrupted.
+      mPendingLowPrecisionUpload = false;
+      mPendingUpload = false;
+      return false;
+    }
   }
-
-  mFirstTileX = newFirstTileX;
-  mFirstTileY = newFirstTileY;
-  mRetainedWidth = newRetainedWidth;
-  mRetainedHeight = newRetainedHeight;
-  mValidRegion = aTiles.validRegion();
-
-  mResolution = aTiles.resolution();
-  mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(),
-                                             aTiles.frameYResolution());
-
   return true;
 }
 
-void
-TiledLayerBufferComposite::Clear()
-{
-  for (TileHost& tile : mRetainedTiles) {
-    tile.ReadUnlock();
-    tile.ReadUnlockPrevious();
-  }
-  mRetainedTiles.Clear();
-  mFirstTileX = 0;
-  mFirstTileY = 0;
-  mRetainedWidth = 0;
-  mRetainedHeight = 0;
-  mValidRegion = nsIntRegion();
-  mPaintedRegion = nsIntRegion();
-  mResolution = 1.0;
-}
-
 void
 TiledContentHost::Composite(EffectChain& aEffectChain,
                             float aOpacity,
@@ -377,6 +376,25 @@ TiledContentHost::Composite(EffectChain& aEffectChain,
                             const nsIntRegion* aVisibleRegion /* = nullptr */)
 {
   MOZ_ASSERT(mCompositor);
+  if (mPendingUpload) {
+    mTiledBuffer.SetCompositor(mCompositor);
+    mTiledBuffer.Upload();
+
+    // For a single-buffered tiled buffer, Upload will upload the shared memory
+    // surface to texture memory and we no longer need to read from them.
+    if (!mTiledBuffer.HasDoubleBufferedTiles()) {
+      mTiledBuffer.ReadUnlock();
+    }
+  }
+  if (mPendingLowPrecisionUpload) {
+    mLowPrecisionTiledBuffer.SetCompositor(mCompositor);
+    mLowPrecisionTiledBuffer.Upload();
+
+    if (!mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
+      mLowPrecisionTiledBuffer.ReadUnlock();
+    }
+  }
+
   // Reduce the opacity of the low-precision buffer to make it a
   // little more subtle and less jarring. In particular, text
   // rendered at low-resolution and scaled tends to look pretty
@@ -422,11 +440,24 @@ TiledContentHost::Composite(EffectChain& aEffectChain,
                     aFilter, aClipRect, *renderRegion, aTransform);
   RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aFilter,
                     aClipRect, *renderRegion, aTransform);
+
+  // Now release the old buffer if it had double-buffered tiles, as we can
+  // guarantee that they're no longer on the screen (and so any locks that may
+  // have been held have been released).
+  if (mPendingUpload && mOldTiledBuffer.HasDoubleBufferedTiles()) {
+    mOldTiledBuffer.ReadUnlock();
+    mOldTiledBuffer = TiledLayerBufferComposite();
+  }
+  if (mPendingLowPrecisionUpload && mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
+    mOldLowPrecisionTiledBuffer.ReadUnlock();
+    mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
+  }
+  mPendingUpload = mPendingLowPrecisionUpload = false;
 }
 
 
 void
-TiledContentHost::RenderTile(TileHost& aTile,
+TiledContentHost::RenderTile(const TileHost& aTile,
                              const gfxRGBA* aBackgroundColor,
                              EffectChain& aEffectChain,
                              float aOpacity,
@@ -493,7 +524,6 @@ TiledContentHost::RenderTile(TileHost& aTile,
   }
   mCompositor->DrawDiagnostics(flags,
                                aScreenRegion, aClipRect, aTransform, mFlashCounter);
-  aTile.ReadUnlockPrevious();
 }
 
 void
@@ -561,10 +591,10 @@ TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
         h = visibleRect.y + visibleRect.height - y;
       }
 
-      nsIntPoint tileOrigin = nsIntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width),
-                                         aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height));
-      TileHost& tileTexture = aLayerBuffer.GetTile(tileOrigin);
-      if (!tileTexture.IsPlaceholderTile()) {
+      TileHost tileTexture = aLayerBuffer.
+        GetTile(IntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width),
+                         aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height)));
+      if (tileTexture != aLayerBuffer.GetPlaceholderTile()) {
         nsIntRegion tileDrawRegion;
         tileDrawRegion.And(IntRect(x, y, w, h), aLayerBuffer.GetValidRegion());
         tileDrawRegion.And(tileDrawRegion, aVisibleRegion);
diff --git a/gfx/layers/composite/TiledContentHost.h b/gfx/layers/composite/TiledContentHost.h
index 2647c6f1557..13a89a20797 100644
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -52,8 +52,6 @@ public:
   // essentially, this is a sentinel used to represent an invalid or blank
   // tile.
   TileHost()
-  : x(-1)
-  , y(-1)
   {}
 
   // Constructs a TileHost from a gfxSharedReadLock and TextureHost.
@@ -67,8 +65,6 @@ public:
     , mTextureHostOnWhite(aTextureHostOnWhite)
     , mTextureSource(aSource)
     , mTextureSourceOnWhite(aSourceOnWhite)
-    , x(-1)
-    , y(-1)
   {}
 
   TileHost(const TileHost& o) {
@@ -77,9 +73,6 @@ public:
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
-    mPreviousSharedLock = o.mPreviousSharedLock;
-    x = o.x;
-    y = o.y;
   }
   TileHost& operator=(const TileHost& o) {
     if (this == &o) {
@@ -90,9 +83,6 @@ public:
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
-    mPreviousSharedLock = o.mPreviousSharedLock;
-    x = o.x;
-    y = o.y;
     return *this;
   }
 
@@ -108,14 +98,6 @@ public:
   void ReadUnlock() {
     if (mSharedLock) {
       mSharedLock->ReadUnlock();
-      mSharedLock = nullptr;
-    }
-  }
-
-  void ReadUnlockPrevious() {
-    if (mPreviousSharedLock) {
-      mPreviousSharedLock->ReadUnlock();
-      mPreviousSharedLock = nullptr;
     }
   }
 
@@ -129,14 +111,10 @@ public:
   }
 
   RefPtr mSharedLock;
-  RefPtr mPreviousSharedLock;
   CompositableTextureHostRef mTextureHost;
   CompositableTextureHostRef mTextureHostOnWhite;
   mutable CompositableTextureSourceRef mTextureSource;
   mutable CompositableTextureSourceRef mTextureSourceOnWhite;
-  // This is not strictly necessary but makes debugging whole lot easier.
-  int x;
-  int y;
 };
 
 class TiledLayerBufferComposite
@@ -145,14 +123,13 @@ class TiledLayerBufferComposite
   friend class TiledLayerBuffer;
 
 public:
+  typedef TiledLayerBuffer::Iterator Iterator;
+
   TiledLayerBufferComposite();
-  ~TiledLayerBufferComposite();
-
-  bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors,
-                Compositor* aCompositor,
-                ISurfaceAllocator* aAllocator);
-
-  void Clear();
+  TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
+                            const SurfaceDescriptorTiles& aDescriptor,
+                            const nsIntRegion& aOldPaintedRegion,
+                            Compositor* aCompositor);
 
   TileHost GetPlaceholderTile() const { return TileHost(); }
 
@@ -160,16 +137,43 @@ public:
   // by the sum of the resolutions of all parent layers' FrameMetrics.
   const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
 
+  void ReadUnlock();
+
+  void ReleaseTextureHosts();
+
+  /**
+   * This will synchronously upload any necessary texture contents, making the
+   * sources immediately available for compositing. For texture hosts that
+   * don't have an internal buffer, this is unlikely to actually do anything.
+   */
+  void Upload();
+
   void SetCompositor(Compositor* aCompositor);
 
+  bool HasDoubleBufferedTiles() { return mHasDoubleBufferedTiles; }
+
+  bool IsValid() const { return mIsValid; }
+
   // Recycle callback for TextureHost.
   // Used when TiledContentClient is present in client side.
   static void RecycleCallback(TextureHost* textureHost, void* aClosure);
 
 protected:
+  TileHost ValidateTile(TileHost aTile,
+                        const gfx::IntPoint& aTileRect,
+                        const nsIntRegion& dirtyRect);
+
+  // do nothing, the desctructor in the texture host takes care of releasing resources
+  void ReleaseTile(TileHost aTile) {}
+
   void SwapTiles(TileHost& aTileA, TileHost& aTileB) { std::swap(aTileA, aTileB); }
 
+  void UnlockTile(TileHost aTile) {}
+  void PostValidate(const nsIntRegion& aPaintRegion) {}
+private:
   CSSToParentLayerScale2D mFrameResolution;
+  bool mHasDoubleBufferedTiles;
+  bool mIsValid;
 };
 
 /**
@@ -229,10 +233,11 @@ public:
 
   virtual void SetCompositor(Compositor* aCompositor) override
   {
-    MOZ_ASSERT(aCompositor);
     CompositableHost::SetCompositor(aCompositor);
     mTiledBuffer.SetCompositor(aCompositor);
     mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
+    mOldTiledBuffer.SetCompositor(aCompositor);
+    mOldLowPrecisionTiledBuffer.SetCompositor(aCompositor);
   }
 
   virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
@@ -274,7 +279,7 @@ private:
                          gfx::Matrix4x4 aTransform);
 
   // Renders a single given tile.
-  void RenderTile(TileHost& aTile,
+  void RenderTile(const TileHost& aTile,
                   const gfxRGBA* aBackgroundColor,
                   EffectChain& aEffectChain,
                   float aOpacity,
@@ -289,6 +294,10 @@ private:
 
   TiledLayerBufferComposite    mTiledBuffer;
   TiledLayerBufferComposite    mLowPrecisionTiledBuffer;
+  TiledLayerBufferComposite    mOldTiledBuffer;
+  TiledLayerBufferComposite    mOldLowPrecisionTiledBuffer;
+  bool                         mPendingUpload;
+  bool                         mPendingLowPrecisionUpload;
 };
 
 }
diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh
index 22fdd827bd3..c1a0a12e078 100644
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -315,7 +315,6 @@ union MaybeTexture {
 struct TexturedTileDescriptor {
   PTexture texture;
   MaybeTexture textureOnWhite;
-  IntRect updateRect;
   TileLock sharedLock;
 };
 
@@ -331,8 +330,6 @@ struct SurfaceDescriptorTiles {
   nsIntRegion validRegion;
   nsIntRegion paintedRegion;
   TileDescriptor[] tiles;
-  int         firstTileX;
-  int         firstTileY;
   int         retainedWidth;
   int         retainedHeight;
   float       resolution;

From e65454c0f6a02861824c268aa61dfb9eb2bbb21d Mon Sep 17 00:00:00 2001
From: Kim Moir 
Date: Mon, 25 May 2015 14:24:19 -0400
Subject: [PATCH 101/107] No bug update mozharness.json to 12ce04f3ec51

---
 testing/mozharness/mozharness.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/testing/mozharness/mozharness.json b/testing/mozharness/mozharness.json
index 91b01f887c5..0f5534b8f63 100644
--- a/testing/mozharness/mozharness.json
+++ b/testing/mozharness/mozharness.json
@@ -1,4 +1,4 @@
 {
     "repo": "https://hg.mozilla.org/build/mozharness",
-    "revision": "66c655a58460"
+    "revision": "12ce04f3ec51"
 }

From bc8e255c0b93f63198790f51d5ce735429999e56 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 11:35:28 -0700
Subject: [PATCH 102/107] Bumping gaia.json for 4 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/27818d086e44
Author: autolander 
Desc: Bug 1167202 - merge pull request #30177 from yzen:bug-1167202 to mozilla-b2g:master

========

https://hg.mozilla.org/integration/gaia-central/rev/a5df0c8e8c10
Author: Yura Zenevich 
Desc: Bug 1167202 - adding strings for newly supported switch role.

========

https://hg.mozilla.org/integration/gaia-central/rev/dfd14031c8ef
Author: autolander 
Desc: Bug 1166612 - merge pull request #30145 from dwi2:bug1166612 to mozilla-b2g:master

========

https://hg.mozilla.org/integration/gaia-central/rev/ef87fd9f68de
Author: Tzu-Lin Huang 
Desc: Bug 1166612 - [Stingray] Hide filter when entering edit mode
---
 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 9fbba244194..234e49722aa 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "a68746f8fdce1d4c37751f72140dd9f2694debca", 
+        "git_revision": "3435c67e924a18a55f601485b4fd0e5982fe7ae0", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "7c8fece4dd8f7ddd61dbad1930e3ea661c784f90", 
+    "revision": "27818d086e44aad7da44d971f9e30d4c5afe98c8", 
     "repo_path": "integration/gaia-central"
 }

From 6f560a23032054c938da3d9f3ff2acc209f19ebc Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 11:37:24 -0700
Subject: [PATCH 103/107] 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 6cd6465ddf7..99703001366 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 5fa81b12672..cb87981b28c 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 33916d64c69..83bc48a90bf 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 0601f285dec..f34ac26831d 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 389becf7f1f..59547733e18 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 b1b28df8ec6..1fae5658775 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 33916d64c69..83bc48a90bf 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 be6f81ad3d6..f34b403df3f 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 c39d726f197..36425427803 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 612114bcdf0..0470d0a438f 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+  
   
   
   

From 12892845fd0f91de0a3bc9f60a4c9bb07d63bab0 Mon Sep 17 00:00:00 2001
From: Kartikaya Gupta 
Date: Mon, 25 May 2015 15:32:09 -0400
Subject: [PATCH 104/107] Bug 1117958 - Allow any debugging options to the run
 or gtest mach subcommands to automatically enable debugging. r=gps

---
 python/mozbuild/mozbuild/mach_commands.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py
index 549501708b7..d2cd24dc5a1 100644
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -645,7 +645,7 @@ class GTestCommands(MachCommandBase):
 
     @CommandArgumentGroup('debugging')
     @CommandArgument('--debug', action='store_true', group='debugging',
-        help='Enable the debugger. Not specifying a --debugger option will result in the default debugger being used. The following arguments have no effect without this.')
+        help='Enable the debugger. Not specifying a --debugger option will result in the default debugger being used.')
     @CommandArgument('--debugger', default=None, type=str, group='debugging',
         help='Name of debugger to use.')
     @CommandArgument('--debugger-args', default=None, metavar='params', type=str,
@@ -661,7 +661,7 @@ class GTestCommands(MachCommandBase):
         app_path = self.get_binary_path('app')
         args = [app_path, '-unittest'];
 
-        if debug:
+        if debug or debugger or debugger_args:
             args = self.prepend_debugger_args(args, debugger, debugger_args)
 
         cwd = os.path.join(self.topobjdir, '_tests', 'gtest')
@@ -862,7 +862,7 @@ class RunProgram(MachCommandBase):
 
     @CommandArgumentGroup('debugging')
     @CommandArgument('--debug', action='store_true', group='debugging',
-        help='Enable the debugger. Not specifying a --debugger option will result in the default debugger being used. The following arguments have no effect without this.')
+        help='Enable the debugger. Not specifying a --debugger option will result in the default debugger being used.')
     @CommandArgument('--debugger', default=None, type=str, group='debugging',
         help='Name of debugger to use.')
     @CommandArgument('--debugparams', default=None, metavar='params', type=str,
@@ -921,7 +921,7 @@ class RunProgram(MachCommandBase):
 
         extra_env = {}
 
-        if debug:
+        if debug or debugger or debugparams:
             import mozdebug
             if not debugger:
                 # No debugger name was provided. Look for the default ones on

From 4463c794e3b96541554e39d4362cae2a8a848f78 Mon Sep 17 00:00:00 2001
From: Kartikaya Gupta 
Date: Mon, 25 May 2015 15:32:20 -0400
Subject: [PATCH 105/107] Bug 1167721 - Ensure we trigger a repaint when
 dropping velocity to zero in CancelAnimation. r=botond

---
 gfx/layers/apz/src/AsyncPanZoomController.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp
index 34f17f63439..e238dda682c 100644
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2275,7 +2275,11 @@ void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) {
   SetState(NOTHING);
   mAnimation = nullptr;
   // Since there is no animation in progress now the axes should
-  // have no velocity either.
+  // have no velocity either. If we are dropping the velocity from a non-zero
+  // value we should trigger a repaint as the displayport margins are dependent
+  // on the velocity and the last repaint request might not have good margins
+  // any more.
+  bool repaint = !IsZero(GetVelocityVector());
   mX.SetVelocity(0);
   mY.SetVelocity(0);
   // Setting the state to nothing and cancelling the animation can
@@ -2283,6 +2287,9 @@ void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) {
   // overscroll here.
   if (!(aFlags & ExcludeOverscroll) && IsOverscrolled()) {
     ClearOverscroll();
+    repaint = true;
+  }
+  if (repaint) {
     RequestContentRepaint();
     ScheduleComposite();
     UpdateSharedCompositorFrameMetrics();

From af72084a5a50185c20509c4d90750b8e934637e5 Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 13:40:33 -0700
Subject: [PATCH 106/107] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump

========

https://hg.mozilla.org/integration/gaia-central/rev/70c21fc8fb4b
Author: autolander 
Desc: Bug 1167637 - merge pull request #30205 from mikehenrty:bug-1167637-apps-tests to mozilla-b2g:master

========

https://hg.mozilla.org/integration/gaia-central/rev/a1a69da6c3da
Author: Michael Henretty 
Desc: Bug 1167637 - Re-enable apps_test.js
---
 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 234e49722aa..999a36c90aa 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "3435c67e924a18a55f601485b4fd0e5982fe7ae0", 
+        "git_revision": "7cd4130d4f988562a77d126860408ada65bb95ef", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "27818d086e44aad7da44d971f9e30d4c5afe98c8", 
+    "revision": "70c21fc8fb4b014e751d1442161c4beabbfaa6bd", 
     "repo_path": "integration/gaia-central"
 }

From 639861b401d2c813a0a93708821bb5bbf78f4e3c Mon Sep 17 00:00:00 2001
From: B2G Bumper Bot 
Date: Mon, 25 May 2015 13:42:56 -0700
Subject: [PATCH 107/107] 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 99703001366..ff1206a7afc 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 cb87981b28c..3bb72cf995e 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 83bc48a90bf..c7b7ab69468 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 f34ac26831d..9126978cedf 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 59547733e18..5647782a0cc 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 1fae5658775..1f01e0dafdd 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 83bc48a90bf..c7b7ab69468 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 f34b403df3f..ee3cca6b089 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 36425427803..667d23916d0 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 0470d0a438f..4e4502b4ec0 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   
     
   
-  
+