--- a/accessible/tests/mochitest/tree/a11y.ini +++ b/accessible/tests/mochitest/tree/a11y.ini @@ -40,13 +40,14 @@ skip-if = buildapp == "mulet" [test_invalidationlist.html] [test_list.html] [test_map.html] [test_media.html] skip-if = buildapp == "mulet" [test_select.html] [test_tabbox.xul] [test_tabbrowser.xul] +skip-if = os == 'linux' && debug # Bug 1389365 [test_table.html] [test_tree.xul] [test_txtcntr.html] [test_txtctrl.html] [test_txtctrl.xul]
--- a/browser/extensions/screenshots/test/browser/browser.ini +++ b/browser/extensions/screenshots/test/browser/browser.ini @@ -1,5 +1,6 @@ [DEFAULT] support-files = head.js [browser_screenshots_ui_check.js] +skip-if = os == 'win' && debug # Bug 1394967 \ No newline at end of file
--- a/browser/modules/WindowsPreviewPerTab.jsm +++ b/browser/modules/WindowsPreviewPerTab.jsm @@ -81,19 +81,37 @@ function _imageFromURI(uri, privateMode, channel.QueryInterface(Ci.nsIPrivateBrowsingChannel); channel.setPrivate(privateMode); } catch (e) { // Ignore channels which do not support nsIPrivateBrowsingChannel } NetUtil.asyncFetch(channel, function(inputStream, resultCode) { if (!Components.isSuccessCode(resultCode)) return; + + const decodeCallback = { + onImageReady(image, status) { + if (!image) { + // We failed, so use the default favicon (only if this wasn't the + // default favicon). + let defaultURI = PlacesUtils.favicons.defaultFavicon; + if (!defaultURI.equals(uri)) { + _imageFromURI(defaultURI, privateMode, callback); + return; + } + } + + callback(image); + } + }; + try { - let out_img = imgTools.decodeImage(inputStream, channel.contentType); - callback(out_img); + let threadManager = Cc["@mozilla.org/thread-manager;1"].getService(); + imgTools.decodeImage(inputStream, channel.contentType, decodeCallback, + threadManager.currentThread); } catch (e) { // We failed, so use the default favicon (only if this wasn't the default // favicon). let defaultURI = PlacesUtils.favicons.defaultFavicon; if (!defaultURI.equals(uri)) _imageFromURI(defaultURI, privateMode, callback); } });
--- a/build/moz.configure/rust.configure +++ b/build/moz.configure/rust.configure @@ -1,18 +1,26 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@imports(_from='os.path', _import='expanduser') +def add_rustup_path(what): + # rustup installs rustc/cargo into ~/.cargo/bin by default, + # so look there if the binaries aren't in $PATH. + return [what, os.path.join(expanduser('~/.cargo/bin'), what)] + + # Rust is required by `rust_compiler` below. We allow_missing here # to propagate failures to the better error message there. -rustc = check_prog('RUSTC', ['rustc'], allow_missing=True) -cargo = check_prog('CARGO', ['cargo'], allow_missing=True) +rustc = check_prog('RUSTC', add_rustup_path('rustc'), allow_missing=True) +cargo = check_prog('CARGO', add_rustup_path('cargo'), allow_missing=True) @depends_if(rustc) @checking('rustc version', lambda info: info.version) def rustc_info(rustc): out = check_cmd_output(rustc, '--version', '--verbose').splitlines() info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:]) return namespace(
--- a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js +++ b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js @@ -303,23 +303,19 @@ function copyImageToClipboard(data) { let clipboardService = Cc["@mozilla.org/widget/clipboard;1"] .getService(Ci.nsIClipboard); let imageTools = Cc["@mozilla.org/image/tools;1"] .getService(Ci.imgITools); // Image data is stored as base64 in the test. let image = atob(data); - let input = Cc["@mozilla.org/io/string-input-stream;1"] - .createInstance(Ci.nsIStringInputStream); - input.setData(image, image.length); - let imgPtr = Cc["@mozilla.org/supports-interface-pointer;1"] .createInstance(Ci.nsISupportsInterfacePointer); - imgPtr.data = imageTools.decodeImage(input, "image/png"); + imgPtr.data = imageTools.decodeImageBuffer(image, image.length, "image/png"); let xferable = Cc["@mozilla.org/widget/transferable;1"] .createInstance(Ci.nsITransferable); xferable.init(null); xferable.addDataFlavor("image/png"); xferable.setTransferData("image/png", imgPtr, -1); clipboardService.setData(xferable, null, clipboardService.kGlobalClipboard);
--- a/devtools/shared/gcli/commands/screenshot.js +++ b/devtools/shared/gcli/commands/screenshot.js @@ -358,55 +358,74 @@ function getFilename(defaultName) { } /** * Save the image data to the clipboard. This returns a promise, so it can * be treated exactly like imgur / file processing, but it's really sync * for now. */ function saveToClipboard(context, reply) { - try { - const channel = NetUtil.newChannel({ - uri: reply.data, - loadUsingSystemPrincipal: true, - contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE - }); - const input = channel.open2(); + return new Promise(resolve => { + try { + const channel = NetUtil.newChannel({ + uri: reply.data, + loadUsingSystemPrincipal: true, + contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE + }); + const input = channel.open2(); + + const loadContext = context.environment.chromeWindow + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); - const loadContext = context.environment.chromeWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsILoadContext); + const callback = { + onImageReady(container, status) { + if (!container) { + console.error("imgTools.decodeImageAsync failed"); + reply.destinations.push(l10n.lookup("screenshotErrorCopying")); + resolve(); + return; + } - const imgTools = Cc["@mozilla.org/image/tools;1"] - .getService(Ci.imgITools); - - const container = imgTools.decodeImage(input, channel.contentType); + try { + const wrapped = Cc["@mozilla.org/supports-interface-pointer;1"] + .createInstance(Ci.nsISupportsInterfacePointer); + wrapped.data = container; - const wrapped = Cc["@mozilla.org/supports-interface-pointer;1"] - .createInstance(Ci.nsISupportsInterfacePointer); - wrapped.data = container; + const trans = Cc["@mozilla.org/widget/transferable;1"] + .createInstance(Ci.nsITransferable); + trans.init(loadContext); + trans.addDataFlavor(channel.contentType); + trans.setTransferData(channel.contentType, wrapped, -1); - const trans = Cc["@mozilla.org/widget/transferable;1"] - .createInstance(Ci.nsITransferable); - trans.init(loadContext); - trans.addDataFlavor(channel.contentType); - trans.setTransferData(channel.contentType, wrapped, -1); + const clip = Cc["@mozilla.org/widget/clipboard;1"] + .getService(Ci.nsIClipboard); + clip.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard); - const clip = Cc["@mozilla.org/widget/clipboard;1"] - .getService(Ci.nsIClipboard); - clip.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard); + reply.destinations.push(l10n.lookup("screenshotCopied")); + } catch (ex) { + console.error(ex); + reply.destinations.push(l10n.lookup("screenshotErrorCopying")); + } + resolve(); + } + }; - reply.destinations.push(l10n.lookup("screenshotCopied")); - } catch (ex) { - console.error(ex); - reply.destinations.push(l10n.lookup("screenshotErrorCopying")); - } - - return Promise.resolve(); + const threadManager = Cc["@mozilla.org/thread-manager;1"].getService(); + const imgTools = Cc["@mozilla.org/image/tools;1"] + .getService(Ci.imgITools); + imgTools.decodeImageAsync(input, channel.contentType, callback, + threadManager.currentThread); + } catch (ex) { + console.error(ex); + reply.destinations.push(l10n.lookup("screenshotErrorCopying")); + resolve(); + } + }); } /** * Upload screenshot data to Imgur, returning a promise of a URL (as a string) */ function uploadToImgur(reply) { return new Promise((resolve, reject) => { const xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
--- a/dom/canvas/ImageBitmap.cpp +++ b/dom/canvas/ImageBitmap.cpp @@ -8,16 +8,17 @@ #include "mozilla/CheckedInt.h" #include "mozilla/dom/ImageBitmapBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRunnable.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Swizzle.h" +#include "mozilla/Mutex.h" #include "ImageBitmapColorUtils.h" #include "ImageBitmapUtils.h" #include "ImageUtils.h" #include "imgTools.h" using namespace mozilla::gfx; using namespace mozilla::layers; @@ -1073,324 +1074,202 @@ AsyncFulfillImageBitmapPromise(Promise* NS_DispatchToCurrentThread(task); // Actually, to the main-thread. } else { RefPtr<FulfillImageBitmapPromiseWorkerTask> task = new FulfillImageBitmapPromiseWorkerTask(aPromise, aImageBitmap); task->Dispatch(); // Actually, to the current worker-thread. } } -static already_AddRefed<SourceSurface> -DecodeBlob(Blob& aBlob) +class CreateImageBitmapFromBlobRunnable; +class CreateImageBitmapFromBlobHolder; + +class CreateImageBitmapFromBlob final : public CancelableRunnable + , public imgIContainerCallback { - // Get the internal stream of the blob. - nsCOMPtr<nsIInputStream> stream; - ErrorResult error; - aBlob.Impl()->CreateInputStream(getter_AddRefs(stream), error); - if (NS_WARN_IF(error.Failed())) { - error.SuppressException(); - return nullptr; - } + friend class CreateImageBitmapFromBlobRunnable; - // Get the MIME type string of the blob. - // The type will be checked in the DecodeImage() method. - nsAutoString mimeTypeUTF16; - aBlob.GetType(mimeTypeUTF16); +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_IMGICONTAINERCALLBACK - // Get the Component object. - nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID); - if (NS_WARN_IF(!imgtool)) { - return nullptr; - } + static already_AddRefed<CreateImageBitmapFromBlob> + Create(Promise* aPromise, + nsIGlobalObject* aGlobal, + Blob& aBlob, + const Maybe<IntRect>& aCropRect, + nsIEventTarget* aMainThreadEventTarget); - // Decode image. - NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeTypeUTF16); // NS_ConvertUTF16toUTF8 ---|> nsAutoCString - nsCOMPtr<imgIContainer> imgContainer; - nsresult rv = imgtool->DecodeImage(stream, mimeTypeUTF8, getter_AddRefs(imgContainer)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } + NS_IMETHOD Run() override + { + MOZ_ASSERT(IsCurrentThread()); - // Get the surface out. - uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_WANT_DATA_SURFACE; - uint32_t whichFrame = imgIContainer::FRAME_FIRST; - RefPtr<SourceSurface> surface = imgContainer->GetFrame(whichFrame, frameFlags); + nsresult rv = StartDecodeAndCropBlob(); + if (NS_WARN_IF(NS_FAILED(rv))) { + DecodeAndCropBlobCompletedMainThread(nullptr, rv); + } - if (NS_WARN_IF(!surface)) { - return nullptr; + return NS_OK; } - return surface.forget(); -} - -static already_AddRefed<layers::Image> -DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect, - /*Output*/ IntSize& sourceSize) -{ - // Decode the blob into a SourceSurface. - RefPtr<SourceSurface> surface = DecodeBlob(aBlob); - - if (NS_WARN_IF(!surface)) { - return nullptr; - } - - // Set the _sourceSize_ output parameter. - sourceSize = surface->GetSize(); - - // Crop the source surface if needed. - RefPtr<SourceSurface> croppedSurface = surface; + // Called by the WorkerHolder. + void WorkerShuttingDown(); - if (aCropRect.isSome()) { - // The blob is just decoded into a RasterImage and not optimized yet, so the - // _surface_ we get is a DataSourceSurface which wraps the RasterImage's - // raw buffer. - // - // The _surface_ might already be optimized so that its type is not - // SurfaceType::DATA. However, we could keep using the generic cropping and - // copying since the decoded buffer is only used in this ImageBitmap so we - // should crop it to save memory usage. - // - // TODO: Bug1189632 is going to refactor this create-from-blob part to - // decode the blob off the main thread. Re-check if we should do - // cropping at this moment again there. - RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); - croppedSurface = CropAndCopyDataSourceSurface(dataSurface, aCropRect.ref()); - aCropRect->MoveTo(0, 0); - } - - if (NS_WARN_IF(!croppedSurface)) { - return nullptr; - } - - // Create an Image from the source surface. - RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface); - - if (NS_WARN_IF(!image)) { - return nullptr; - } - - return image.forget(); -} - -class CreateImageBitmapFromBlob -{ -protected: +private: CreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal, - Blob& aBlob, - const Maybe<IntRect>& aCropRect) - : mPromise(aPromise), - mGlobalObject(aGlobal), - mBlob(&aBlob), - mCropRect(aCropRect) + already_AddRefed<nsIInputStream> aInputStream, + const nsACString& aMimeType, + const Maybe<IntRect>& aCropRect, + nsIEventTarget* aMainThreadEventTarget) + : CancelableRunnable("dom::CreateImageBitmapFromBlob") + , mMutex("dom::CreateImageBitmapFromBlob::mMutex") + , mPromise(aPromise) + , mGlobalObject(aGlobal) + , mInputStream(Move(aInputStream)) + , mMimeType(aMimeType) + , mCropRect(aCropRect) + , mOriginalCropRect(aCropRect) + , mMainThreadEventTarget(aMainThreadEventTarget) + , mThread(GetCurrentVirtualThread()) { } virtual ~CreateImageBitmapFromBlob() { } - // Returns true on success, false on failure. - bool DoCreateImageBitmapFromBlob() + bool IsCurrentThread() const { - RefPtr<ImageBitmap> imageBitmap = CreateImageBitmap(); - - // handle errors while creating ImageBitmap - // (1) error occurs during reading of the object - // (2) the image data is not in a supported file format - // (3) the image data is corrupted - // All these three cases should reject the promise with "InvalidStateError" - // DOMException - if (!imageBitmap) { - return false; - } - - if (imageBitmap && mCropRect.isSome()) { - ErrorResult rv; - imageBitmap->SetPictureRect(mCropRect.ref(), rv); - - if (rv.Failed()) { - mPromise->MaybeReject(rv); - return false; - } - } - - imageBitmap->mAllocatedImageData = true; - - mPromise->MaybeResolve(imageBitmap); - return true; + return mThread == GetCurrentVirtualThread(); } - // Will return null on failure. In that case, mPromise will already - // be rejected with the right thing. - virtual already_AddRefed<ImageBitmap> CreateImageBitmap() = 0; + // Called on the owning thread. + nsresult StartDecodeAndCropBlob(); + + // Will be called when the decoding + cropping is completed on the + // main-thread. This could the not the owning thread! + void DecodeAndCropBlobCompletedMainThread(layers::Image* aImage, + nsresult aStatus); + + // Will be called when the decoding + cropping is completed on the owning + // thread. + void DecodeAndCropBlobCompletedOwningThread(layers::Image* aImage, + nsresult aStatus); + + // This is called on the main-thread only. + nsresult DecodeAndCropBlob(); + + Mutex mMutex; + // The access to this object is protected by mutex but is always nullified on + // the owning thread. + UniquePtr<CreateImageBitmapFromBlobHolder> mWorkerHolder; + + // Touched only on the owning thread. RefPtr<Promise> mPromise; + + // Touched only on the owning thread. nsCOMPtr<nsIGlobalObject> mGlobalObject; - RefPtr<mozilla::dom::Blob> mBlob; + + nsCOMPtr<nsIInputStream> mInputStream; + nsCString mMimeType; Maybe<IntRect> mCropRect; + Maybe<IntRect> mOriginalCropRect; + IntSize mSourceSize; + + nsCOMPtr<nsIEventTarget> mMainThreadEventTarget; + void* mThread; }; -class CreateImageBitmapFromBlobTask final : public Runnable, - public CreateImageBitmapFromBlob +NS_IMPL_ISUPPORTS_INHERITED(CreateImageBitmapFromBlob, CancelableRunnable, + imgIContainerCallback) + +class CreateImageBitmapFromBlobRunnable : public WorkerRunnable { public: - CreateImageBitmapFromBlobTask(Promise* aPromise, - nsIGlobalObject* aGlobal, - Blob& aBlob, - const Maybe<IntRect>& aCropRect) - : Runnable("dom::CreateImageBitmapFromBlobTask") - , CreateImageBitmapFromBlob(aPromise, aGlobal, aBlob, aCropRect) + explicit CreateImageBitmapFromBlobRunnable(WorkerPrivate* aWorkerPrivate, + CreateImageBitmapFromBlob* aTask, + layers::Image* aImage, + nsresult aStatus) + : WorkerRunnable(aWorkerPrivate) + , mTask(aTask) + , mImage(aImage) + , mStatus(aStatus) + {} + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { - } - - NS_IMETHOD Run() override - { - DoCreateImageBitmapFromBlob(); - return NS_OK; + mTask->DecodeAndCropBlobCompletedOwningThread(mImage, mStatus); + return true; } private: - already_AddRefed<ImageBitmap> CreateImageBitmap() override - { - // _sourceSize_ is used to get the original size of the source image, - // before being cropped. - IntSize sourceSize; - - // Keep the orignal cropping rectangle because the mCropRect might be - // modified in DecodeAndCropBlob(). - Maybe<IntRect> originalCropRect = mCropRect; - - RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect, sourceSize); - - if (NS_WARN_IF(!data)) { - mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; - } - - // Create ImageBitmap object. - RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data); - - // Set mIsCroppingAreaOutSideOfSourceImage. - imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect); - - return imageBitmap.forget(); - } + RefPtr<CreateImageBitmapFromBlob> mTask; + RefPtr<layers::Image> mImage; + nsresult mStatus; }; -class CreateImageBitmapFromBlobWorkerTask final : public WorkerSameThreadRunnable, - public CreateImageBitmapFromBlob +// This class keeps the worker alive and it informs CreateImageBitmapFromBlob +// when it goes away. +class CreateImageBitmapFromBlobHolder final : public WorkerHolder { - // This is a synchronous task. - class DecodeBlobInMainThreadSyncTask final : public WorkerMainThreadRunnable - { - public: - DecodeBlobInMainThreadSyncTask(WorkerPrivate* aWorkerPrivate, - Blob& aBlob, - Maybe<IntRect>& aCropRect, - layers::Image** aImage, - IntSize& aSourceSize) - : WorkerMainThreadRunnable(aWorkerPrivate, - NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Blob")) - , mBlob(aBlob) - , mCropRect(aCropRect) - , mImage(aImage) - , mSourceSize(aSourceSize) - { - } - - bool MainThreadRun() override - { - RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect, mSourceSize); +public: + CreateImageBitmapFromBlobHolder(WorkerPrivate* aWorkerPrivate, + CreateImageBitmapFromBlob* aTask) + : WorkerHolder("CreateImageBitmapFromBlobHolder") + , mWorkerPrivate(aWorkerPrivate) + , mTask(aTask) + , mNotified(false) + {} - if (NS_WARN_IF(!image)) { - return true; - } - - image.forget(mImage); - - return true; + bool Notify(Status aStatus) override + { + if (!mNotified) { + mNotified = true; + mTask->WorkerShuttingDown(); } - - private: - Blob& mBlob; - Maybe<IntRect>& mCropRect; - layers::Image** mImage; - IntSize mSourceSize; - }; - -public: - CreateImageBitmapFromBlobWorkerTask(Promise* aPromise, - nsIGlobalObject* aGlobal, - mozilla::dom::Blob& aBlob, - const Maybe<IntRect>& aCropRect) - : WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()), - CreateImageBitmapFromBlob(aPromise, aGlobal, aBlob, aCropRect) - { + return true; } - bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + WorkerPrivate* GetWorkerPrivate() const { - return DoCreateImageBitmapFromBlob(); + return mWorkerPrivate; } private: - already_AddRefed<ImageBitmap> CreateImageBitmap() override - { - // _sourceSize_ is used to get the original size of the source image, - // before being cropped. - IntSize sourceSize; - - // Keep the orignal cropping rectangle because the mCropRect might be - // modified in DecodeAndCropBlob(). - Maybe<IntRect> originalCropRect = mCropRect; - - RefPtr<layers::Image> data; - - ErrorResult rv; - RefPtr<DecodeBlobInMainThreadSyncTask> task = - new DecodeBlobInMainThreadSyncTask(mWorkerPrivate, *mBlob, mCropRect, - getter_AddRefs(data), sourceSize); - task->Dispatch(Terminating, rv); // This is a synchronous call. - - // In case the worker is terminating, this rejection can be handled. - if (NS_WARN_IF(rv.Failed())) { - mPromise->MaybeReject(rv); - return nullptr; - } - - if (NS_WARN_IF(!data)) { - mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; - } - - // Create ImageBitmap object. - RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data); - - // Set mIsCroppingAreaOutSideOfSourceImage. - imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect); - - return imageBitmap.forget(); - } - + WorkerPrivate* mWorkerPrivate; + RefPtr<CreateImageBitmapFromBlob> mTask; + bool mNotified; }; static void AsyncCreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal, Blob& aBlob, const Maybe<IntRect>& aCropRect) { + // Let's identify the main-thread event target. + nsCOMPtr<nsIEventTarget> mainThreadEventTarget; if (NS_IsMainThread()) { - nsCOMPtr<nsIRunnable> task = - new CreateImageBitmapFromBlobTask(aPromise, aGlobal, aBlob, aCropRect); - NS_DispatchToCurrentThread(task); // Actually, to the main-thread. + mainThreadEventTarget = aGlobal->EventTargetFor(TaskCategory::Other); } else { - RefPtr<CreateImageBitmapFromBlobWorkerTask> task = - new CreateImageBitmapFromBlobWorkerTask(aPromise, aGlobal, aBlob, aCropRect); - task->Dispatch(); // Actually, to the current worker-thread. + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + mainThreadEventTarget = workerPrivate->MainThreadEventTarget(); } + + RefPtr<CreateImageBitmapFromBlob> task = + CreateImageBitmapFromBlob::Create(aPromise, aGlobal, aBlob, aCropRect, + mainThreadEventTarget); + if (NS_WARN_IF(!task)) { + aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + NS_DispatchToCurrentThread(task); } /* static */ already_AddRefed<Promise> ImageBitmap::Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc, const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv) { MOZ_ASSERT(aGlobal); @@ -2138,10 +2017,250 @@ ImageBitmap::GetAllocatedSize() const } size_t BindingJSObjectMallocBytes(ImageBitmap* aBitmap) { return aBitmap->GetAllocatedSize(); } +/* static */ already_AddRefed<CreateImageBitmapFromBlob> +CreateImageBitmapFromBlob::Create(Promise* aPromise, + nsIGlobalObject* aGlobal, + Blob& aBlob, + const Maybe<IntRect>& aCropRect, + nsIEventTarget* aMainThreadEventTarget) +{ + // Get the internal stream of the blob. + nsCOMPtr<nsIInputStream> stream; + ErrorResult error; + aBlob.Impl()->CreateInputStream(getter_AddRefs(stream), error); + if (NS_WARN_IF(error.Failed())) { + return nullptr; + } + + // Get the MIME type string of the blob. + // The type will be checked in the DecodeImageAsync() method. + nsAutoString mimeTypeUTF16; + aBlob.Impl()->GetType(mimeTypeUTF16); + NS_ConvertUTF16toUTF8 mimeType(mimeTypeUTF16); + + RefPtr<CreateImageBitmapFromBlob> task = + new CreateImageBitmapFromBlob(aPromise, aGlobal, stream.forget(), mimeType, + aCropRect, aMainThreadEventTarget); + + // Nothing to do for the main-thread. + if (NS_IsMainThread()) { + return task.forget(); + } + + // Let's use a WorkerHolder to keep the worker alive if this is not the + // main-thread. + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + UniquePtr<CreateImageBitmapFromBlobHolder> holder( + new CreateImageBitmapFromBlobHolder(workerPrivate, task)); + + if (!holder->HoldWorker(workerPrivate, Terminating)) { + return nullptr; + } + + task->mWorkerHolder = Move(holder); + return task.forget(); +} + +nsresult +CreateImageBitmapFromBlob::StartDecodeAndCropBlob() +{ + MOZ_ASSERT(IsCurrentThread()); + + // Workers. + if (!NS_IsMainThread()) { + RefPtr<CreateImageBitmapFromBlob> self = this; + nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( + "CreateImageBitmapFromBlob::DecodeAndCropBlob", + [self]() { + nsresult rv = self->DecodeAndCropBlob(); + if (NS_WARN_IF(NS_FAILED(rv))) { + self->DecodeAndCropBlobCompletedMainThread(nullptr, rv); + } + }); + + return mMainThreadEventTarget->Dispatch(r.forget()); + } + + // Main-thread. + return DecodeAndCropBlob(); +} + +nsresult +CreateImageBitmapFromBlob::DecodeAndCropBlob() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // Get the Component object. + nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID); + if (NS_WARN_IF(!imgtool)) { + return NS_ERROR_FAILURE; + } + + // Decode image. + nsCOMPtr<imgIContainer> imgContainer; + nsresult rv = imgtool->DecodeImageAsync(mInputStream, mMimeType, this, + mMainThreadEventTarget); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +NS_IMETHODIMP +CreateImageBitmapFromBlob::OnImageReady(imgIContainer* aImgContainer, + nsresult aStatus) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (NS_FAILED(aStatus)) { + DecodeAndCropBlobCompletedMainThread(nullptr, aStatus); + return NS_OK; + } + + MOZ_ASSERT(aImgContainer); + + // Get the surface out. + uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_WANT_DATA_SURFACE; + uint32_t whichFrame = imgIContainer::FRAME_FIRST; + RefPtr<SourceSurface> surface = aImgContainer->GetFrame(whichFrame, frameFlags); + + if (NS_WARN_IF(!surface)) { + DecodeAndCropBlobCompletedMainThread(nullptr, NS_ERROR_FAILURE); + return NS_OK; + } + + // Store the sourceSize value for the DecodeAndCropBlobCompletedMainThread call. + mSourceSize = surface->GetSize(); + + // Crop the source surface if needed. + RefPtr<SourceSurface> croppedSurface = surface; + + if (mCropRect.isSome()) { + // The blob is just decoded into a RasterImage and not optimized yet, so the + // _surface_ we get is a DataSourceSurface which wraps the RasterImage's + // raw buffer. + // + // The _surface_ might already be optimized so that its type is not + // SurfaceType::DATA. However, we could keep using the generic cropping and + // copying since the decoded buffer is only used in this ImageBitmap so we + // should crop it to save memory usage. + // + // TODO: Bug1189632 is going to refactor this create-from-blob part to + // decode the blob off the main thread. Re-check if we should do + // cropping at this moment again there. + RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); + croppedSurface = CropAndCopyDataSourceSurface(dataSurface, mCropRect.ref()); + mCropRect->MoveTo(0, 0); + } + + if (NS_WARN_IF(!croppedSurface)) { + DecodeAndCropBlobCompletedMainThread(nullptr, NS_ERROR_FAILURE); + return NS_OK; + } + + // Create an Image from the source surface. + RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface); + + if (NS_WARN_IF(!image)) { + DecodeAndCropBlobCompletedMainThread(nullptr, NS_ERROR_FAILURE); + return NS_OK; + } + + DecodeAndCropBlobCompletedMainThread(image, NS_OK); + return NS_OK; +} + +void +CreateImageBitmapFromBlob::DecodeAndCropBlobCompletedMainThread(layers::Image* aImage, + nsresult aStatus) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!IsCurrentThread()) { + MutexAutoLock lock(mMutex); + + if (!mWorkerHolder) { + // The worker is already gone. + return; + } + + RefPtr<CreateImageBitmapFromBlobRunnable> r = + new CreateImageBitmapFromBlobRunnable(mWorkerHolder->GetWorkerPrivate(), + this, aImage, aStatus); + r->Dispatch(); + return; + } + + DecodeAndCropBlobCompletedOwningThread(aImage, aStatus); +} + +void +CreateImageBitmapFromBlob::DecodeAndCropBlobCompletedOwningThread(layers::Image* aImage, + nsresult aStatus) +{ + MOZ_ASSERT(IsCurrentThread()); + + if (!mPromise) { + // The worker is going to be released soon. No needs to continue. + return; + } + + // Let's release what has to be released on the owning thread. + auto raii = MakeScopeExit([&] { + // Doing this we also release the worker. + mWorkerHolder = nullptr; + + mPromise = nullptr; + mGlobalObject = nullptr; + }); + + if (NS_WARN_IF(NS_FAILED(aStatus))) { + mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + // Create ImageBitmap object. + RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, aImage); + + // Set mIsCroppingAreaOutSideOfSourceImage. + imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(mSourceSize, + mOriginalCropRect); + + if (mCropRect.isSome()) { + ErrorResult rv; + imageBitmap->SetPictureRect(mCropRect.ref(), rv); + + if (rv.Failed()) { + mPromise->MaybeReject(rv); + return; + } + } + + imageBitmap->mAllocatedImageData = true; + + mPromise->MaybeResolve(imageBitmap); +} + +void +CreateImageBitmapFromBlob::WorkerShuttingDown() +{ + MOZ_ASSERT(IsCurrentThread()); + + MutexAutoLock lock(mMutex); + + // Let's release all the non-thread-safe objects now. + mWorkerHolder = nullptr; + mPromise = nullptr; + mGlobalObject = nullptr; +} + } // namespace dom } // namespace mozilla
new file mode 100644 index 0000000000000000000000000000000000000000..48c454d27c9c46574597f80fdd64ecd4d254577d GIT binary patch literal 361 zc$}S%Jr2S!427SY62(c{IxR3UaS;ZjN^lMi!+jW87&rt!3pWYQNSWZXWLZz|+0M88 zg6zlL2%-urE#xP*Fjb*+qtv!8IBRE?o%igRee9wSZD4Lv({>_BeC&r_2A!oWp^`MU z)1U+n@-z8@<Z!^j&>B@DlPF)Y&{y$^Re3H`6{;?Yrf$c@oxa@<L_>PVBy4b|?sS}5 I{)F=V4K5EK<NyEw
--- a/dom/file/ipc/tests/mochitest.ini +++ b/dom/file/ipc/tests/mochitest.ini @@ -1,6 +1,8 @@ [DEFAULT] support-files = - script_file.js [test_ipcBlob_fileReaderSync.html] +support-files = script_file.js [test_ipcBlob_workers.html] +[test_ipcBlob_createImageBitmap.html] +support-files = green.jpg
new file mode 100644 --- /dev/null +++ b/dom/file/ipc/tests/test_ipcBlob_createImageBitmap.html @@ -0,0 +1,84 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test IPCBlob and CreateImageBitmap</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function test_mainThread() { + let bc = new BroadcastChannel('testMainThread'); + bc.onmessage = e => { + createImageBitmap(e.data).then(image => { + ok(image.height, "this image has a valid size."); + }, () => { + ok(false, "error creating the image!"); + }).then(next); + } + + fetch('green.jpg').then(r => r.blob()).then(blob => { + let bc = new BroadcastChannel('testMainThread'); + bc.postMessage(blob); + }); +} + +function test_worker() { + function workerScript() { + function ok(a, msg) { postMessage({ type: 'test', status: !!a, msg }); }; + function finish() { postMessage({ type: 'finish' }); }; + + let bc = new BroadcastChannel('testWorker'); + bc.onmessage = e => { + createImageBitmap(e.data).then(image => { + ok(image.height, "this image has a valid size."); + }, () => { + ok(false, "error creating the image!"); + }).then(finish); + } + + fetch('http://mochi.test:8888/tests/dom/file/ipc/tests/green.jpg').then(r => r.blob()).then(blob => { + let bc = new BroadcastChannel('testWorker'); + bc.postMessage(blob); + }); + } + let workerUrl = URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); + let worker = new Worker(workerUrl); + + worker.onmessage = event => { + if (event.data.type == 'test') { + ok(event.data.status, event.data.msg); + return; + } + + if (event.data.type == 'finish') { + next(); + } + } +} + +let tests = [ + test_mainThread, + test_worker, +]; + +function next() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + let test = tests.shift(); + test(); +} + +next(); + +</script> +</pre> +</body> +</html>
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -82,17 +82,16 @@ #ifdef MOZ_GECKO_PROFILER #include "ChildProfilerController.h" #endif #if defined(MOZ_CONTENT_SANDBOX) #include "mozilla/SandboxSettings.h" #if defined(XP_WIN) -#define TARGET_SANDBOX_EXPORTS #include "mozilla/sandboxTarget.h" #elif defined(XP_LINUX) #include "mozilla/Sandbox.h" #include "mozilla/SandboxInfo.h" // Remove this include with Bug 1104619 #include "CubebUtils.h" #elif defined(XP_MACOSX)
--- a/dom/media/CloneableWithRangeMediaResource.cpp +++ b/dom/media/CloneableWithRangeMediaResource.cpp @@ -39,17 +39,20 @@ public: nsresult Read(char* aBuffer, uint32_t aSize, uint32_t* aRead) { uint32_t done = 0; do { uint32_t read; nsresult rv = SyncRead(aBuffer + done, aSize - done, &read); - if (NS_WARN_IF(NS_FAILED(rv)) || read == 0) { + if (NS_SUCCEEDED(rv) && read == 0) { + break; + } + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } done += read; } while(done != aSize); *aRead = done; return NS_OK; } @@ -74,17 +77,17 @@ private: nsresult SyncRead(char* aBuffer, uint32_t aSize, uint32_t* aRead) { while (1) { nsresult rv = mStream->Read(aBuffer, aSize, aRead); // All good. if (rv == NS_BASE_STREAM_CLOSED || NS_SUCCEEDED(rv)) { - return rv; + return NS_OK; } // An error. if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { return rv; } // We need to proceed async.
--- a/dom/plugins/ipc/PluginProcessChild.cpp +++ b/dom/plugins/ipc/PluginProcessChild.cpp @@ -18,17 +18,16 @@ // An undocumented CoreGraphics framework method, present in the same form // since at least OS X 10.5. extern "C" CGError CGSSetDebugOptions(int options); #endif #ifdef XP_WIN bool ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath); #if defined(MOZ_SANDBOX) -#define TARGET_SANDBOX_EXPORTS #include "mozilla/sandboxTarget.h" #endif #endif using mozilla::ipc::IOThreadChild; #ifdef OS_WIN #include "nsSetDllDirectory.h"
--- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -248,36 +248,16 @@ GetWorkerPref(const nsACString& aPref, else { result = aDefault; } } return result; } -// This fn creates a key for a SharedWorker that contains the name, script -// spec, and the serialized origin attributes: -// "name|scriptSpec^key1=val1&key2=val2&key3=val3" -void -GenerateSharedWorkerKey(const nsACString& aScriptSpec, - const nsAString& aName, - const OriginAttributes& aAttrs, - nsCString& aKey) -{ - nsAutoCString suffix; - aAttrs.CreateSuffix(suffix); - - aKey.Truncate(); - aKey.SetCapacity(aName.Length() + aScriptSpec.Length() + suffix.Length() + 2); - aKey.Append(NS_ConvertUTF16toUTF8(aName)); - aKey.Append('|'); - aKey.Append(aScriptSpec); - aKey.Append(suffix); -} - void LoadContextOptions(const char* aPrefName, void* /* aClosure */) { AssertIsOnMainThread(); RuntimeService* rts = RuntimeService::GetService(); if (!rts) { // May be shutting down, just bail. @@ -1597,26 +1577,33 @@ RuntimeService::RegisterWorker(WorkerPri else if (isServiceWorker) { domainInfo->mActiveServiceWorkers.AppendElement(aWorkerPrivate); } else { domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate); } if (isSharedWorker) { - const nsString& sharedWorkerName(aWorkerPrivate->WorkerName()); - nsAutoCString key; - GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName, - aWorkerPrivate->GetOriginAttributes(), key); - MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key)); - - SharedWorkerInfo* sharedWorkerInfo = +#ifdef DEBUG + for (const UniquePtr<SharedWorkerInfo>& data : domainInfo->mSharedWorkerInfos) { + if (data->mScriptSpec == sharedWorkerScriptSpec && + data->mName == aWorkerPrivate->WorkerName() && + // We want to be sure that the window's principal subsumes the + // SharedWorker's principal and vice versa. + data->mWorkerPrivate->GetPrincipal()->Subsumes(aWorkerPrivate->GetPrincipal()) && + aWorkerPrivate->GetPrincipal()->Subsumes(data->mWorkerPrivate->GetPrincipal())) { + MOZ_CRASH("We should not instantiate a new SharedWorker!"); + } + } +#endif + + UniquePtr<SharedWorkerInfo> sharedWorkerInfo( new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec, - sharedWorkerName); - domainInfo->mSharedWorkerInfos.Put(key, sharedWorkerInfo); + aWorkerPrivate->WorkerName())); + domainInfo->mSharedWorkerInfos.AppendElement(Move(sharedWorkerInfo)); } } // From here on out we must call UnregisterWorker if something fails! if (parent) { if (!parent->AddChildWorker(aWorkerPrivate)) { UnregisterWorker(aWorkerPrivate); return false; @@ -1666,28 +1653,21 @@ RuntimeService::RegisterWorker(WorkerPri } return true; } void RuntimeService::RemoveSharedWorker(WorkerDomainInfo* aDomainInfo, WorkerPrivate* aWorkerPrivate) { - for (auto iter = aDomainInfo->mSharedWorkerInfos.Iter(); - !iter.Done(); - iter.Next()) { - SharedWorkerInfo* data = iter.UserData(); + for (uint32_t i = 0; i < aDomainInfo->mSharedWorkerInfos.Length(); ++i) { + const UniquePtr<SharedWorkerInfo>& data = + aDomainInfo->mSharedWorkerInfos[i]; if (data->mWorkerPrivate == aWorkerPrivate) { -#ifdef DEBUG - nsAutoCString key; - GenerateSharedWorkerKey(data->mScriptSpec, data->mName, - aWorkerPrivate->GetOriginAttributes(), key); - MOZ_ASSERT(iter.Key() == key); -#endif - iter.Remove(); + aDomainInfo->mSharedWorkerInfos.RemoveElementAt(i); break; } } } void RuntimeService::UnregisterWorker(WorkerPrivate* aWorkerPrivate) { @@ -2494,31 +2474,35 @@ RuntimeService::CreateSharedWorkerFromLo AssertIsOnMainThread(); MOZ_ASSERT(aLoadInfo); MOZ_ASSERT(aLoadInfo->mResolvedScriptURI); RefPtr<WorkerPrivate> workerPrivate; { MutexAutoLock lock(mMutex); - WorkerDomainInfo* domainInfo; - SharedWorkerInfo* sharedWorkerInfo; - nsCString scriptSpec; nsresult rv = aLoadInfo->mResolvedScriptURI->GetSpec(scriptSpec); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(aLoadInfo->mPrincipal); - nsAutoCString key; - GenerateSharedWorkerKey(scriptSpec, aName, - aLoadInfo->mPrincipal->OriginAttributesRef(), key); - - if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo) && - domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) { - workerPrivate = sharedWorkerInfo->mWorkerPrivate; + + WorkerDomainInfo* domainInfo; + if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo)) { + for (const UniquePtr<SharedWorkerInfo>& data : domainInfo->mSharedWorkerInfos) { + if (data->mScriptSpec == scriptSpec && + data->mName == aName && + // We want to be sure that the window's principal subsumes the + // SharedWorker's principal and vice versa. + aLoadInfo->mPrincipal->Subsumes(data->mWorkerPrivate->GetPrincipal()) && + data->mWorkerPrivate->GetPrincipal()->Subsumes(aLoadInfo->mPrincipal)) { + workerPrivate = data->mWorkerPrivate; + break; + } + } } } // Keep a reference to the window before spawning the worker. If the worker is // a Shared/Service worker and the worker script loads and executes before // the SharedWorker object itself is created before then WorkerScriptLoaded() // will reset the loadInfo's window. nsCOMPtr<nsPIDOMWindowInner> window = aLoadInfo->mWindow;
--- a/dom/workers/RuntimeService.h +++ b/dom/workers/RuntimeService.h @@ -40,17 +40,17 @@ class RuntimeService final : public nsIO }; struct WorkerDomainInfo { nsCString mDomain; nsTArray<WorkerPrivate*> mActiveWorkers; nsTArray<WorkerPrivate*> mActiveServiceWorkers; nsTArray<WorkerPrivate*> mQueuedWorkers; - nsClassHashtable<nsCStringHashKey, SharedWorkerInfo> mSharedWorkerInfos; + nsTArray<UniquePtr<SharedWorkerInfo>> mSharedWorkerInfos; uint32_t mChildWorkerCount; WorkerDomainInfo() : mActiveWorkers(1), mChildWorkerCount(0) { } uint32_t ActiveWorkerCount() const
--- a/gfx/ipc/GPUProcessImpl.cpp +++ b/gfx/ipc/GPUProcessImpl.cpp @@ -3,17 +3,16 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GPUProcessImpl.h" #include "mozilla/ipc/IOThreadChild.h" #include "nsXPCOM.h" #if defined(OS_WIN) && defined(MOZ_SANDBOX) -#define TARGET_SANDBOX_EXPORTS #include "mozilla/sandboxTarget.h" #endif namespace mozilla { namespace gfx { using namespace ipc;
--- a/gfx/layers/client/ClientPaintedLayer.cpp +++ b/gfx/layers/client/ClientPaintedLayer.cpp @@ -61,23 +61,17 @@ ClientPaintedLayer::CanRecordLayer(Readb return false; } // Not supported yet if (aReadback && UsedForReadback()) { return false; } - // If we have mask layers, we have to render those first - // In this case, don't record for now. - if (GetMaskLayer()) { - return false; - } - - return GetAncestorMaskLayerCount() == 0; + return true; } void ClientPaintedLayer::UpdateContentClient(PaintState& aState) { Mutated(); AddToValidRegion(aState.mRegionToDraw);
--- a/image/imgITools.idl +++ b/image/imgITools.idl @@ -1,40 +1,69 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" +interface nsIEventTarget; interface nsIInputStream; interface imgIContainer; interface imgILoader; interface imgICache; interface nsIDOMDocument; interface imgIScriptedNotificationObserver; interface imgINotificationObserver; +interface imgIContainerCallback; [scriptable, builtinclass, uuid(4c2383a4-931c-484d-8c4a-973590f66e3f)] interface imgITools : nsISupports { /** * decodeImage - * Caller provides an input stream and mimetype. We read from the stream - * and decompress it (according to the specified mime type) and return - * the resulting imgIContainer. + * decodeImageBuffer + * Caller provides an buffer, a buffer size and a mimetype. We read from + * the stream and decompress it (according to the specified mime type) and + * return the resulting imgIContainer. + * + * @param aStream + * An input stream for an encoded image file. + * @param aBuffer + * Data in memory. + * @param aSize + * Buffer size. + * @param aMimeType + * Type of image in the stream. + */ + imgIContainer decodeImageBuffer(in string aBuffer, + in unsigned long aSize, + in ACString aMimeType); + + /** + * decodeImageAsync + * See decodeImage. The main difference between this method and decodeImage + * is that here the operation is done async on a thread from the decode + * pool. When the operation is completed, the callback is executed with the + * result. * * @param aStream * An input stream for an encoded image file. * @param aMimeType * Type of image in the stream. + * @param aCallback + * The callback is executed when the imgContainer is fully created. + * @param aEventTarget + * This eventTarget is used to execute aCallback */ - imgIContainer decodeImage(in nsIInputStream aStream, - in ACString aMimeType); + void decodeImageAsync(in nsIInputStream aStream, + in ACString aMimeType, + in imgIContainerCallback aCallback, + in nsIEventTarget aEventTarget); /** * encodeImage * Caller provides an image container, and the mime type it should be * encoded to. We return an input stream for the encoded image data. * * @param aContainer * An image container. @@ -123,8 +152,18 @@ interface imgITools : nsISupports * Create a wrapper around a scripted notification observer (ordinarily * imgINotificationObserver cannot be implemented from scripts). * * @param aObserver The scripted observer to wrap */ imgINotificationObserver createScriptedObserver(in imgIScriptedNotificationObserver aObserver); }; + +/** + * This is a companion interface for nsIAsyncInputStream::asyncWait. + */ +[function, scriptable, uuid(f195772c-a4c0-47ae-80ca-211e001c67be)] +interface imgIContainerCallback : nsISupports +{ + /* If the operation fails, aStatus will contain the error value */ + void onImageReady(in imgIContainer aImage, in nsresult aStatus); +};
--- a/image/imgTools.cpp +++ b/image/imgTools.cpp @@ -1,40 +1,174 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "imgTools.h" +#include "DecodePool.h" #include "gfxUtils.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Logging.h" #include "mozilla/RefPtr.h" #include "nsCOMPtr.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsError.h" #include "imgLoader.h" #include "imgICache.h" #include "imgIContainer.h" #include "imgIEncoder.h" #include "nsStreamUtils.h" +#include "nsStringStream.h" #include "nsContentUtils.h" +#include "nsProxyRelease.h" #include "ImageFactory.h" #include "Image.h" #include "ScriptedNotificationObserver.h" #include "imgIScriptedNotificationObserver.h" #include "gfxPlatform.h" using namespace mozilla::gfx; namespace mozilla { namespace image { + +namespace { + +class ImageDecoderHelper final : public Runnable + , public nsIInputStreamCallback +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + ImageDecoderHelper(already_AddRefed<image::Image> aImage, + already_AddRefed<nsIInputStream> aInputStream, + nsIEventTarget* aEventTarget, + imgIContainerCallback* aCallback, + nsIEventTarget* aCallbackEventTarget) + : Runnable("ImageDecoderHelper") + , mImage(Move(aImage)) + , mInputStream(Move(aInputStream)) + , mEventTarget(aEventTarget) + , mCallback(aCallback) + , mCallbackEventTarget(aCallbackEventTarget) + , mStatus(NS_OK) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + NS_IMETHOD + Run() override + { + // This runnable is dispatched on the Image thread when reading data, but + // at the end, it goes back to the main-thread in order to complete the + // operation. + if (NS_IsMainThread()) { + // Let the Image know we've sent all the data. + mImage->OnImageDataComplete(nullptr, nullptr, mStatus, true); + + RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker(); + tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); + + nsCOMPtr<imgIContainer> container; + if (NS_SUCCEEDED(mStatus)) { + container = do_QueryInterface(mImage); + } + + mCallback->OnImageReady(container, mStatus); + return NS_OK; + } + + uint64_t length; + nsresult rv = mInputStream->Available(&length); + if (rv == NS_BASE_STREAM_CLOSED) { + return OperationCompleted(NS_OK); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return OperationCompleted(rv); + } + + // Nothing else to read, but maybe we just need to wait. + if (length == 0) { + nsCOMPtr<nsIAsyncInputStream> asyncInputStream = + do_QueryInterface(mInputStream); + if (asyncInputStream) { + rv = asyncInputStream->AsyncWait(this, 0, 0, mEventTarget); + if (NS_WARN_IF(NS_FAILED(rv))) { + return OperationCompleted(rv); + } + return NS_OK; + } + + // We really have nothing else to read. + if (length == 0) { + return OperationCompleted(NS_OK); + } + } + + // Send the source data to the Image. + rv = mImage->OnImageDataAvailable(nullptr, nullptr, mInputStream, 0, + uint32_t(length)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return OperationCompleted(rv); + } + + rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return OperationCompleted(rv); + } + + return NS_OK; + } + + NS_IMETHOD + OnInputStreamReady(nsIAsyncInputStream* aAsyncInputStream) override + { + MOZ_ASSERT(!NS_IsMainThread()); + return Run(); + } + + nsresult + OperationCompleted(nsresult aStatus) + { + MOZ_ASSERT(!NS_IsMainThread()); + + mStatus = aStatus; + mCallbackEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); + return NS_OK; + } + +private: + ~ImageDecoderHelper() + { + NS_ReleaseOnMainThreadSystemGroup("ImageDecoderHelper::mImage", + mImage.forget()); + NS_ReleaseOnMainThreadSystemGroup("ImageDecoderHelper::mCallback", + mCallback.forget()); + } + + RefPtr<image::Image> mImage; + + nsCOMPtr<nsIInputStream> mInputStream; + nsCOMPtr<nsIEventTarget> mEventTarget; + nsCOMPtr<imgIContainerCallback> mCallback; + nsCOMPtr<nsIEventTarget> mCallbackEventTarget; + + nsresult mStatus; +}; + +NS_IMPL_ISUPPORTS_INHERITED(ImageDecoderHelper, Runnable, + nsIInputStreamCallback) + +} // anonymous + /* ========== imgITools implementation ========== */ NS_IMPL_ISUPPORTS(imgTools, imgITools) imgTools::imgTools() { @@ -42,65 +176,103 @@ imgTools::imgTools() } imgTools::~imgTools() { /* destructor code */ } NS_IMETHODIMP -imgTools::DecodeImage(nsIInputStream* aInStr, - const nsACString& aMimeType, - imgIContainer** aContainer) +imgTools::DecodeImageBuffer(const char* aBuffer, uint32_t aSize, + const nsACString& aMimeType, + imgIContainer** aContainer) { MOZ_ASSERT(NS_IsMainThread()); - nsresult rv; - - NS_ENSURE_ARG_POINTER(aInStr); - - // Prepare the input stream. - nsCOMPtr<nsIInputStream> inStream = aInStr; - if (!NS_InputStreamIsBuffered(aInStr)) { - nsCOMPtr<nsIInputStream> bufStream; - rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), - inStream.forget(), 1024); - if (NS_SUCCEEDED(rv)) { - inStream = bufStream; - } - } - - // Figure out how much data we've been passed. - uint64_t length; - rv = inStream->Available(&length); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); + NS_ENSURE_ARG_POINTER(aBuffer); // Create a new image container to hold the decoded data. nsAutoCString mimeType(aMimeType); RefPtr<image::Image> image = - ImageFactory::CreateAnonymousImage(mimeType, uint32_t(length)); + ImageFactory::CreateAnonymousImage(mimeType, aSize); RefPtr<ProgressTracker> tracker = image->GetProgressTracker(); if (image->HasError()) { return NS_ERROR_FAILURE; } - // Send the source data to the Image. - rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0, - uint32_t(length)); + // Let's create a temporary inputStream. + nsCOMPtr<nsIInputStream> stream; + nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), + aBuffer, aSize, + NS_ASSIGNMENT_DEPEND); + NS_ENSURE_SUCCESS(rv, rv); + MOZ_ASSERT(stream); + MOZ_ASSERT(NS_InputStreamIsBuffered(stream)); + + rv = image->OnImageDataAvailable(nullptr, nullptr, stream, 0, + aSize); NS_ENSURE_SUCCESS(rv, rv); // Let the Image know we've sent all the data. rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true); tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); NS_ENSURE_SUCCESS(rv, rv); // All done. - NS_ADDREF(*aContainer = image.get()); + image.forget(aContainer); + return NS_OK; +} + +NS_IMETHODIMP +imgTools::DecodeImageAsync(nsIInputStream* aInStr, + const nsACString& aMimeType, + imgIContainerCallback* aCallback, + nsIEventTarget* aEventTarget) +{ + MOZ_ASSERT(NS_IsMainThread()); + + NS_ENSURE_ARG_POINTER(aInStr); + NS_ENSURE_ARG_POINTER(aCallback); + NS_ENSURE_ARG_POINTER(aEventTarget); + + nsresult rv; + + // Let's continuing the reading on a separate thread. + DecodePool* decodePool = DecodePool::Singleton(); + MOZ_ASSERT(decodePool); + + RefPtr<nsIEventTarget> target = decodePool->GetIOEventTarget(); + NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); + + // Prepare the input stream. + nsCOMPtr<nsIInputStream> stream = aInStr; + if (!NS_InputStreamIsBuffered(aInStr)) { + nsCOMPtr<nsIInputStream> bufStream; + rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), + stream.forget(), 1024); + NS_ENSURE_SUCCESS(rv, rv); + stream = bufStream.forget(); + } + + // Create a new image container to hold the decoded data. + nsAutoCString mimeType(aMimeType); + RefPtr<image::Image> image = ImageFactory::CreateAnonymousImage(mimeType, 0); + + // Already an error? + if (image->HasError()) { + return NS_ERROR_FAILURE; + } + + RefPtr<ImageDecoderHelper> helper = + new ImageDecoderHelper(image.forget(), stream.forget(), target, aCallback, + aEventTarget); + rv = target->Dispatch(helper.forget(), NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; } /** * This takes a DataSourceSurface rather than a SourceSurface because some * of the callers have a DataSourceSurface and we don't want to call * GetDataSurface on such surfaces since that may incure a conversion to * SurfaceType::DATA which we don't need.
--- a/image/test/unit/test_imgtools.js +++ b/image/test/unit/test_imgtools.js @@ -1,15 +1,17 @@ /* * Tests for imgITools */ var Ci = Components.interfaces; var Cc = Components.classes; +var Cu = Components.utils; +Cu.import("resource://gre/modules/NetUtil.jsm"); /* * dumpToFile() * * For test development, dumps the specified array to a file. * Call |dumpToFile(outData);| in a test to file to a file. */ function dumpToFile(aData) { @@ -140,17 +142,18 @@ testdesc = "test decoding a PNG"; // 64x64 png, 8415 bytes. var imgName = "image1.png"; var inMimeType = "image/png"; var imgFile = do_get_file(imgName); var istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 8415); -var container = imgTools.decodeImage(istream, inMimeType); +var buffer = NetUtil.readInputStreamToString(istream, istream.available()); +var container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType); // It's not easy to look at the pixel values from JS, so just // check the container's size. do_check_eq(container.width, 64); do_check_eq(container.height, 64); /* ========== 2 ========== */ @@ -198,17 +201,18 @@ testdesc = "test decoding a JPEG"; // 32x32 jpeg, 3494 bytes. imgName = "image2.jpg"; inMimeType = "image/jpeg"; imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 3494); -container = imgTools.decodeImage(istream, inMimeType); +buffer = NetUtil.readInputStreamToString(istream, istream.available()); +container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType); // It's not easy to look at the pixel values from JS, so just // check the container's size. do_check_eq(container.width, 32); do_check_eq(container.height, 32); /* ========== 5 ========== */ @@ -260,17 +264,18 @@ testdesc = "test decoding a ICO"; // 16x16 ico, 1406 bytes. imgName = "image3.ico"; inMimeType = "image/x-icon"; imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 1406); -container = imgTools.decodeImage(istream, inMimeType); +buffer = NetUtil.readInputStreamToString(istream, istream.available()); +container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType); // It's not easy to look at the pixel values from JS, so just // check the container's size. do_check_eq(container.width, 16); do_check_eq(container.height, 16); /* ========== 8 ========== */ @@ -318,17 +323,18 @@ testdesc = "test decoding a GIF"; // 32x32 gif, 1809 bytes. imgName = "image4.gif"; inMimeType = "image/gif"; imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 1809); -container = imgTools.decodeImage(istream, inMimeType); +buffer = NetUtil.readInputStreamToString(istream, istream.available()); +container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType); // It's not easy to look at the pixel values from JS, so just // check the container's size. do_check_eq(container.width, 32); do_check_eq(container.height, 32); /* ========== 11 ========== */ testnum++; @@ -426,17 +432,18 @@ testdesc = "test cropping a JPG"; // 32x32 jpeg, 3494 bytes. imgName = "image2.jpg"; inMimeType = "image/jpeg"; imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 3494); -container = imgTools.decodeImage(istream, inMimeType); +buffer = NetUtil.readInputStreamToString(istream, istream.available()); +container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType); // It's not easy to look at the pixel values from JS, so just // check the container's size. do_check_eq(container.width, 32); do_check_eq(container.height, 32); // encode a cropped image istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 16, 16); @@ -644,17 +651,19 @@ for(var i=0; i<testData.length; ++i) { var imgFile = do_get_file(dict["refImage"]); var istream = getFileInputStream(imgFile); var refBytes = streamToArray(istream); imgFile = do_get_file(dict["preImage"]); istream = getFileInputStream(imgFile); - var container = imgTools.decodeImage(istream, dict["preImageMimeType"]); + var buffer = NetUtil.readInputStreamToString(istream, istream.available()); + var container = imgTools.decodeImageBuffer(buffer, buffer.length, + dict["preImageMimeType"]); istream = imgTools.encodeImage(container, dict["refImageMimeType"]); var sstream = Cc["@mozilla.org/storagestream;1"]. createInstance(Ci.nsIStorageStream); sstream.init(4096, 4294967295, null); var ostream = sstream.getOutputStream(0); var bostream = Cc["@mozilla.org/network/buffered-output-stream;1"]. @@ -680,22 +689,24 @@ imgName = "bug413512.ico"; inMimeType = "image/x-icon"; imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 17759); var errsrc = "none"; try { - container = imgTools.decodeImage(istream, inMimeType); + buffer = NetUtil.readInputStreamToString(istream, istream.available()); + container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType); // We expect to hit an error during encoding because the ICO header of the - // image is fine, but the actual resources are corrupt. Since decodeImage() - // only performs a metadata decode, it doesn't decode far enough to realize - // this, but we'll find out when we do a full decode during encodeImage(). + // image is fine, but the actual resources are corrupt. Since + // decodeImageBuffer() only performs a metadata decode, it doesn't decode far + // enough to realize this, but we'll find out when we do a full decode during + // encodeImage(). try { istream = imgTools.encodeImage(container, "image/png"); } catch (e) { err = e; errsrc = "encode"; } } catch (e) { err = e; @@ -712,17 +723,18 @@ testdesc = "test correct ico hotspots (b imgName = "bug815359.ico"; inMimeType = "image/x-icon"; imgFile = do_get_file(imgName); istream = getFileInputStream(imgFile); do_check_eq(istream.available(), 4286); -container = imgTools.decodeImage(istream, inMimeType); +buffer = NetUtil.readInputStreamToString(istream, istream.available()); +container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType); var props = container.QueryInterface(Ci.nsIProperties); do_check_eq(props.get("hotspotX", Ci.nsISupportsPRUint32).data, 10); do_check_eq(props.get("hotspotY", Ci.nsISupportsPRUint32).data, 9); /* ========== end ========== */
--- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -19,17 +19,16 @@ #include "mozilla/ipc/MessageChannel.h" #include "mozilla/ipc/Transport.h" #include "mozilla/StaticMutex.h" #include "mozilla/SystemGroup.h" #include "mozilla/Unused.h" #include "nsPrintfCString.h" #if defined(MOZ_SANDBOX) && defined(XP_WIN) -#define TARGET_SANDBOX_EXPORTS #include "mozilla/sandboxTarget.h" #endif #if defined(XP_WIN) #include "aclapi.h" #include "sddl.h" #include "mozilla/TypeTraits.h"
--- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -9310,16 +9310,22 @@ nsTextFrame::ReflowText(nsLineLayout& aL // Make sure this frame maps the trimmable whitespace. if (MOZ_UNLIKELY(offset > GetContentEnd())) { SetLength(offset - GetContentOffset(), &aLineLayout, ALLOW_FRAME_CREATION_AND_DESTRUCTION); } } } + // If trimming whitespace left us with nothing to do, return early. + if (length == 0) { + ClearMetrics(aMetrics); + return; + } + bool completedFirstLetter = false; // Layout dependent styles are a problem because we need to reconstruct // the gfxTextRun based on our layout. if (aLineLayout.GetInFirstLetter() || aLineLayout.GetInFirstLine()) { SetLength(maxContentLength, &aLineLayout, ALLOW_FRAME_CREATION_AND_DESTRUCTION); if (aLineLayout.GetInFirstLetter()) { @@ -9798,22 +9804,31 @@ nsTextFrame::CanContinueTextRun() const { // We can continue a text run through a text frame return true; } nsTextFrame::TrimOutput nsTextFrame::TrimTrailingWhiteSpace(DrawTarget* aDrawTarget) { + MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW), + "frame should have been reflowed"); + TrimOutput result; result.mChanged = false; result.mDeltaWidth = 0; AddStateBits(TEXT_END_OF_LINE); + if (!GetTextRun(nsTextFrame::eInflated)) { + // If reflow didn't create a textrun, there must have been no content once + // leading whitespace was trimmed, so nothing more to do here. + return result; + } + int32_t contentLength = GetContentLength(); if (!contentLength) return result; gfxSkipCharsIterator start = EnsureTextRun(nsTextFrame::eInflated, aDrawTarget); NS_ENSURE_TRUE(mTextRun, result);
--- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -1029,16 +1029,17 @@ void ServoStyleSet::RecordStyleSheetChange( ServoStyleSheet* aSheet, StyleSheet::ChangeType aChangeType) { switch (aChangeType) { case StyleSheet::ChangeType::RuleAdded: case StyleSheet::ChangeType::RuleRemoved: case StyleSheet::ChangeType::RuleChanged: + case StyleSheet::ChangeType::ReparsedFromInspector: // FIXME(emilio): We can presumably do better in a bunch of these. return MarkOriginsDirty(aSheet->GetOrigin()); case StyleSheet::ChangeType::ApplicableStateChanged: case StyleSheet::ChangeType::Added: case StyleSheet::ChangeType::Removed: // Do nothing, we've already recorded the change in the // Append/Remove/Replace methods, etc, and will act consequently. return;
--- a/layout/style/ServoStyleSheet.cpp +++ b/layout/style/ServoStyleSheet.cpp @@ -4,19 +4,20 @@ * 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 "mozilla/ServoStyleSheet.h" #include "mozilla/css/Rule.h" #include "mozilla/StyleBackendType.h" #include "mozilla/ServoBindings.h" +#include "mozilla/ServoCSSRuleList.h" #include "mozilla/ServoImportRule.h" #include "mozilla/ServoMediaList.h" -#include "mozilla/ServoCSSRuleList.h" +#include "mozilla/ServoStyleSet.h" #include "mozilla/css/GroupRule.h" #include "mozilla/dom/CSSRuleList.h" #include "mozilla/dom/MediaList.h" #include "nsIStyleSheetLinkingElement.h" #include "Loader.h" #include "mozAutoDocUpdate.h" @@ -337,16 +338,29 @@ ServoStyleSheet::ReparseSheet(const nsAS // Document observers could possibly detach document from this sheet. if (!mDocument) { // If detached, don't process any more rules. break; } } } + // FIXME(emilio): This is kind-of a hack for bug 1420713. As you may notice, + // there's nothing that triggers a style flush or anything similar (neither + // here or in the relevant Gecko path inside DidDirty). + // + // The tl;dr is: if we want to make sure scripted changes to sheets not + // associated with any document get properly reflected, we need to rejigger a + // fair amount of stuff. I'm probably doing that work as part of the shadow + // DOM stuff. + for (StyleSetHandle handle : mStyleSets) { + handle->AsServo()->RecordStyleSheetChange( + this, StyleSheet::ChangeType::ReparsedFromInspector); + } + return NS_OK; } // nsICSSLoaderObserver implementation NS_IMETHODIMP ServoStyleSheet::StyleSheetLoaded(StyleSheet* aSheet, bool aWasAlternate, nsresult aStatus)
--- a/layout/style/StyleSheet.cpp +++ b/layout/style/StyleSheet.cpp @@ -866,16 +866,19 @@ StyleSheet::List(FILE* out, int32_t aInd child->List(out, aIndent + 1); } } #endif void StyleSheet::SetMedia(dom::MediaList* aMedia) { + if (aMedia) { + aMedia->SetStyleSheet(this); + } mMedia = aMedia; } void StyleSheet::DropMedia() { if (mMedia) { mMedia->SetStyleSheet(nullptr);
--- a/layout/style/StyleSheet.h +++ b/layout/style/StyleSheet.h @@ -72,16 +72,17 @@ public: */ enum class ChangeType { Added, Removed, ApplicableStateChanged, RuleAdded, RuleRemoved, RuleChanged, + ReparsedFromInspector, }; void SetOwningNode(nsINode* aOwningNode) { mOwningNode = aOwningNode; } css::SheetParsingMode ParsingMode() const { return mParsingMode; }
--- a/taskcluster/taskgraph/util/verify.py +++ b/taskcluster/taskgraph/util/verify.py @@ -138,18 +138,24 @@ def verify_dependency_tiers(task, taskgr .get('tier', sys.maxint) else: def printable_tier(tier): if tier == sys.maxint: return 'unknown' return tier for task in taskgraph.tasks.itervalues(): + # Buildbot bridge tasks cannot have tiers, so we cannot enforce + # this check for them + if task.task.get("workerType") == "buildbot-bridge": + continue tier = tiers[task.label] for d in task.dependencies.itervalues(): + if taskgraph[d].task.get("workerType") == "buildbot-bridge": + continue if tier < tiers[d]: raise Exception( '{} (tier {}) cannot depend on {} (tier {})' .format(task.label, printable_tier(tier), d, printable_tier(tiers[d]))) @verifications.add('optimized_task_graph')
--- a/testing/config/tooltool-manifests/macosx64/hostutils.manifest +++ b/testing/config/tooltool-manifests/macosx64/hostutils.manifest @@ -1,10 +1,10 @@ [ { - "size": 121025970, + "size": 72112596, "visibility": "public", - "digest": "1374f461ad90fcdf1f6859df56379f000c7dbb0f8ea54c6652472c9e6f25052ec2386d00d6a41ed55579060e9300886674363f9689b1391e44518e500b9f2681", + "digest": "4d3fe61dd8ad9ca09c72e677e8d2d5b7409988de32d6feb9fc55cb0a320a4835b6e2f384a68d2409e89e7a0d2f622bf3ceb9b707e01ad8afe1270ad0d1c9973c", "algorithm": "sha512", - "filename": "host-utils-50.0a2.en-US.mac.tar.gz", + "filename": "host-utils-58.0a1.en-US.mac.tar.gz", "unpack": true } ]
--- a/testing/mozharness/scripts/android_emulator_unittest.py +++ b/testing/mozharness/scripts/android_emulator_unittest.py @@ -195,16 +195,25 @@ class AndroidEmulatorTest(BlobUploadMixi # constructed in start_emulator. env['LD_LIBRARY_PATH'] = self.abs_dirs['abs_work_dir'] # Write a default ddms.cfg to avoid unwanted prompts avd_home_dir = self.abs_dirs['abs_avds_dir'] with open(os.path.join(avd_home_dir, "ddms.cfg"), 'w') as f: f.write("pingOptIn=false\npingId=0\n") + # Delete emulator auth file, so it doesn't prompt + AUTH_FILE = os.path.join(os.path.expanduser('~'), '.emulator_console_auth_token') + if os.path.exists(AUTH_FILE): + try: + os.remove(AUTH_FILE) + self.info("deleted %s" % AUTH_FILE) + except: + self.warning("failed to remove %s" % AUTH_FILE) + # Set environment variables to help emulator find the AVD. # In newer versions of the emulator, ANDROID_AVD_HOME should # point to the 'avd' directory. # For older versions of the emulator, ANDROID_SDK_HOME should # point to the directory containing the '.android' directory # containing the 'avd' directory. env['ANDROID_AVD_HOME'] = os.path.join(avd_home_dir, 'avd') env['ANDROID_SDK_HOME'] = os.path.abspath(os.path.join(avd_home_dir, '..')) @@ -254,39 +263,39 @@ class AndroidEmulatorTest(BlobUploadMixi return status def _run_with_timeout(self, timeout, cmd, quiet=False): timeout_cmd = ['timeout', '%s' % timeout] + cmd return self._run_proc(timeout_cmd, quiet=quiet) def _run_proc(self, cmd, quiet=False): self.info('Running %s' % subprocess.list2cmdline(cmd)) - p = subprocess.Popen(cmd, stdout=subprocess.PIPE) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if out and not quiet: self.info('%s' % str(out.strip())) if err and not quiet: self.info('stderr: %s' % str(err.strip())) - return out + return out, err def _verify_adb(self): self.info('Verifying adb connectivity') self._run_with_timeout(180, [self.adb_path, 'wait-for-device']) return True def _verify_adb_device(self): - out = self._run_with_timeout(30, [self.adb_path, 'devices']) + out, _ = self._run_with_timeout(30, [self.adb_path, 'devices']) if (self.emulator['device_id'] in out) and ("device" in out): return True return False def _is_boot_completed(self): boot_cmd = [self.adb_path, '-s', self.emulator['device_id'], 'shell', 'getprop', 'sys.boot_completed'] - out = self._run_with_timeout(30, boot_cmd) + out, _ = self._run_with_timeout(30, boot_cmd) if out.strip() == '1': return True return False def _verify_emulator(self): adb_ok = self._verify_adb() if not adb_ok: self.warning('Unable to communicate with adb') @@ -322,31 +331,31 @@ class AndroidEmulatorTest(BlobUploadMixi def _install_fennec_apk(self): install_ok = False if int(self.sdk_level) >= 23: cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r', '-g', self.installer_path] else: cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r', self.installer_path] - out = self._run_with_timeout(300, cmd, True) - if 'Success' in out: + out, err = self._run_with_timeout(300, cmd, True) + if 'Success' in out or 'Success' in err: install_ok = True return install_ok def _install_robocop_apk(self): install_ok = False if int(self.sdk_level) >= 23: cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r', '-g', self.robocop_path] else: cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r', self.robocop_path] - out = self._run_with_timeout(300, cmd, True) - if 'Success' in out: + out, err = self._run_with_timeout(300, cmd, True) + if 'Success' in out or 'Success' in err: install_ok = True return install_ok def _dump_host_state(self): self._run_proc(['ps', '-ef']) self._run_proc(['netstat', '-a', '-p', '-n', '-t', '-u']) def _dump_emulator_log(self): @@ -363,17 +372,17 @@ class AndroidEmulatorTest(BlobUploadMixi for line in out.splitlines(): if process_name in line: pid = int(line.split(None, 1)[0]) self.info("Killing pid %d." % pid) os.kill(pid, signal.SIGKILL) def _restart_adbd(self): self._run_with_timeout(30, [self.adb_path, 'kill-server']) - self._run_with_timeout(30, [self.adb_path, 'start-server']) + self._run_with_timeout(30, [self.adb_path, 'root']) def _screenshot(self, prefix): """ Save a screenshot of the entire screen to the blob upload directory. """ dirs = self.query_abs_dirs() utility = os.path.join(self.xre_path, "screentopng") if not os.path.exists(utility): @@ -609,43 +618,43 @@ class AndroidEmulatorTest(BlobUploadMixi sometimes. This is hopefully a temporary diagnostic. See bug 1321605. ''' dir = self.query_abs_dirs()['abs_blob_upload_dir'] perf_path = os.path.join(dir, "android-performance.log") with open(perf_path, "w") as f: f.write('\n\nHost /proc/cpuinfo:\n') - out = self._run_proc(['cat', '/proc/cpuinfo'], quiet=True) + out, _ = self._run_proc(['cat', '/proc/cpuinfo'], quiet=True) f.write(out) f.write('\n\nHost /proc/meminfo:\n') - out = self._run_proc(['cat', '/proc/meminfo'], quiet=True) + out, _ = self._run_proc(['cat', '/proc/meminfo'], quiet=True) f.write(out) f.write('\n\nHost process list:\n') - out = self._run_proc(['ps', '-ef'], quiet=True) + out, _ = self._run_proc(['ps', '-ef'], quiet=True) f.write(out) f.write('\n\nEmulator /proc/cpuinfo:\n') cmd = [self.adb_path, '-s', self.emulator['device_id'], 'shell', 'cat', '/proc/cpuinfo'] - out = self._run_with_timeout(30, cmd, quiet=True) + out, _ = self._run_with_timeout(30, cmd, quiet=True) f.write(out) f.write('\n\nEmulator /proc/meminfo:\n') cmd = [self.adb_path, '-s', self.emulator['device_id'], 'shell', 'cat', '/proc/meminfo'] - out = self._run_with_timeout(30, cmd, quiet=True) + out, _ = self._run_with_timeout(30, cmd, quiet=True) f.write(out) f.write('\n\nEmulator process list:\n') cmd = [self.adb_path, '-s', self.emulator['device_id'], 'shell', 'ps'] - out = self._run_with_timeout(30, cmd, quiet=True) + out, _ = self._run_with_timeout(30, cmd, quiet=True) f.write(out) def verify_emulator(self): ''' Check to see if the emulator can be contacted via adb. If any communication attempt fails, kill the emulator, re-launch, and re-check. ''' self.mkdir_p(self.query_abs_dirs()['abs_blob_upload_dir']) @@ -702,19 +711,19 @@ class AndroidEmulatorTest(BlobUploadMixi self.config["suite_definitions"][self.test_suite].get("install") if install_needed is False: self.info("Skipping apk installation for %s" % self.test_suite) return assert self.installer_path is not None, \ "Either add installer_path to the config or use --installer-path." - self.sdk_level = self._run_with_timeout(30, [self.adb_path, '-s', - self.emulator['device_id'], - 'shell', 'getprop', 'ro.build.version.sdk']) + cmd = [self.adb_path, '-s', self.emulator['device_id'], 'shell', + 'getprop', 'ro.build.version.sdk'] + self.sdk_level, _ = self._run_with_timeout(30, cmd) # Install Fennec install_ok = self._retry(3, 30, self._install_fennec_apk, "Install app APK") if not install_ok: self.fatal('INFRA-ERROR: Failed to install %s on %s' % (self.installer_path, self.emulator["name"]), EXIT_STATUS_DICT[TBPL_RETRY])
--- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -155018,16 +155018,28 @@ [ "/css/cssom-view/scrollingElement-quirks-dynamic-002-ref.html", "==" ] ], {} ] ], + "css/cssom/medialist-dynamic-001.html": [ + [ + "/css/cssom/medialist-dynamic-001.html", + [ + [ + "/css/cssom/medialist-dynamic-001-ref.html", + "==" + ] + ], + {} + ] + ], "css/cssom/selectorText-modification-restyle-001.html": [ [ "/css/cssom/selectorText-modification-restyle-001.html", [ [ "/css/cssom/selectorText-modification-restyle-001-ref.html", "==" ] @@ -253158,16 +253170,21 @@ {} ] ], "css/cssom/OWNERS": [ [ {} ] ], + "css/cssom/medialist-dynamic-001-ref.html": [ + [ + {} + ] + ], "css/cssom/selectorText-modification-restyle-001-ref.html": [ [ {} ] ], "css/cssom/stylesheet-same-origin.css": [ [ {} @@ -511060,17 +511077,17 @@ "b2261ec702116c211ab5ac6fbb53698dfe60a7be", "testharness" ], "css/cssom-view/offsetTopLeftInScrollableParent.html": [ "82a6920db4d0be087e6ed621313ecf92e3e2ed68", "testharness" ], "css/cssom-view/overscrollBehavior-manual.html": [ - "ca369af991f1e1c34116f2effe0d53a806ed9df5", + "65cb71ca6b606dc8b0b527c58902566539e771b2", "manual" ], "css/cssom-view/resources/elementsFromPoint.js": [ "0c31158817d4d6f9e59df0d2ebe0e41c6ce41bb5", "support" ], "css/cssom-view/resources/iframe1.html": [ "ec93f617bdc7b0055d96c7b00ab7832cca1c1af0", @@ -511463,16 +511480,24 @@ "css/cssom/insertRule-no-index.html": [ "812f2b02d7694dd270b7a3e1ef205b99890ab216", "testharness" ], "css/cssom/interfaces.html": [ "c1dfd96239986c9c57d7b07caebbd1fc9654e0b9", "testharness" ], + "css/cssom/medialist-dynamic-001-ref.html": [ + "bdf98c994adcebff3a3434260dfe71e99c8441e1", + "support" + ], + "css/cssom/medialist-dynamic-001.html": [ + "8c62d1e6b5791b68240551c0c9cd115f4d16a892", + "reftest" + ], "css/cssom/medialist-interfaces-001.html": [ "dfaea262508d72d123006409174e3e21832a305f", "testharness" ], "css/cssom/medialist-interfaces-002.html": [ "114fac94342afe2e7fe432a67c4b0bbf03d24bc4", "testharness" ],
new file mode 100644 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/medialist-dynamic-001-ref.html @@ -0,0 +1,5 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Test Reference</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +Should not be red.
new file mode 100644 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/medialist-dynamic-001.html @@ -0,0 +1,12 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Test: Dynamic changes to the stylesheet media attributes via CSSOM get reflected</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="match" href="medialist-dynamic-001-ref.html"> +<link rel="help" href="https://drafts.csswg.org/cssom/#dom-medialist-mediatext"> +<style media="all">* { color: red }</style> +Should not be red. +<script> + document.body.offsetTop; + document.styleSheets[0].media.mediaText = "not all"; +</script>
--- a/toolkit/components/extensions/ext-clipboard.js +++ b/toolkit/components/extensions/ext-clipboard.js @@ -1,37 +1,33 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; XPCOMUtils.defineLazyServiceGetter(this, "imgTools", "@mozilla.org/image/tools;1", "imgITools"); -const ArrayBufferInputStream = Components.Constructor( - "@mozilla.org/io/arraybuffer-input-stream;1", "nsIArrayBufferInputStream"); const SupportsInterfacePointer = Components.Constructor( "@mozilla.org/supports-interface-pointer;1", "nsISupportsInterfacePointer"); const Transferable = Components.Constructor( "@mozilla.org/widget/transferable;1", "nsITransferable"); this.clipboard = class extends ExtensionAPI { getAPI(context) { return { clipboard: { async setImageData(imageData, imageType) { if (AppConstants.platform == "android") { return Promise.reject({message: "Writing images to the clipboard is not supported on Android"}); } let mimeType = `image/${imageType}`; - let input = new ArrayBufferInputStream(); - input.setData(imageData, 0, imageData.byteLength); - let container; try { - container = imgTools.decodeImage(input, mimeType); + let str = String.fromCharCode.apply(String, new Uint8Array(imageData)); + container = imgTools.decodeImageBuffer(str, str.length, mimeType); } catch (e) { return Promise.reject({message: `Data is not a valid ${imageType} image`}); } // Other applications can only access the copied image once the data // is exported via the platform-specific clipboard APIs: // nsClipboard::SelectionGetEvent (widget/gtk/nsClipboard.cpp) // nsClipboard::PasteDictFromTransferable (widget/cocoa/nsClipboard.mm)
--- a/toolkit/components/places/nsFaviconService.cpp +++ b/toolkit/components/places/nsFaviconService.cpp @@ -17,17 +17,16 @@ #include "nsNavHistory.h" #include "nsPlacesMacros.h" #include "Helpers.h" #include "nsNetUtil.h" #include "nsReadableUtils.h" #include "nsStreamUtils.h" -#include "nsStringStream.h" #include "plbase64.h" #include "nsIClassInfoImpl.h" #include "mozilla/ArrayUtils.h" #include "mozilla/LoadInfo.h" #include "mozilla/Preferences.h" #include "nsILoadInfo.h" #include "nsIContentPolicy.h" #include "nsContentUtils.h" @@ -814,27 +813,22 @@ nsFaviconService::OptimizeIconSizes(Icon aIcon.payloads.Clear(); } return NS_OK; } // Make space for the optimized payloads. aIcon.payloads.Clear(); - nsCOMPtr<nsIInputStream> stream; - nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), - payload.data.get(), - payload.data.Length(), - NS_ASSIGNMENT_DEPEND); - NS_ENSURE_SUCCESS(rv, rv); - // decode image nsCOMPtr<imgIContainer> container; - rv = GetImgTools()->DecodeImage(stream, payload.mimeType, - getter_AddRefs(container)); + nsresult rv = GetImgTools()->DecodeImageBuffer(payload.data.get(), + payload.data.Length(), + payload.mimeType, + getter_AddRefs(container)); NS_ENSURE_SUCCESS(rv, rv); // For ICO files, we must evaluate each of the frames we care about. nsTArray<FrameData> framesInfo; rv = GetFramesInfoForContainer(container, framesInfo); NS_ENSURE_SUCCESS(rv, rv); for (const auto& frameInfo : framesInfo) {
--- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -27,19 +27,16 @@ XPCOMUtils.defineLazyModuleGetters(this, }); XPCOMUtils.defineLazyServiceGetters(this, { gTextToSubURI: ["@mozilla.org/intl/texttosuburi;1", "nsITextToSubURI"], gEnvironment: ["@mozilla.org/process/environment;1", "nsIEnvironment"], gChromeReg: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"], }); -const ArrayBufferInputStream = Components.Constructor( - "@mozilla.org/io/arraybuffer-input-stream;1", - "nsIArrayBufferInputStream", "setData"); const BinaryInputStream = Components.Constructor( "@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream", "setInputStream"); Cu.importGlobalProperties(["XMLHttpRequest"]); // A text encoder to UTF8, used whenever we commit the cache to disk. XPCOMUtils.defineLazyGetter(this, "gEncoder", @@ -352,20 +349,21 @@ loadListener.prototype = { * @param aByteArray Byte array containing the icon payload. * @param aContentType Mime type of the payload. * @param [optional] aSize desired icon size. * @throws if the icon cannot be rescaled or the rescaled icon is too big. */ function rescaleIcon(aByteArray, aContentType, aSize = 32) { if (aContentType == "image/svg+xml") throw new Error("Cannot rescale SVG image"); - let buffer = Uint8Array.from(aByteArray).buffer; + + let str = String.fromCharCode.apply(String, new Uint8Array(aByteArray)); + let imgTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools); - let input = new ArrayBufferInputStream(buffer, 0, buffer.byteLength); - let container = imgTools.decodeImage(input, aContentType); + let container = imgTools.decodeImageBuffer(str, str.length, aContentType); let stream = imgTools.encodeScaledImage(container, "image/png", aSize, aSize); let size = stream.available(); if (size > MAX_ICON_SIZE) throw new Error("Icon is too big"); let bis = new BinaryInputStream(stream); return [bis.readByteArray(size), "image/png"]; }
--- a/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm +++ b/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm @@ -1,16 +1,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; this.EXPORTED_SYMBOLS = ["LightweightThemeImageOptimizer"]; +const Cc = Components.classes; const Cu = Components.utils; const Ci = Components.interfaces; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); @@ -93,24 +94,24 @@ var ImageCropper = { inProgress[aTargetFile.path] = true; function resetInProgress() { delete inProgress[aTargetFile.path]; } ImageFile.read(aURI, function(aInputStream, aContentType) { if (aInputStream && aContentType) { - let image = ImageTools.decode(aInputStream, aContentType); - if (image && image.width && image.height) { - let stream = ImageTools.encode(image, aScreen, aOrigin, aContentType); - if (stream) { - ImageFile.write(aTargetFile, stream, resetInProgress); - return; + ImageTools.decode(aInputStream, aContentType, function(aImage) { + if (aImage && aImage.width && aImage.height) { + let stream = ImageTools.encode(aImage, aScreen, aOrigin, aContentType); + if (stream) { + ImageFile.write(aTargetFile, stream, resetInProgress); + } } - } + }); } resetInProgress(); }); } }; var ImageFile = { @@ -145,24 +146,30 @@ var ImageFile = { }); } }; XPCOMUtils.defineLazyModuleGetter(ImageFile, "_netUtil", "resource://gre/modules/NetUtil.jsm", "NetUtil"); var ImageTools = { - decode(aInputStream, aContentType) { - let outParam = null; + decode(aInputStream, aContentType, aCallback) { + let callback = { + onImageReady(aImage, aStatus) { + aCallback(aImage); + } + }; try { - outParam = this._imgTools.decodeImage(aInputStream, aContentType); - } catch (e) {} - - return outParam; + let threadManager = Cc["@mozilla.org/thread-manager;1"].getService(); + this._imgTools.decodeImageAsync(aInputStream, aContentType, callback, + threadManager.currentThread); + } catch (e) { + aCallback(null); + } }, encode(aImage, aScreen, aOrigin, aContentType) { let stream; let width = Math.min(aImage.width, aScreen.width); let height = Math.min(aImage.height, aScreen.height); let x = aOrigin == ORIGIN_TOP_RIGHT ? aImage.width - width : 0;
--- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -27,17 +27,16 @@ #include "nsContentUtils.h" #include "mozilla/Logging.h" #include "nsString.h" #include "nsDirectoryServiceUtils.h" #include "imgIContainer.h" #include "imgITools.h" -#include "nsStringStream.h" #include "nsNetUtil.h" #include "nsIOutputStream.h" #include "nsNetCID.h" #include "prtime.h" #ifdef MOZ_PLACES #include "mozIAsyncFavicons.h" #endif #include "nsIIconURI.h" @@ -1261,28 +1260,22 @@ AsyncFaviconDataReady::OnComplete(nsIURI nsCOMPtr<nsIFile> icoFile; nsresult rv = FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut); NS_ENSURE_SUCCESS(rv, rv); nsAutoString path; rv = icoFile->GetPath(path); NS_ENSURE_SUCCESS(rv, rv); - // Convert the obtained favicon data to an input stream - nsCOMPtr<nsIInputStream> stream; - rv = NS_NewByteInputStream(getter_AddRefs(stream), - reinterpret_cast<const char*>(aData), - aDataLen, - NS_ASSIGNMENT_DEPEND); - NS_ENSURE_SUCCESS(rv, rv); - // Decode the image from the format it was returned to us in (probably PNG) nsCOMPtr<imgIContainer> container; nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1"); - rv = imgtool->DecodeImage(stream, aMimeType, getter_AddRefs(container)); + rv = imgtool->DecodeImageBuffer(reinterpret_cast<const char*>(aData), + aDataLen, aMimeType, + getter_AddRefs(container)); NS_ENSURE_SUCCESS(rv, rv); RefPtr<SourceSurface> surface = container->GetFrame(imgIContainer::FRAME_FIRST, imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY); NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);