diff --git a/CLOBBER b/CLOBBER index 4108b26533d8..22d13e193ea8 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 916012 moves definition from one WEBIDL_FILE to another (Bug 979886) +Bug 995411 moves some files around in gfx/layers and widget/xpwidget diff --git a/b2g/config/mozconfigs/common b/b2g/config/mozconfigs/common index 30581f1e49c6..c6baae8bc92b 100644 --- a/b2g/config/mozconfigs/common +++ b/b2g/config/mozconfigs/common @@ -3,6 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. no_tooltool=1 +no_sccache=1 # This file is included at the top of all b2g mozconfigs diff --git a/b2g/config/mozconfigs/linux32_gecko/nightly b/b2g/config/mozconfigs/linux32_gecko/nightly index e5d72130b381..fdab89d68daf 100644 --- a/b2g/config/mozconfigs/linux32_gecko/nightly +++ b/b2g/config/mozconfigs/linux32_gecko/nightly @@ -21,7 +21,8 @@ export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. # DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors -# Use ccache +# Use sccache +no_sccache= . "$topsrcdir/build/mozconfig.cache" #B2G options diff --git a/b2g/config/mozconfigs/linux64_gecko/debug b/b2g/config/mozconfigs/linux64_gecko/debug index a5b058908b19..256b44165884 100644 --- a/b2g/config/mozconfigs/linux64_gecko/debug +++ b/b2g/config/mozconfigs/linux64_gecko/debug @@ -22,7 +22,8 @@ export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. # DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors -# Use ccache +# Use sccache +no_sccache= . "$topsrcdir/build/mozconfig.cache" #B2G options diff --git a/b2g/config/mozconfigs/linux64_gecko/nightly b/b2g/config/mozconfigs/linux64_gecko/nightly index 991bbf3ce20d..fa9bae8ac008 100644 --- a/b2g/config/mozconfigs/linux64_gecko/nightly +++ b/b2g/config/mozconfigs/linux64_gecko/nightly @@ -21,7 +21,8 @@ export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. # DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors -# Use ccache +# Use sccache +no_sccache= . "$topsrcdir/build/mozconfig.cache" #B2G options diff --git a/b2g/config/tooltool-manifests/ics.manifest b/b2g/config/tooltool-manifests/ics.manifest deleted file mode 100644 index f1afd33eef65..000000000000 --- a/b2g/config/tooltool-manifests/ics.manifest +++ /dev/null @@ -1,14 +0,0 @@ -[ -{ -"size": 195, -"digest": "236362c71c433971c36b46d34e8560342435718364bc390df8de6a33249fb1fbf4fc3d0143f1e22bca262a7af7dc1b277a920bfde3ee8197eb07db2e7cef3e1f", -"algorithm": "sha512", -"filename": "setup.sh" -}, -{ -"size": 63159127, -"digest": "fcf629c815b5cbed7858d7697815f355275dcc6b060ae5455b4b31fde0d78ebc176927564a5353ceacdb9f9c9bfc1357f1341bf6ba844c25153a89664e661510", -"algorithm": "sha512", -"filename": "gonk-toolchain-7.tar.bz2" -} -] diff --git a/b2g/config/tooltool-manifests/linux32/releng.manifest b/b2g/config/tooltool-manifests/linux32/releng.manifest new file mode 100644 index 000000000000..285a8f8cd9d6 --- /dev/null +++ b/b2g/config/tooltool-manifests/linux32/releng.manifest @@ -0,0 +1,14 @@ +[ +{ +"size": 50, +"digest": "48f405d8c2712838b9dd3be118951c8b41c63c891576f5287d2e05afa8fd051a08807511259581aa3170a3c4f7d4e77e6c0539b00b8f6845f01562127f6a27fa", +"algorithm": "sha512", +"filename": "setup.sh" +}, +{ +"size": 160232, +"digest": "8656c3fc2daa66839ec81a0edbd9759040a83c7a41c3e472d7f90508b80eefd008b87305dc8549b4ff6098dc33fe17fedc9b4eb76cf5307d5f22dae925c033db", +"algorithm": "sha512", +"filename": "sccache.tar.xz" +} +] diff --git a/b2g/config/tooltool-manifests/linux64/releng.manifest b/b2g/config/tooltool-manifests/linux64/releng.manifest new file mode 100644 index 000000000000..285a8f8cd9d6 --- /dev/null +++ b/b2g/config/tooltool-manifests/linux64/releng.manifest @@ -0,0 +1,14 @@ +[ +{ +"size": 50, +"digest": "48f405d8c2712838b9dd3be118951c8b41c63c891576f5287d2e05afa8fd051a08807511259581aa3170a3c4f7d4e77e6c0539b00b8f6845f01562127f6a27fa", +"algorithm": "sha512", +"filename": "setup.sh" +}, +{ +"size": 160232, +"digest": "8656c3fc2daa66839ec81a0edbd9759040a83c7a41c3e472d7f90508b80eefd008b87305dc8549b4ff6098dc33fe17fedc9b4eb76cf5307d5f22dae925c033db", +"algorithm": "sha512", +"filename": "sccache.tar.xz" +} +] diff --git a/b2g/config/tooltool-manifests/releng.manifest b/b2g/config/tooltool-manifests/releng.manifest deleted file mode 100644 index f6fd515acfb7..000000000000 --- a/b2g/config/tooltool-manifests/releng.manifest +++ /dev/null @@ -1,14 +0,0 @@ -[ -{ -"size": 195, -"digest": "da2edcb1ec9b169f6c685d02ebd0bd4ad53ace2df58598f15e1bde43dd74bcb816620601587c97e636bda3327045b43c8f5e973672ebd904e06036f70466908f", -"algorithm": "sha512", -"filename": "setup.sh" -}, -{ -"size": 121166734, -"digest": "10da1d28d49ff1aa9ad3d84e52235dc8ed7df6721530b896a53424480ec23a2e9f28cadd631f562342325611ecb72152be51f9b62616356f33005f6cc0fb82fe", -"algorithm": "sha512", -"filename": "gonk-toolchain-3.tar.bz2" -} -] diff --git a/browser/base/content/test/general/browser_bug435325.js b/browser/base/content/test/general/browser_bug435325.js index 2e9136d2be36..fe05c757a12d 100644 --- a/browser/base/content/test/general/browser_bug435325.js +++ b/browser/base/content/test/general/browser_bug435325.js @@ -51,6 +51,11 @@ function checkPage() { // Now press the "Try Again" button ok(gBrowser.contentDocument.getElementById("errorTryAgain"), "The error page has got a #errorTryAgain element"); + + // Re-enable the proxy so example.com is resolved to localhost, rather than + // the actual example.com. + Services.prefs.setIntPref("network.proxy.type", proxyPrefValue); + gBrowser.contentDocument.getElementById("errorTryAgain").click(); ok(!Services.io.offline, "After clicking the Try Again button, we're back " + @@ -60,7 +65,6 @@ function checkPage() { } registerCleanupFunction(function() { - Services.prefs.setIntPref("network.proxy.type", proxyPrefValue); Services.prefs.setBoolPref("browser.cache.disk.enable", true); Services.prefs.setBoolPref("browser.cache.memory.enable", true); Services.io.offline = false; diff --git a/browser/config/mozconfigs/linux32/l10n-mozconfig b/browser/config/mozconfigs/linux32/l10n-mozconfig index 1e88587cf64d..45a3727a30ea 100644 --- a/browser/config/mozconfigs/linux32/l10n-mozconfig +++ b/browser/config/mozconfigs/linux32/l10n-mozconfig @@ -1,4 +1,5 @@ no_tooltool=1 +no_sccache=1 ac_add_options --with-l10n-base=../../l10n ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/browser/config/mozconfigs/linux32/valgrind b/browser/config/mozconfigs/linux32/valgrind index 8c8dd0648f32..c98ecd79fcd6 100644 --- a/browser/config/mozconfigs/linux32/valgrind +++ b/browser/config/mozconfigs/linux32/valgrind @@ -1,4 +1,5 @@ no_tooltool=1 +no_sccache=1 . $topsrcdir/browser/config/mozconfigs/linux32/nightly diff --git a/browser/config/mozconfigs/linux64/l10n-mozconfig b/browser/config/mozconfigs/linux64/l10n-mozconfig index e2141ccbd95d..ce8b34ecb390 100644 --- a/browser/config/mozconfigs/linux64/l10n-mozconfig +++ b/browser/config/mozconfigs/linux64/l10n-mozconfig @@ -1,4 +1,5 @@ no_tooltool=1 +no_sccache=1 ac_add_options --with-l10n-base=../../l10n ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/browser/config/mozconfigs/linux64/valgrind b/browser/config/mozconfigs/linux64/valgrind index c6fd78aabd48..bff7ff7b0b1b 100644 --- a/browser/config/mozconfigs/linux64/valgrind +++ b/browser/config/mozconfigs/linux64/valgrind @@ -1,4 +1,5 @@ no_tooltool=1 +no_sccache=1 . $topsrcdir/browser/config/mozconfigs/linux64/nightly diff --git a/build/mozconfig.cache b/build/mozconfig.cache index 844326e5fb82..028dc6f4c6dc 100644 --- a/build/mozconfig.cache +++ b/build/mozconfig.cache @@ -9,7 +9,7 @@ $(python2.7 -c 'import json; p = json.loads(open("'"$topsrcdir"'/../buildprops.j EOF bucket= -if test -z "$SCCACHE_DISABLE" -a -z "$no_tooltool"; then +if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache"; then case "${branch}_${master}" in try_*scl1.mozilla.com*|try_*.scl3.mozilla.com*) bucket=mozilla-releng-ceph-cache-scl3-try diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 5c33622b4579..3b633f53d181 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -998,6 +998,7 @@ GK_ATOM(showresizer, "showresizer") GK_ATOM(simple, "simple") GK_ATOM(single, "single") GK_ATOM(size, "size") +GK_ATOM(sizes, "sizes") GK_ATOM(sizemode, "sizemode") GK_ATOM(sizetopopup, "sizetopopup") GK_ATOM(slider, "slider") diff --git a/content/canvas/public/moz.build b/content/canvas/public/moz.build index 6635d5ebe58f..c775112dcdc2 100644 --- a/content/canvas/public/moz.build +++ b/content/canvas/public/moz.build @@ -11,7 +11,6 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'content_canvas' EXPORTS += [ - 'nsICanvasElementExternal.h', 'nsICanvasRenderingContextInternal.h', ] diff --git a/content/canvas/public/nsICanvasElementExternal.h b/content/canvas/public/nsICanvasElementExternal.h deleted file mode 100644 index 1e1ddcd9a8bc..000000000000 --- a/content/canvas/public/nsICanvasElementExternal.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nsICanvasElementExternal_h___ -#define nsICanvasElementExternal_h___ - -#include "nsISupports.h" -#include "GraphicsFilter.h" - -class gfxContext; -class nsIFrame; -struct gfxRect; - -#define NS_ICANVASELEMENTEXTERNAL_IID \ - { 0x51870f54, 0x6c4c, 0x469a, {0xad, 0x46, 0xf0, 0xa9, 0x8e, 0x32, 0xa7, 0xe2 } } - -class nsRenderingContext; -class nsICanvasRenderingContextInternal; - -struct _cairo_surface; - -/* - * This interface contains methods that are needed outside of the content/layout - * modules, specifically widget. It should eventually go away when we support - * libxul builds, and HTMLCanvasElement be used directly. - * - * Code internal to content/layout should /never/ use this interface; if the - * same functionality is needed in both places, two separate methods should be - * used. - */ - -class nsICanvasElementExternal : public nsISupports { -public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASELEMENTEXTERNAL_IID) - - enum { - RenderFlagPremultAlpha = 0x1 - }; - - /** - * Get the size in pixels of this canvas element - */ - NS_IMETHOD_(nsIntSize) GetSizeExternal() = 0; - - /* - * Ask the canvas element to tell the contexts to render themselves - * to the given gfxContext at the origin of its coordinate space. - */ - NS_IMETHOD RenderContextsExternal(gfxContext *ctx, - GraphicsFilter aFilter, - uint32_t aFlags = RenderFlagPremultAlpha) = 0; -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsICanvasElementExternal, NS_ICANVASELEMENTEXTERNAL_IID) - -#endif /* nsICanvasElementExternal_h___ */ diff --git a/content/canvas/public/nsICanvasRenderingContextInternal.h b/content/canvas/public/nsICanvasRenderingContextInternal.h index a1a1fc28e72c..0eea62e0aeb8 100644 --- a/content/canvas/public/nsICanvasRenderingContextInternal.h +++ b/content/canvas/public/nsICanvasRenderingContextInternal.h @@ -6,6 +6,7 @@ #ifndef nsICanvasRenderingContextInternal_h___ #define nsICanvasRenderingContextInternal_h___ +#include "mozilla/gfx/2D.h" #include "nsISupports.h" #include "nsIInputStream.h" #include "nsIDocShell.h" @@ -14,8 +15,8 @@ #include "mozilla/RefPtr.h" #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \ -{ 0x9a6a5bdf, 0x1261, 0x4057, \ - { 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } } +{ 0x3cc9e801, 0x1806, 0x4ff6, \ + { 0x86, 0x14, 0xf9, 0xd0, 0xf4, 0xfb, 0x3b, 0x08 } } class gfxContext; class gfxASurface; @@ -41,10 +42,6 @@ public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID) - enum { - RenderFlagPremultAlpha = 0x1 - }; - void SetCanvasElement(mozilla::dom::HTMLCanvasElement* aParentCanvas) { mCanvasElement = aParentCanvas; @@ -66,11 +63,6 @@ public: NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0; - // Render the canvas at the origin of the given gfxContext - NS_IMETHOD Render(gfxContext *ctx, - GraphicsFilter aFilter, - uint32_t aFlags = RenderFlagPremultAlpha) = 0; - // Creates an image buffer. Returns null on failure. virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0; @@ -83,14 +75,13 @@ public: NS_IMETHOD GetInputStream(const char *aMimeType, const char16_t *aEncoderOptions, nsIInputStream **aStream) = 0; - - // If this canvas context can be represented with a simple Thebes surface, - // return the surface. Otherwise returns an error. - NS_IMETHOD GetThebesSurface(gfxASurface **surface) = 0; // This gets an Azure SourceSurface for the canvas, this will be a snapshot // of the canvas at the time it was called. - virtual mozilla::TemporaryRef GetSurfaceSnapshot() = 0; + // If aPremultAlpha is provided, then it assumed the callee can handle + // un-premultiplied surfaces, and *aPremultAlpha will be set to false + // if one is returned. + virtual mozilla::TemporaryRef GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) = 0; // If this context is opaque, the backing store of the canvas should // be created as opaque; all compositing operators should assume the diff --git a/content/canvas/src/CanvasRenderingContext2D.cpp b/content/canvas/src/CanvasRenderingContext2D.cpp index 6e8e1eee5fad..dbdb432c8c25 100755 --- a/content/canvas/src/CanvasRenderingContext2D.cpp +++ b/content/canvas/src/CanvasRenderingContext2D.cpp @@ -1053,51 +1053,6 @@ CanvasRenderingContext2D::SetIsIPC(bool isIPC) return NS_OK; } -NS_IMETHODIMP -CanvasRenderingContext2D::Render(gfxContext *ctx, GraphicsFilter aFilter, uint32_t aFlags) -{ - nsresult rv = NS_OK; - - EnsureTarget(); - if (!IsTargetValid()) { - return NS_ERROR_FAILURE; - } - - nsRefPtr surface; - - if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) { - return NS_ERROR_FAILURE; - } - - nsRefPtr pat = new gfxPattern(surface); - - pat->SetFilter(aFilter); - pat->SetExtend(gfxPattern::EXTEND_PAD); - - gfxContext::GraphicsOperator op = ctx->CurrentOperator(); - if (mOpaque) - ctx->SetOperator(gfxContext::OPERATOR_SOURCE); - - // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee - // pixel alignment for this stuff! - ctx->NewPath(); - ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat); - ctx->Fill(); - - if (mOpaque) - ctx->SetOperator(op); - - if (!(aFlags & RenderFlagPremultAlpha)) { - nsRefPtr curSurface = ctx->CurrentSurface(); - nsRefPtr gis = curSurface->GetAsImageSurface(); - MOZ_ASSERT(gis, "If non-premult alpha, must be able to get image surface!"); - - gfxUtils::UnpremultiplyImageSurface(gis); - } - - return rv; -} - NS_IMETHODIMP CanvasRenderingContext2D::SetContextOptions(JSContext* aCx, JS::Handle aOptions) { @@ -3266,28 +3221,6 @@ CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } - - // Special case for Canvas, which could be an Azure canvas! - nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0); - if (srcCanvas == this) { - // Self-copy. - srcSurf = mTarget->Snapshot(); - imgSize = gfxIntSize(mWidth, mHeight); - } else if (srcCanvas) { - // This might not be an Azure canvas! - srcSurf = srcCanvas->GetSurfaceSnapshot(); - - if (srcSurf) { - if (mCanvasElement) { - // Do security check here. - CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement, - element->NodePrincipal(), - canvas->IsWriteOnly(), - false); - } - imgSize = gfxIntSize(srcSurf->GetSize().width, srcSurf->GetSize().height); - } - } } else { if (image.IsHTMLImageElement()) { HTMLImageElement* img = &image.GetAsHTMLImageElement(); @@ -4147,27 +4080,6 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w return NS_OK; } -NS_IMETHODIMP -CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface) -{ - EnsureTarget(); - if (!IsTargetValid()) { - return NS_ERROR_FAILURE; - } - - nsRefPtr thebesSurface = - gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget); - - if (!thebesSurface) { - return NS_ERROR_FAILURE; - } - - *surface = thebesSurface; - NS_ADDREF(*surface); - - return NS_OK; -} - static already_AddRefed CreateImageData(JSContext* cx, CanvasRenderingContext2D* context, uint32_t w, uint32_t h, ErrorResult& error) diff --git a/content/canvas/src/CanvasRenderingContext2D.h b/content/canvas/src/CanvasRenderingContext2D.h index 5621b7d3e766..f3a4ba0623da 100644 --- a/content/canvas/src/CanvasRenderingContext2D.h +++ b/content/canvas/src/CanvasRenderingContext2D.h @@ -457,16 +457,18 @@ public: NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE; NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE; - NS_IMETHOD Render(gfxContext *ctx, - GraphicsFilter aFilter, - uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE; NS_IMETHOD GetInputStream(const char* aMimeType, const char16_t* aEncoderOptions, nsIInputStream **aStream) MOZ_OVERRIDE; - NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE; - mozilla::TemporaryRef GetSurfaceSnapshot() MOZ_OVERRIDE - { EnsureTarget(); return mTarget->Snapshot(); } + mozilla::TemporaryRef GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) MOZ_OVERRIDE + { + EnsureTarget(); + if (aPremultAlpha) { + *aPremultAlpha = true; + } + return mTarget->Snapshot(); + } NS_IMETHOD SetIsOpaque(bool isOpaque) MOZ_OVERRIDE; bool GetIsOpaque() MOZ_OVERRIDE { return mOpaque; } diff --git a/content/canvas/src/WebGLContext.cpp b/content/canvas/src/WebGLContext.cpp index b416c67a884b..0c178c2fb6a1 100644 --- a/content/canvas/src/WebGLContext.cpp +++ b/content/canvas/src/WebGLContext.cpp @@ -136,25 +136,6 @@ WebGLContext::WebGLContext() mFakeVertexAttrib0BufferObject = 0; mFakeVertexAttrib0BufferStatus = WebGLVertexAttrib0Status::Default; - // these are de default values, see 6.2 State tables in the OpenGL ES 2.0.25 spec - mColorWriteMask[0] = 1; - mColorWriteMask[1] = 1; - mColorWriteMask[2] = 1; - mColorWriteMask[3] = 1; - mDepthWriteMask = 1; - mColorClearValue[0] = 0.f; - mColorClearValue[1] = 0.f; - mColorClearValue[2] = 0.f; - mColorClearValue[3] = 0.f; - mDepthClearValue = 1.f; - mStencilClearValue = 0; - mStencilRefFront = 0; - mStencilRefBack = 0; - mStencilValueMaskFront = 0xffffffff; - mStencilValueMaskBack = 0xffffffff; - mStencilWriteMaskFront = 0xffffffff; - mStencilWriteMaskBack = 0xffffffff; - mViewportX = 0; mViewportY = 0; mViewportWidth = 0; @@ -209,7 +190,7 @@ WebGLContext::WebGLContext() InvalidateBufferFetching(); - mIsScreenCleared = false; + mBackbufferNeedsClear = true; mDisableFragHighP = false; @@ -423,7 +404,7 @@ WebGLContext::SetDimensions(int32_t width, int32_t height) mHeight = gl->OffscreenSize().height; mResetLayer = true; - ClearScreen(); + mBackbufferNeedsClear = true; return NS_OK; } @@ -610,7 +591,11 @@ WebGLContext::SetDimensions(int32_t width, int32_t height) gl->fClearDepth(1.0f); gl->fClearStencil(0); - gl->ClearSafely(); + mBackbufferNeedsClear = true; + + // Clear immediately, because we need to present the cleared initial + // buffer. + ClearBackbufferIfNeeded(); mShouldPresent = true; @@ -625,46 +610,23 @@ WebGLContext::SetDimensions(int32_t width, int32_t height) return NS_OK; } -NS_IMETHODIMP -WebGLContext::Render(gfxContext *ctx, GraphicsFilter f, uint32_t aFlags) +void +WebGLContext::ClearBackbufferIfNeeded() { - if (!gl) - return NS_OK; - - nsRefPtr surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight), - gfxImageFormat::ARGB32); - if (surf->CairoStatus() != 0) - return NS_ERROR_FAILURE; + if (!mBackbufferNeedsClear) + return; +#ifdef DEBUG gl->MakeCurrent(); - ReadScreenIntoImageSurface(gl, surf); - bool srcPremultAlpha = mOptions.premultipliedAlpha; - bool dstPremultAlpha = aFlags & RenderFlagPremultAlpha; + GLuint fb = 0; + gl->GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &fb); + MOZ_ASSERT(fb == 0); +#endif - if (!srcPremultAlpha && dstPremultAlpha) { - gfxUtils::PremultiplyImageSurface(surf); - } else if (srcPremultAlpha && !dstPremultAlpha) { - gfxUtils::UnpremultiplyImageSurface(surf); - } - surf->MarkDirty(); + ClearScreen(); - nsRefPtr pat = new gfxPattern(surf); - pat->SetFilter(f); - - // Pixels from ReadPixels will be "upside down" compared to - // what cairo wants, so draw with a y-flip and a translte to - // flip them. - gfxMatrix m; - m.Translate(gfxPoint(0.0, mHeight)); - m.Scale(1.0, -1.0); - pat->SetMatrix(m); - - ctx->NewPath(); - ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat); - ctx->Fill(); - - return NS_OK; + mBackbufferNeedsClear = false; } void WebGLContext::LoseOldestWebGLContextIfLimitExceeded() @@ -758,25 +720,31 @@ WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) *aImageBuffer = nullptr; *aFormat = 0; - nsRefPtr imgsurf = - new gfxImageSurface(gfxIntSize(mWidth, mHeight), - gfxImageFormat::ARGB32); + // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied + bool premult; + RefPtr snapshot = + GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult); + if (!snapshot) { + return; + } + MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!"); - if (!imgsurf || imgsurf->CairoStatus()) { + RefPtr dataSurface = snapshot->GetDataSurface(); + + DataSourceSurface::MappedSurface map; + if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { return; } - nsRefPtr ctx = new gfxContext(imgsurf); - if (!ctx || ctx->HasError()) { + static const fallible_t fallible = fallible_t(); + uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4]; + if (!imageBuffer) { + dataSurface->Unmap(); return; } + memcpy(imageBuffer, map.mData, mWidth * mHeight * 4); - // Use Render() to make sure that appropriate y-flip gets applied - uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0; - nsresult rv = Render(ctx, GraphicsFilter::FILTER_NEAREST, flags); - if (NS_FAILED(rv)) { - return; - } + dataSurface->Unmap(); int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB; if (!mOptions.premultipliedAlpha) { @@ -785,17 +753,10 @@ WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) // Yes, it is THAT silly. // Except for different lossy conversions by color, // we could probably just change the label, and not change the data. - gfxUtils::ConvertBGRAtoRGBA(imgsurf); + gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4); format = imgIEncoder::INPUT_FORMAT_RGBA; } - static const fallible_t fallible = fallible_t(); - uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4]; - if (!imageBuffer) { - return; - } - memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4); - *aImageBuffer = imageBuffer; *aFormat = format; } @@ -827,12 +788,6 @@ WebGLContext::GetInputStream(const char* aMimeType, encoder, aEncoderOptions, aStream); } -NS_IMETHODIMP -WebGLContext::GetThebesSurface(gfxASurface **surface) -{ - return NS_ERROR_NOT_AVAILABLE; -} - void WebGLContext::UpdateLastUseIndex() { static CheckedInt sIndex = 0; @@ -1014,7 +969,6 @@ WebGLContext::ClearScreen() colorAttachmentsMask[0] = true; ForceClearFramebufferWithDefaultValues(clearMask, colorAttachmentsMask); - mIsScreenCleared = true; } #ifdef DEBUG @@ -1202,13 +1156,14 @@ WebGLContext::PresentScreenBuffer() } gl->MakeCurrent(); + MOZ_ASSERT(!mBackbufferNeedsClear); if (!gl->PublishFrame()) { this->ForceLoseContext(); return false; } if (!mOptions.preserveDrawingBuffer) { - ClearScreen(); + mBackbufferNeedsClear = true; } mShouldPresent = false; @@ -1376,9 +1331,61 @@ void WebGLContext::MakeContextCurrent() const { gl->MakeCurrent(); } mozilla::TemporaryRef -WebGLContext::GetSurfaceSnapshot() +WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha) { - return nullptr; + if (!gl) + return nullptr; + + nsRefPtr surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight), + gfxImageFormat::ARGB32, + mWidth * 4, 0, false); + if (surf->CairoStatus() != 0) { + return nullptr; + } + + gl->MakeCurrent(); + { + ScopedBindFramebuffer autoFB(gl, 0); + ClearBackbufferIfNeeded(); + ReadPixelsIntoImageSurface(gl, surf); + } + + if (aPremultAlpha) { + *aPremultAlpha = true; + } + bool srcPremultAlpha = mOptions.premultipliedAlpha; + if (!srcPremultAlpha) { + if (aPremultAlpha) { + *aPremultAlpha = false; + } else { + gfxUtils::PremultiplyImageSurface(surf); + surf->MarkDirty(); + } + } + + RefPtr dt = + Factory::CreateDrawTarget(BackendType::CAIRO, + IntSize(mWidth, mHeight), + SurfaceFormat::B8G8R8A8); + + if (!dt) { + return nullptr; + } + + RefPtr source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surf); + + Matrix m; + m.Translate(0.0, mHeight); + m.Scale(1.0, -1.0); + dt->SetTransform(m); + + dt->DrawSurface(source, + Rect(0, 0, mWidth, mHeight), + Rect(0, 0, mWidth, mHeight), + DrawSurfaceOptions(), + DrawOptions(1.0f, CompositionOp::OP_SOURCE)); + + return dt->Snapshot(); } // diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h index b9a8fa63ef17..f6896332aa6f 100644 --- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -165,15 +165,11 @@ public: { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD Reset() MOZ_OVERRIDE { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; } - NS_IMETHOD Render(gfxContext *ctx, - GraphicsFilter f, - uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE; virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat); NS_IMETHOD GetInputStream(const char* aMimeType, const char16_t* aEncoderOptions, nsIInputStream **aStream) MOZ_OVERRIDE; - NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE; - mozilla::TemporaryRef GetSurfaceSnapshot() MOZ_OVERRIDE; + mozilla::TemporaryRef GetSurfaceSnapshot(bool* aPremultAlpha) MOZ_OVERRIDE; NS_IMETHOD SetIsOpaque(bool b) MOZ_OVERRIDE { return NS_OK; }; bool GetIsOpaque() MOZ_OVERRIDE { return false; } @@ -243,6 +239,7 @@ public: // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'. void ClearScreen(); + void ClearBackbufferIfNeeded(); bool MinCapabilityMode() const { return mMinCapability; } @@ -841,7 +838,7 @@ protected: bool mLoseContextOnHeapMinimize; bool mCanLoseContextInForeground; bool mShouldPresent; - bool mIsScreenCleared; + bool mBackbufferNeedsClear; bool mDisableFragHighP; template @@ -1034,7 +1031,7 @@ protected: if (mPixelStoreColorspaceConversion == LOCAL_GL_NONE) flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION; if (!mPixelStorePremultiplyAlpha) - flags |= nsLayoutUtils::SFE_NO_PREMULTIPLY_ALPHA; + flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA; return nsLayoutUtils::SurfaceFromElement(aElement, flags); } template diff --git a/content/canvas/src/WebGLContextDraw.cpp b/content/canvas/src/WebGLContextDraw.cpp index c7d4ce4f4ac9..034053ce6389 100644 --- a/content/canvas/src/WebGLContextDraw.cpp +++ b/content/canvas/src/WebGLContextDraw.cpp @@ -103,6 +103,8 @@ bool WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcoun ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info); return false; } + } else { + ClearBackbufferIfNeeded(); } if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) { @@ -264,6 +266,8 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type, ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info); return false; } + } else { + ClearBackbufferIfNeeded(); } if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) { @@ -333,7 +337,7 @@ void WebGLContext::Draw_cleanup() if (!mBoundFramebuffer) { Invalidate(); mShouldPresent = true; - mIsScreenCleared = false; + MOZ_ASSERT(!mBackbufferNeedsClear); } if (gl->WorkAroundDriverBugs()) { diff --git a/content/canvas/src/WebGLContextFramebufferOperations.cpp b/content/canvas/src/WebGLContextFramebufferOperations.cpp index 249f2d37c144..ae9162f71769 100644 --- a/content/canvas/src/WebGLContextFramebufferOperations.cpp +++ b/content/canvas/src/WebGLContextFramebufferOperations.cpp @@ -35,43 +35,13 @@ WebGLContext::Clear(GLbitfield mask) gl->fClear(mask); return; + } else { + ClearBackbufferIfNeeded(); } // Ok, we're clearing the default framebuffer/screen. - bool needsClear = true; - if (mIsScreenCleared) { - bool isClearRedundant = true; - if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { - if (mColorClearValue[0] != 0.0f || - mColorClearValue[1] != 0.0f || - mColorClearValue[2] != 0.0f || - mColorClearValue[3] != 0.0f) - { - isClearRedundant = false; - } - } - - if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) { - if (mDepthClearValue != 1.0f) { - isClearRedundant = false; - } - } - - if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) { - if (mStencilClearValue != 0) { - isClearRedundant = false; - } - } - - if (isClearRedundant) - needsClear = false; - } - - if (needsClear) { - gl->fClear(mask); - mIsScreenCleared = false; - } + gl->fClear(mask); Invalidate(); mShouldPresent = true; diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index 1ea087c2ed2d..f28d32761a3c 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -29,6 +29,7 @@ #include "nsLayoutUtils.h" #include "CanvasUtils.h" +#include "gfxUtils.h" #include "jsfriendapi.h" @@ -472,6 +473,8 @@ WebGLContext::CopyTexImage2D(GLenum target, return ErrorInvalidOperation("copyTexImage2D: Read source attachment doesn't have the" " correct color/depth/stencil type."); } + } else { + ClearBackbufferIfNeeded(); } bool texFormatRequiresAlpha = internalformat == LOCAL_GL_RGBA || @@ -584,6 +587,8 @@ WebGLContext::CopyTexSubImage2D(GLenum target, return ErrorInvalidOperation("copyTexSubImage2D: Read source attachment doesn't have the" " correct color/depth/stencil type."); } + } else { + ClearBackbufferIfNeeded(); } bool texFormatRequiresAlpha = (internalFormat == LOCAL_GL_RGBA || @@ -2194,6 +2199,8 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, return ErrorInvalidOperation("readPixels: Read source attachment doesn't have the" " correct color/depth/stencil type."); } + } else { + ClearBackbufferIfNeeded(); } // Now that the errors are out of the way, on to actually reading @@ -2521,6 +2528,10 @@ WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromE return NS_OK; } + if (!mPixelStorePremultiplyAlpha && res.mIsPremultiplied) { + data = gfxUtils::UnpremultiplyDataSurface(data); + } + // We disallow loading cross-domain images and videos that have not been validated // with CORS as WebGL textures. The reason for doing that is that timing // attacks on WebGL shaders are able to retrieve approximations of the diff --git a/content/canvas/src/WebGLContextState.cpp b/content/canvas/src/WebGLContextState.cpp index 8de720b75fe2..5b076eb43eb2 100644 --- a/content/canvas/src/WebGLContextState.cpp +++ b/content/canvas/src/WebGLContextState.cpp @@ -80,9 +80,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) if (MinCapabilityMode()) { switch(pname) { - // + //////////////////////////// // Single-value params - // // int case LOCAL_GL_MAX_VERTEX_ATTRIBS: @@ -118,18 +117,15 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) } } - if (IsExtensionEnabled(WEBGL_draw_buffers)) - { - if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) - { + if (IsExtensionEnabled(WEBGL_draw_buffers)) { + if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) { return JS::Int32Value(mGLMaxColorAttachments); - } - else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) - { + + } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) { return JS::Int32Value(mGLMaxDrawBuffers); - } - else if (pname >= LOCAL_GL_DRAW_BUFFER0 && - pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers)) + + } else if (pname >= LOCAL_GL_DRAW_BUFFER0 && + pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers)) { if (mBoundFramebuffer) { GLint iv = 0; @@ -149,17 +145,12 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) } if (IsExtensionEnabled(OES_vertex_array_object)) { - switch (pname) { - - case LOCAL_GL_VERTEX_ARRAY_BINDING: - { - if (mBoundVertexArray == mDefaultVertexArray){ - return WebGLObjectAsJSValue(cx, (WebGLVertexArray *) nullptr, rv); - } - - return WebGLObjectAsJSValue(cx, mBoundVertexArray.get(), rv); + if (pname == LOCAL_GL_VERTEX_ARRAY_BINDING) { + if (mBoundVertexArray == mDefaultVertexArray){ + return WebGLObjectAsJSValue(cx, (WebGLVertexArray *) nullptr, rv); } + return WebGLObjectAsJSValue(cx, mBoundVertexArray.get(), rv); } } @@ -171,8 +162,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return StringValue(cx, "Mozilla", rv); case LOCAL_GL_RENDERER: return StringValue(cx, "Mozilla", rv); - case LOCAL_GL_VERSION: - { + case LOCAL_GL_VERSION: { const char* version = 0; if (IsWebGL2()) { @@ -189,13 +179,11 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) // Privileged string params exposed by WEBGL_debug_renderer_info: case UNMASKED_VENDOR_WEBGL: - case UNMASKED_RENDERER_WEBGL: - { + case UNMASKED_RENDERER_WEBGL: { // The privilege check is done in WebGLContext::IsExtensionSupported. // So here we just have to check that the extension is enabled. if (!IsExtensionEnabled(WEBGL_debug_renderer_info)) { - ErrorInvalidEnumInfo("getParameter: parameter", pname); - return JS::NullValue(); + break; } GLenum glstringname = LOCAL_GL_NONE; if (pname == UNMASKED_VENDOR_WEBGL) { @@ -207,9 +195,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return StringValue(cx, string, rv); } - // + //////////////////////////////// // Single-value params - // // unsigned int case LOCAL_GL_CULL_FACE_MODE: @@ -230,8 +217,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) case LOCAL_GL_BLEND_DST_ALPHA: case LOCAL_GL_BLEND_EQUATION_RGB: case LOCAL_GL_BLEND_EQUATION_ALPHA: - case LOCAL_GL_GENERATE_MIPMAP_HINT: - { + case LOCAL_GL_GENERATE_MIPMAP_HINT: { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::NumberValue(uint32_t(i)); @@ -254,23 +240,20 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) case LOCAL_GL_BLUE_BITS: case LOCAL_GL_ALPHA_BITS: case LOCAL_GL_DEPTH_BITS: - case LOCAL_GL_STENCIL_BITS: - { + case LOCAL_GL_STENCIL_BITS: { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::Int32Value(i); } - case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: + case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: { if (IsExtensionEnabled(OES_standard_derivatives)) { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::Int32Value(i); + } else { + break; } - else { - ErrorInvalidEnum("getParameter: parameter", pname); - return JS::NullValue(); - } - + } case LOCAL_GL_MAX_TEXTURE_SIZE: return JS::Int32Value(mGLMaxTextureSize); @@ -291,8 +274,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) case LOCAL_GL_NUM_COMPRESSED_TEXTURE_FORMATS: return JS::Int32Value(0); - case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: - { + case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: { uint32_t length = mCompressedTextureFormats.Length(); JSObject* obj = Uint32Array::Create(cx, this, length, mCompressedTextureFormats.Elements()); if (!obj) { @@ -300,8 +282,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) } return JS::ObjectOrNullValue(obj); } - case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: - { + case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: { if (!IsWebGL2()) { break; } @@ -313,8 +294,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) case LOCAL_GL_STENCIL_BACK_VALUE_MASK: case LOCAL_GL_STENCIL_BACK_WRITEMASK: case LOCAL_GL_STENCIL_VALUE_MASK: - case LOCAL_GL_STENCIL_WRITEMASK: - { + case LOCAL_GL_STENCIL_WRITEMASK: { GLint i = 0; // the GL api (glGetIntegerv) only does signed ints gl->fGetIntegerv(pname, &i); GLuint i_unsigned(i); // this is where -1 becomes 2^32-1 @@ -323,21 +303,20 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) } // float - case LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: + case LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: { if (IsExtensionEnabled(EXT_texture_filter_anisotropic)) { GLfloat f = 0.f; gl->fGetFloatv(pname, &f); return JS::DoubleValue(f); } else { - ErrorInvalidEnumInfo("getParameter: parameter", pname); - return JS::NullValue(); + break; } + } case LOCAL_GL_DEPTH_CLEAR_VALUE: case LOCAL_GL_LINE_WIDTH: case LOCAL_GL_POLYGON_OFFSET_FACTOR: case LOCAL_GL_POLYGON_OFFSET_UNITS: - case LOCAL_GL_SAMPLE_COVERAGE_VALUE: - { + case LOCAL_GL_SAMPLE_COVERAGE_VALUE: { GLfloat f = 0.f; gl->fGetFloatv(pname, &f); return JS::DoubleValue(f); @@ -352,8 +331,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) case LOCAL_GL_POLYGON_OFFSET_FILL: case LOCAL_GL_SCISSOR_TEST: case LOCAL_GL_SAMPLE_COVERAGE_INVERT: - case LOCAL_GL_DEPTH_WRITEMASK: - { + case LOCAL_GL_DEPTH_WRITEMASK: { realGLboolean b = 0; gl->fGetBooleanv(pname, &b); return JS::BooleanValue(bool(b)); @@ -369,13 +347,13 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) case UNPACK_COLORSPACE_CONVERSION_WEBGL: return JS::NumberValue(uint32_t(mPixelStoreColorspaceConversion)); - // + //////////////////////////////// // Complex values - // - case LOCAL_GL_DEPTH_RANGE: // 2 floats - case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: // 2 floats - case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: // 2 floats - { + + // 2 floats + case LOCAL_GL_DEPTH_RANGE: + case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: + case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: { GLfloat fv[2] = { 0 }; gl->fGetFloatv(pname, fv); JSObject* obj = Float32Array::Create(cx, this, 2, fv); @@ -385,9 +363,9 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return JS::ObjectOrNullValue(obj); } - case LOCAL_GL_COLOR_CLEAR_VALUE: // 4 floats - case LOCAL_GL_BLEND_COLOR: // 4 floats - { + // 4 floats + case LOCAL_GL_COLOR_CLEAR_VALUE: + case LOCAL_GL_BLEND_COLOR: { GLfloat fv[4] = { 0 }; gl->fGetFloatv(pname, fv); JSObject* obj = Float32Array::Create(cx, this, 4, fv); @@ -397,8 +375,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return JS::ObjectOrNullValue(obj); } - case LOCAL_GL_MAX_VIEWPORT_DIMS: // 2 ints - { + // 2 ints + case LOCAL_GL_MAX_VIEWPORT_DIMS: { GLint iv[2] = { 0 }; gl->fGetIntegerv(pname, iv); JSObject* obj = Int32Array::Create(cx, this, 2, iv); @@ -408,9 +386,9 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return JS::ObjectOrNullValue(obj); } - case LOCAL_GL_SCISSOR_BOX: // 4 ints - case LOCAL_GL_VIEWPORT: // 4 ints - { + // 4 ints + case LOCAL_GL_SCISSOR_BOX: + case LOCAL_GL_VIEWPORT: { GLint iv[4] = { 0 }; gl->fGetIntegerv(pname, iv); JSObject* obj = Int32Array::Create(cx, this, 4, iv); @@ -420,8 +398,8 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return JS::ObjectOrNullValue(obj); } - case LOCAL_GL_COLOR_WRITEMASK: // 4 bools - { + // 4 bools + case LOCAL_GL_COLOR_WRITEMASK: { realGLboolean gl_bv[4] = { 0 }; gl->fGetBooleanv(pname, gl_bv); bool vals[4] = { bool(gl_bv[0]), bool(gl_bv[1]), @@ -433,53 +411,46 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return arr; } - case LOCAL_GL_ARRAY_BUFFER_BINDING: - { + case LOCAL_GL_ARRAY_BUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundArrayBuffer.get(), rv); } - case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: - { + case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: { if (!IsWebGL2()) { break; } return WebGLObjectAsJSValue(cx, mBoundTransformFeedbackBuffer.get(), rv); } - case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING: - { + case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundVertexArray->mBoundElementArrayBuffer.get(), rv); } - case LOCAL_GL_RENDERBUFFER_BINDING: - { + case LOCAL_GL_RENDERBUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundRenderbuffer.get(), rv); } - case LOCAL_GL_FRAMEBUFFER_BINDING: - { + case LOCAL_GL_FRAMEBUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundFramebuffer.get(), rv); } - case LOCAL_GL_CURRENT_PROGRAM: - { + case LOCAL_GL_CURRENT_PROGRAM: { return WebGLObjectAsJSValue(cx, mCurrentProgram.get(), rv); } - case LOCAL_GL_TEXTURE_BINDING_2D: - { + case LOCAL_GL_TEXTURE_BINDING_2D: { return WebGLObjectAsJSValue(cx, mBound2DTextures[mActiveTexture].get(), rv); } - case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: - { + case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: { return WebGLObjectAsJSValue(cx, mBoundCubeMapTextures[mActiveTexture].get(), rv); } default: - ErrorInvalidEnumInfo("getParameter: parameter", pname); + break; } + ErrorInvalidEnumInfo("getParameter: parameter", pname); return JS::NullValue(); } diff --git a/content/canvas/src/WebGLContextValidate.cpp b/content/canvas/src/WebGLContextValidate.cpp index 4e8e33ead581..f051633bc415 100644 --- a/content/canvas/src/WebGLContextValidate.cpp +++ b/content/canvas/src/WebGLContextValidate.cpp @@ -1626,6 +1626,27 @@ WebGLContext::InitAndValidateGL() mDisableFragHighP = true; } + // These are the default values, see 6.2 State tables in the + // OpenGL ES 2.0.25 spec. + mColorWriteMask[0] = 1; + mColorWriteMask[1] = 1; + mColorWriteMask[2] = 1; + mColorWriteMask[3] = 1; + mDepthWriteMask = 1; + mColorClearValue[0] = 0.f; + mColorClearValue[1] = 0.f; + mColorClearValue[2] = 0.f; + mColorClearValue[3] = 0.f; + mDepthClearValue = 1.f; + mStencilClearValue = 0; + mStencilRefFront = 0; + mStencilRefBack = 0; + mStencilValueMaskFront = 0xffffffff; + mStencilValueMaskBack = 0xffffffff; + mStencilWriteMaskFront = 0xffffffff; + mStencilWriteMaskBack = 0xffffffff; + + // Bindings, etc. mActiveTexture = 0; mEmitContextLostErrorOnce = true; mWebGLError = LOCAL_GL_NO_ERROR; diff --git a/content/html/content/public/HTMLCanvasElement.h b/content/html/content/public/HTMLCanvasElement.h index 9c5086e21e64..a10bb69153d2 100644 --- a/content/html/content/public/HTMLCanvasElement.h +++ b/content/html/content/public/HTMLCanvasElement.h @@ -13,7 +13,6 @@ #include "nsSize.h" #include "nsError.h" -#include "nsICanvasElementExternal.h" #include "mozilla/gfx/Rect.h" class nsICanvasRenderingContextInternal; @@ -26,6 +25,9 @@ namespace layers { class CanvasLayer; class LayerManager; } +namespace gfx { +class SourceSurface; +} namespace dom { @@ -34,7 +36,6 @@ class HTMLCanvasPrintState; class PrintCallback; class HTMLCanvasElement MOZ_FINAL : public nsGenericHTMLElement, - public nsICanvasElementExternal, public nsIDOMHTMLCanvasElement { enum { @@ -159,13 +160,7 @@ public: */ bool GetIsOpaque(); - /* - * nsICanvasElementExternal -- for use outside of content/layout - */ - NS_IMETHOD_(nsIntSize) GetSizeExternal() MOZ_OVERRIDE; - NS_IMETHOD RenderContextsExternal(gfxContext *aContext, - GraphicsFilter aFilter, - uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE; + virtual TemporaryRef GetSurfaceSnapshot(bool* aPremultAlpha = nullptr); virtual bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, diff --git a/content/html/content/src/HTMLCanvasElement.cpp b/content/html/content/src/HTMLCanvasElement.cpp index 0d34a306c217..111af0acc850 100644 --- a/content/html/content/src/HTMLCanvasElement.cpp +++ b/content/html/content/src/HTMLCanvasElement.cpp @@ -38,6 +38,7 @@ #endif using namespace mozilla::layers; +using namespace mozilla::gfx; NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas) @@ -130,9 +131,7 @@ NS_IMPL_ADDREF_INHERITED(HTMLCanvasElement, Element) NS_IMPL_RELEASE_INHERITED(HTMLCanvasElement, Element) NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement) - NS_INTERFACE_TABLE_INHERITED2(HTMLCanvasElement, - nsIDOMHTMLCanvasElement, - nsICanvasElementExternal) + NS_INTERFACE_TABLE_INHERITED1(HTMLCanvasElement, nsIDOMHTMLCanvasElement) NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement) NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement) @@ -934,19 +933,13 @@ HTMLCanvasElement::MarkContextClean() mCurrentContext->MarkContextClean(); } -NS_IMETHODIMP_(nsIntSize) -HTMLCanvasElement::GetSizeExternal() -{ - return GetWidthHeight(); -} - -NS_IMETHODIMP -HTMLCanvasElement::RenderContextsExternal(gfxContext *aContext, GraphicsFilter aFilter, uint32_t aFlags) +TemporaryRef +HTMLCanvasElement::GetSurfaceSnapshot(bool* aPremultAlpha) { if (!mCurrentContext) - return NS_OK; + return nullptr; - return mCurrentContext->Render(aContext, aFilter, aFlags); + return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha); } } // namespace dom diff --git a/content/html/content/src/HTMLLinkElement.cpp b/content/html/content/src/HTMLLinkElement.cpp index 1420e91838db..030439f3be3e 100644 --- a/content/html/content/src/HTMLLinkElement.cpp +++ b/content/html/content/src/HTMLLinkElement.cpp @@ -196,10 +196,16 @@ HTMLLinkElement::ParseAttribute(int32_t aNamespaceID, const nsAString& aValue, nsAttrValue& aResult) { - if (aNamespaceID == kNameSpaceID_None && - aAttribute == nsGkAtoms::crossorigin) { - ParseCORSValue(aValue, aResult); - return true; + if (aNamespaceID == kNameSpaceID_None) { + if (aAttribute == nsGkAtoms::crossorigin) { + ParseCORSValue(aValue, aResult); + return true; + } + + if (aAttribute == nsGkAtoms::sizes) { + aResult.ParseAtomArray(aValue); + return true; + } } return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, diff --git a/content/html/content/src/HTMLLinkElement.h b/content/html/content/src/HTMLLinkElement.h index 0fc387fdce1c..73507bd7c77c 100644 --- a/content/html/content/src/HTMLLinkElement.h +++ b/content/html/content/src/HTMLLinkElement.h @@ -109,6 +109,10 @@ public: { SetHTMLAttr(nsGkAtoms::hreflang, aHreflang, aRv); } + nsDOMSettableTokenList* Sizes() + { + return GetTokenList(nsGkAtoms::sizes); + } // XPCOM GetType is fine. void SetType(const nsAString& aType, ErrorResult& aRv) { diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 026d87b06364..a82d416caf7e 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -3154,6 +3154,7 @@ static nsIAtom** sPropertiesToTraverseAndUnlink[] = &nsGkAtoms::itemref, &nsGkAtoms::itemprop, &nsGkAtoms::sandbox, + &nsGkAtoms::sizes, nullptr }; diff --git a/content/html/content/test/mochitest.ini b/content/html/content/test/mochitest.ini index 830a73f35d33..c0f589642fde 100644 --- a/content/html/content/test/mochitest.ini +++ b/content/html/content/test/mochitest.ini @@ -448,6 +448,7 @@ skip-if = buildapp == 'b2g' || e10s # b2g(multiple concurrent window.open()s fai [test_imageSrcSet.html] [test_li_attributes_reflection.html] [test_link_attributes_reflection.html] +[test_link_sizes.html] [test_map_attributes_reflection.html] [test_meta_attributes_reflection.html] [test_mod_attributes_reflection.html] diff --git a/content/html/content/test/test_link_sizes.html b/content/html/content/test/test_link_sizes.html new file mode 100644 index 000000000000..b242748886c5 --- /dev/null +++ b/content/html/content/test/test_link_sizes.html @@ -0,0 +1,35 @@ + + + +Test link.sizes attribute + + + + + + +
+
+
+ + diff --git a/content/media/AudioStream.cpp b/content/media/AudioStream.cpp index 541ebc5005fd..39c632d190f3 100644 --- a/content/media/AudioStream.cpp +++ b/content/media/AudioStream.cpp @@ -502,6 +502,7 @@ AudioStream::CheckForStart() NS_IMETHODIMP AudioInitTask::Run() { + MOZ_ASSERT(mThread); if (NS_IsMainThread()) { mThread->Shutdown(); // can't Shutdown from the thread itself, darn mThread = nullptr; diff --git a/content/media/AudioStream.h b/content/media/AudioStream.h index 2b0562c19ce0..c3dfa5b23e1c 100644 --- a/content/media/AudioStream.h +++ b/content/media/AudioStream.h @@ -433,7 +433,12 @@ public: nsresult Dispatch() { - return NS_NewNamedThread("CubebInit", getter_AddRefs(mThread), this); + // Can't add 'this' as the event to run, since mThread may not be set yet + nsresult rv = NS_NewNamedThread("CubebInit", getter_AddRefs(mThread)); + if (NS_SUCCEEDED(rv)) { + rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL); + } + return rv; } protected: diff --git a/content/media/MediaStreamGraph.cpp b/content/media/MediaStreamGraph.cpp index eed6c64f0259..a3ba8c4f8bd1 100644 --- a/content/media/MediaStreamGraph.cpp +++ b/content/media/MediaStreamGraph.cpp @@ -1259,8 +1259,8 @@ MediaStreamGraphImpl::RunThread() UpdateStreamOrder(); } + TrackRate sampleRate; // Find the sampling rate that we need to use for non-realtime graphs. - TrackRate sampleRate = IdealAudioRate(); if (!mRealtime) { for (uint32_t i = 0; i < mStreams.Length(); ++i) { AudioNodeStream* n = mStreams[i]->AsAudioNodeStream(); @@ -1270,6 +1270,8 @@ MediaStreamGraphImpl::RunThread() break; } } + } else { + sampleRate = IdealAudioRate(); } GraphTime endBlockingDecisions = diff --git a/content/media/gstreamer/GStreamerFormatHelper.cpp b/content/media/gstreamer/GStreamerFormatHelper.cpp index a5e5db8fedf7..6557ad4c8bf6 100644 --- a/content/media/gstreamer/GStreamerFormatHelper.cpp +++ b/content/media/gstreamer/GStreamerFormatHelper.cpp @@ -30,10 +30,8 @@ GStreamerFormatHelper* GStreamerFormatHelper::Instance() { } void GStreamerFormatHelper::Shutdown() { - if (gInstance) { - delete gInstance; - gInstance = nullptr; - } + delete gInstance; + gInstance = nullptr; } static char const *const sContainers[6][2] = { diff --git a/content/media/omx/AudioOutput.cpp b/content/media/omx/AudioOutput.cpp index 7a96ddbab478..69f4ac909efb 100644 --- a/content/media/omx/AudioOutput.cpp +++ b/content/media/omx/AudioOutput.cpp @@ -180,10 +180,8 @@ void AudioOutput::Close() AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__)); mTrack.clear(); - if (mCallbackData) { - delete mCallbackData; - mCallbackData = NULL; - } + delete mCallbackData; + mCallbackData = nullptr; } // static diff --git a/content/media/plugins/MediaPluginHost.cpp b/content/media/plugins/MediaPluginHost.cpp index b8c4844ec48d..c8ea4e682635 100644 --- a/content/media/plugins/MediaPluginHost.cpp +++ b/content/media/plugins/MediaPluginHost.cpp @@ -323,10 +323,8 @@ MediaPluginHost *GetMediaPluginHost() void MediaPluginHost::Shutdown() { - if (sMediaPluginHost) { - delete sMediaPluginHost; - sMediaPluginHost = nullptr; - } + delete sMediaPluginHost; + sMediaPluginHost = nullptr; } } // namespace mozilla diff --git a/content/media/webaudio/OscillatorNode.cpp b/content/media/webaudio/OscillatorNode.cpp index 2607407b9d8a..53111d776304 100644 --- a/content/media/webaudio/OscillatorNode.cpp +++ b/content/media/webaudio/OscillatorNode.cpp @@ -386,9 +386,7 @@ public: tableInterpolationFactor); // mPhase runs 0..periodicWaveSize here instead of 0..2*M_PI. mPhase += periodicWaveSize * mFinalFrequency * rate; - if (mPhase >= periodicWaveSize) { - mPhase -= periodicWaveSize; - } + mPhase = fmod(mPhase, periodicWaveSize); // Bilinear interpolation between adjacent samples in each table. uint32_t j1 = floor(mPhase); uint32_t j2 = j1 + 1; diff --git a/content/media/webrtc/MediaEngineWebRTCAudio.cpp b/content/media/webrtc/MediaEngineWebRTCAudio.cpp index dfbf2121336c..a01a20209d23 100644 --- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -468,10 +468,7 @@ MediaEngineWebRTCAudioSource::Shutdown() mVoENetwork->DeRegisterExternalTransport(mChannel); } - if (mNullTransport) { - delete mNullTransport; - } - + delete mNullTransport; return; } @@ -491,9 +488,7 @@ MediaEngineWebRTCAudioSource::Shutdown() mVoENetwork->DeRegisterExternalTransport(mChannel); } - if (mNullTransport) { - delete mNullTransport; - } + delete mNullTransport; mVoEProcessing = nullptr; mVoENetwork = nullptr; diff --git a/dom/base/Console.cpp b/dom/base/Console.cpp index e722bf656dc3..890c76a79047 100644 --- a/dom/base/Console.cpp +++ b/dom/base/Console.cpp @@ -7,6 +7,8 @@ #include "mozilla/dom/ConsoleBinding.h" #include "mozilla/dom/Exceptions.h" +#include "mozilla/dom/ToJSValue.h" +#include "mozilla/Maybe.h" #include "nsCycleCollectionParticipant.h" #include "nsDocument.h" #include "nsDOMNavigationTiming.h" @@ -16,6 +18,7 @@ #include "WorkerPrivate.h" #include "WorkerRunnable.h" #include "xpcprivate.h" +#include "nsContentUtils.h" #include "nsIConsoleAPIStorage.h" #include "nsIDOMWindowUtils.h" @@ -171,7 +174,16 @@ public: nsString mMethodString; nsTArray> mArguments; - Sequence mStack; + + // Stack management is complicated, because we want to do it as + // lazily as possible. Therefore, we have the following behavior: + // 1) mTopStackFrame is initialized whenever we have any JS on the stack + // 2) mReifiedStack is initialized if we're created in a worker. + // 3) mStack is set (possibly to null if there is no JS on the stack) if + // we're created on main thread. + Maybe mTopStackFrame; + Maybe> mReifiedStack; + nsCOMPtr mStack; }; // This class is used to clear any exception at the end of this method. @@ -733,6 +745,58 @@ Console::__noSuchMethod__() // Nothing to do. } +static +nsresult +StackFrameToStackEntry(nsIStackFrame* aStackFrame, + ConsoleStackEntry& aStackEntry, + uint32_t aLanguage) +{ + MOZ_ASSERT(aStackFrame); + + nsresult rv = aStackFrame->GetFilename(aStackEntry.mFilename); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t lineNumber; + rv = aStackFrame->GetLineNumber(&lineNumber); + NS_ENSURE_SUCCESS(rv, rv); + + aStackEntry.mLineNumber = lineNumber; + + rv = aStackFrame->GetName(aStackEntry.mFunctionName); + NS_ENSURE_SUCCESS(rv, rv); + + aStackEntry.mLanguage = aLanguage; + return NS_OK; +} + +static +nsresult +ReifyStack(nsIStackFrame* aStack, nsTArray& aRefiedStack) +{ + nsCOMPtr stack(aStack); + + while (stack) { + uint32_t language; + nsresult rv = stack->GetLanguage(&language); + NS_ENSURE_SUCCESS(rv, rv); + + if (language == nsIProgrammingLanguage::JAVASCRIPT || + language == nsIProgrammingLanguage::JAVASCRIPT2) { + ConsoleStackEntry& data = *aRefiedStack.AppendElement(); + rv = StackFrameToStackEntry(stack, data, language); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr caller; + rv = stack->GetCaller(getter_AddRefs(caller)); + NS_ENSURE_SUCCESS(rv, rv); + + stack.swap(caller); + } + + return NS_OK; +} + // Queue a call to a console method. See the CALL_DELAY constant. void Console::Method(JSContext* aCx, MethodName aMethodName, @@ -797,8 +861,7 @@ Console::Method(JSContext* aCx, MethodName aMethodName, return; } - // nsIStackFrame is not thread-safe so we take what we need and we store in - // an array of ConsoleStackEntry objects. + // Walk up to the first JS stack frame and save it if we find it. do { uint32_t language; nsresult rv = stack->GetLanguage(&language); @@ -809,30 +872,16 @@ Console::Method(JSContext* aCx, MethodName aMethodName, if (language == nsIProgrammingLanguage::JAVASCRIPT || language == nsIProgrammingLanguage::JAVASCRIPT2) { - ConsoleStackEntry& data = *callData->mStack.AppendElement(); - - rv = stack->GetFilename(data.mFilename); + callData->mTopStackFrame.construct(); + nsresult rv = StackFrameToStackEntry(stack, + callData->mTopStackFrame.ref(), + language); if (NS_FAILED(rv)) { Throw(aCx, rv); return; } - int32_t lineNumber; - rv = stack->GetLineNumber(&lineNumber); - if (NS_FAILED(rv)) { - Throw(aCx, rv); - return; - } - - data.mLineNumber = lineNumber; - - rv = stack->GetName(data.mFunctionName); - if (NS_FAILED(rv)) { - Throw(aCx, rv); - return; - } - - data.mLanguage = language; + break; } nsCOMPtr caller; @@ -845,6 +894,19 @@ Console::Method(JSContext* aCx, MethodName aMethodName, stack.swap(caller); } while (stack); + if (NS_IsMainThread()) { + callData->mStack = stack; + } else { + // nsIStackFrame is not threadsafe, so we need to snapshot it now, + // before we post our runnable to the main thread. + callData->mReifiedStack.construct(); + nsresult rv = ReifyStack(stack, callData->mReifiedStack.ref()); + if (NS_WARN_IF(NS_FAILED(rv))) { + Throw(aCx, rv); + return; + } + } + // Monotonic timer for 'time' and 'timeEnd' if ((aMethodName == MethodTime || aMethodName == MethodTimeEnd) && mWindow) { nsGlobalWindow *win = static_cast(mWindow.get()); @@ -918,14 +980,60 @@ Console::Notify(nsITimer *timer) return NS_OK; } +// We store information to lazily compute the stack in the reserved slots of +// LazyStackGetter. The first slot always stores a JS object: it's either the +// JS wrapper of the nsIStackFrame or the actual reified stack representation. +// The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't +// reified the stack yet, or an UndefinedValue() otherwise. +enum { + SLOT_STACKOBJ, + SLOT_RAW_STACK +}; + +bool +LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) +{ + JS::CallArgs args = CallArgsFromVp(aArgc, aVp); + JS::Rooted callee(aCx, &args.callee()); + + JS::Value v = js::GetFunctionNativeReserved(&args.callee(), SLOT_RAW_STACK); + if (v.isUndefined()) { + // Already reified. + args.rval().set(js::GetFunctionNativeReserved(callee, SLOT_STACKOBJ)); + return true; + } + + nsIStackFrame* stack = reinterpret_cast(v.toPrivate()); + nsTArray reifiedStack; + nsresult rv = ReifyStack(stack, reifiedStack); + if (NS_FAILED(rv)) { + Throw(aCx, rv); + return false; + } + + JS::Rooted stackVal(aCx); + if (!ToJSValue(aCx, reifiedStack, &stackVal)) { + return false; + } + + MOZ_ASSERT(stackVal.isObject()); + + js::SetFunctionNativeReserved(callee, SLOT_STACKOBJ, stackVal); + js::SetFunctionNativeReserved(callee, SLOT_RAW_STACK, JS::UndefinedValue()); + + args.rval().set(stackVal); + return true; +} + void Console::ProcessCallData(ConsoleCallData* aData) { MOZ_ASSERT(aData); + MOZ_ASSERT(NS_IsMainThread()); ConsoleStackEntry frame; - if (!aData->mStack.IsEmpty()) { - frame = aData->mStack[0]; + if (!aData->mTopStackFrame.empty()) { + frame = aData->mTopStackFrame.ref(); } AutoSafeJSContext cx; @@ -971,14 +1079,9 @@ Console::ProcessCallData(ConsoleCallData* aData) ArgumentsToValueList(aData->mArguments, event.mArguments.Value()); } - if (ShouldIncludeStackrace(aData->mMethodName)) { - event.mStacktrace.Construct(); - event.mStacktrace.Value().SwapElements(aData->mStack); - } - - else if (aData->mMethodName == MethodGroup || - aData->mMethodName == MethodGroupCollapsed || - aData->mMethodName == MethodGroupEnd) { + if (aData->mMethodName == MethodGroup || + aData->mMethodName == MethodGroupCollapsed || + aData->mMethodName == MethodGroupEnd) { ComposeGroupName(cx, aData->mArguments, event.mGroupName); } @@ -994,6 +1097,18 @@ Console::ProcessCallData(ConsoleCallData* aData) event.mCounter = IncreaseCounter(cx, frame, aData->mArguments); } + // We want to create a console event object and pass it to our + // nsIConsoleAPIStorage implementation. We want to define some accessor + // properties on this object, and those will need to keep an nsIStackFrame + // alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And + // further, passing untrusted objects to system code is likely to run afoul of + // Object Xrays. So we want to wrap in a system-principal scope here. But + // which one? We could cheat and try to get the underlying JSObject* of + // mStorage, but that's a bit fragile. Instead, we just use the junk scope, + // with explicit permission from the XPConnect module owner. If you're + // tempted to do that anywhere else, talk to said module owner first. + JSAutoCompartment ac2(cx, xpc::GetJunkScope()); + JS::Rooted eventValue(cx); if (!event.ToObject(cx, &eventValue)) { Throw(cx, NS_ERROR_FAILURE); @@ -1007,6 +1122,51 @@ Console::ProcessCallData(ConsoleCallData* aData) return; } + if (ShouldIncludeStackrace(aData->mMethodName)) { + // Now define the "stacktrace" property on eventObj. There are two cases + // here. Either we came from a worker and have a reified stack, or we want + // to define a getter that will lazily reify the stack. + if (!aData->mReifiedStack.empty()) { + JS::Rooted stacktrace(cx); + if (!ToJSValue(cx, aData->mReifiedStack.ref(), &stacktrace) || + !JS_DefineProperty(cx, eventObj, "stacktrace", stacktrace, + JSPROP_ENUMERATE)) { + return; + } + } else { + JSFunction* fun = js::NewFunctionWithReserved(cx, LazyStackGetter, 0, 0, + eventObj, "stacktrace"); + if (!fun) { + return; + } + + JS::Rooted funObj(cx, JS_GetFunctionObject(fun)); + + // We want to store our stack in the function and have it stay alive. But + // we also need sane access to the C++ nsIStackFrame. So store both a JS + // wrapper and the raw pointer: the former will keep the latter alive. + JS::Rooted stackVal(cx); + nsresult rv = nsContentUtils::WrapNative(cx, aData->mStack, + &stackVal); + if (NS_FAILED(rv)) { + return; + } + + js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal); + js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK, + JS::PrivateValue(aData->mStack.get())); + + if (!JS_DefineProperty(cx, eventObj, "stacktrace", + JS::UndefinedHandleValue, + JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER | + JSPROP_SETTER, + JS_DATA_TO_FUNC_PTR(JSPropertyOp, funObj.get()), + nullptr)) { + return; + } + } + } + if (!mStorage) { mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1"); } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index fa4c3af418fc..2bec7ac3e87b 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -12459,82 +12459,38 @@ nsGlobalWindow::GetScrollFrame() } nsresult -nsGlobalWindow::BuildURIfromBase(const char *aURL, nsIURI **aBuiltURI, - JSContext **aCXused) +nsGlobalWindow::SecurityCheckURL(const char *aURL) { - nsIScriptContext *scx = GetContextInternal(); - JSContext *cx = nullptr; - - *aBuiltURI = nullptr; - if (aCXused) - *aCXused = nullptr; - - // get JSContext - NS_ASSERTION(scx, "opening window missing its context"); - NS_ASSERTION(mDoc, "opening window missing its document"); - if (!scx || !mDoc) - return NS_ERROR_FAILURE; - - nsCOMPtr chrome_win = do_QueryObject(this); - - if (nsContentUtils::IsCallerChrome() && !chrome_win) { - // If open() is called from chrome on a non-chrome window, we'll - // use the context from the window on which open() is being called - // to prevent giving chrome priveleges to new windows opened in - // such a way. This also makes us get the appropriate base URI for - // the below URI resolution code. - - cx = scx->GetNativeContext(); - } else { - // get the JSContext from the call stack - cx = nsContentUtils::GetCurrentJSContext(); - } - - /* resolve the URI, which could be relative to the calling window - (note the algorithm to get the base URI should match the one - used to actually kick off the load in nsWindowWatcher.cpp). */ - nsAutoCString charset(NS_LITERAL_CSTRING("UTF-8")); // default to utf-8 - nsIURI* baseURI = nullptr; - nsCOMPtr uriToLoad; nsCOMPtr sourceWindow; - - if (cx) { - nsIScriptContext *scriptcx = nsJSUtils::GetDynamicScriptContext(cx); - if (scriptcx) - sourceWindow = do_QueryInterface(scriptcx->GetGlobalObject()); + JSContext* topCx = nsContentUtils::GetCurrentJSContext(); + if (topCx) { + sourceWindow = do_QueryInterface(nsJSUtils::GetDynamicScriptGlobal(topCx)); } - if (!sourceWindow) { sourceWindow = this; } + AutoJSContext cx; + nsGlobalWindow* sourceWin = static_cast(sourceWindow.get()); + JSAutoCompartment ac(cx, sourceWin->GetGlobalJSObject()); + // Resolve the baseURI, which could be relative to the calling window. + // + // Note the algorithm to get the base URI should match the one + // used to actually kick off the load in nsWindowWatcher.cpp. nsCOMPtr doc = sourceWindow->GetDoc(); + nsIURI* baseURI = nullptr; + nsAutoCString charset(NS_LITERAL_CSTRING("UTF-8")); // default to utf-8 if (doc) { baseURI = doc->GetDocBaseURI(); charset = doc->GetDocumentCharacterSet(); } - - if (aCXused) - *aCXused = cx; - return NS_NewURI(aBuiltURI, nsDependentCString(aURL), charset.get(), baseURI); -} - -nsresult -nsGlobalWindow::SecurityCheckURL(const char *aURL) -{ - JSContext *cxUsed; nsCOMPtr uri; - - if (NS_FAILED(BuildURIfromBase(aURL, getter_AddRefs(uri), &cxUsed))) { - return NS_ERROR_FAILURE; + nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aURL), + charset.get(), baseURI); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; } - if (!cxUsed) { - return NS_OK; - } - - AutoPushJSContext cx(cxUsed); - if (NS_FAILED(nsContentUtils::GetSecurityManager()-> CheckLoadURIFromScript(cx, uri))) { return NS_ERROR_FAILURE; diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 8f541acc2a7d..fca53e817dbb 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -1198,8 +1198,7 @@ protected: already_AddRefed GetTreeOwnerWindow(); already_AddRefed GetWebBrowserChrome(); nsresult SecurityCheckURL(const char *aURL); - nsresult BuildURIfromBase(const char *aURL, nsIURI **aBuiltURI, - JSContext **aCXused); + bool PopupWhitelisted(); PopupControlState RevisePopupAbuseLevel(PopupControlState); void FireAbuseEvents(bool aBlocked, bool aWindow, diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp index fd0f2c3b9a7e..2bb35cd6ae7f 100644 --- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -131,28 +131,7 @@ Throw(JSContext* aCx, nsresult aRv, const char* aMessage) } } - nsRefPtr finalException; - - // Do we use DOM exceptions for this error code? - switch (NS_ERROR_GET_MODULE(aRv)) { - case NS_ERROR_MODULE_DOM: - case NS_ERROR_MODULE_SVG: - case NS_ERROR_MODULE_DOM_XPATH: - case NS_ERROR_MODULE_DOM_INDEXEDDB: - case NS_ERROR_MODULE_DOM_FILEHANDLE: - finalException = DOMException::Create(aRv); - break; - - default: - break; - } - - // If not, use the default. - if (!finalException) { - // aMessage can be null. - finalException = new Exception(nsCString(aMessage), aRv, - EmptyCString(), nullptr, nullptr); - } + nsRefPtr finalException = CreateException(aCx, aRv, aMessage); MOZ_ASSERT(finalException); if (!ThrowExceptionObject(aCx, finalException)) { @@ -164,6 +143,29 @@ Throw(JSContext* aCx, nsresult aRv, const char* aMessage) return false; } +already_AddRefed +CreateException(JSContext* aCx, nsresult aRv, const char* aMessage) +{ + // Do we use DOM exceptions for this error code? + switch (NS_ERROR_GET_MODULE(aRv)) { + case NS_ERROR_MODULE_DOM: + case NS_ERROR_MODULE_SVG: + case NS_ERROR_MODULE_DOM_XPATH: + case NS_ERROR_MODULE_DOM_INDEXEDDB: + case NS_ERROR_MODULE_DOM_FILEHANDLE: + return DOMException::Create(aRv); + default: + break; + } + + // If not, use the default. + // aMessage can be null, so we can't use nsDependentCString on it. + nsRefPtr exception = + new Exception(nsCString(aMessage), aRv, + EmptyCString(), nullptr, nullptr); + return exception.forget(); +} + already_AddRefed GetCurrentJSStack() { diff --git a/dom/bindings/Exceptions.h b/dom/bindings/Exceptions.h index cd24d784b628..a77572a5b9ba 100644 --- a/dom/bindings/Exceptions.h +++ b/dom/bindings/Exceptions.h @@ -30,6 +30,11 @@ ThrowExceptionObject(JSContext* aCx, Exception* aException); bool ThrowExceptionObject(JSContext* aCx, nsIException* aException); +// Create an exception object for the given nsresult and message but +// don't set it pending on aCx. This never returns null. +already_AddRefed +CreateException(JSContext* aCx, nsresult aRv, const char* aMessage = nullptr); + already_AddRefed GetCurrentJSStack(); diff --git a/dom/bindings/ToJSValue.cpp b/dom/bindings/ToJSValue.cpp index 5ffded8facc6..42122716565f 100644 --- a/dom/bindings/ToJSValue.cpp +++ b/dom/bindings/ToJSValue.cpp @@ -5,6 +5,8 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/ToJSValue.h" +#include "mozilla/dom/DOMException.h" +#include "mozilla/dom/Exceptions.h" #include "nsAString.h" #include "nsContentUtils.h" #include "nsStringBuffer.h" @@ -49,5 +51,14 @@ ISupportsToJSValue(JSContext* aCx, } // namespace tojsvalue_detail +bool +ToJSValue(JSContext* aCx, + nsresult aArgument, + JS::MutableHandle aValue) +{ + nsRefPtr exception = CreateException(aCx, aArgument); + return ToJSValue(aCx, exception, aValue); +} + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h index fdbf52f78d4a..9056a423c993 100644 --- a/dom/bindings/ToJSValue.h +++ b/dom/bindings/ToJSValue.h @@ -54,6 +54,14 @@ ToJSValue(JSContext* aCx, return true; } +// The uint32_t version is disabled for now because on the super-old b2g +// compiler nsresult and uint32_t are the same type. If someone needs this at +// some point we'll need to figure out how to make it work (e.g. by switching to +// traits structs and using the trick IPC's ParamTraits uses, where a traits +// struct templated on the type inherits from a base traits struct of some sort, +// templated on the same type, or something). Maybe b2g will update to a modern +// compiler before that happens.... +#if 0 inline bool ToJSValue(JSContext* aCx, uint32_t aArgument, @@ -65,6 +73,7 @@ ToJSValue(JSContext* aCx, aValue.setNumber(aArgument); return true; } +#endif inline bool ToJSValue(JSContext* aCx, @@ -203,6 +212,22 @@ ToJSValue(JSContext* aCx, return aArgument.ToObject(aCx, aValue); } +// Accept existing JS values (which may not be same-compartment with us +inline bool +ToJSValue(JSContext* aCx, JS::Handle aArgument, + JS::MutableHandle aValue) +{ + aValue.set(aArgument); + return MaybeWrapValue(aCx, aValue); +} + +// Accept nsresult, for use in rejections, and create an XPCOM +// exception object representing that nsresult. +bool +ToJSValue(JSContext* aCx, + nsresult aArgument, + JS::MutableHandle aValue); + // Accept arrays of other things we accept template bool diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index e99acd76806f..d23d926e4ccd 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -18,6 +18,8 @@ #include "mozilla/docshell/OfflineCacheUpdateChild.h" #include "mozilla/ipc/DocumentRendererChild.h" #include "mozilla/ipc/FileDescriptorUtils.h" +#include "mozilla/layers/ActiveElementManager.h" +#include "mozilla/layers/APZCCallbackHelper.h" #include "mozilla/layers/AsyncPanZoomController.h" #include "mozilla/layers/CompositorChild.h" #include "mozilla/layers/ImageBridgeChild.h" @@ -68,13 +70,11 @@ #include "StructuredCloneUtils.h" #include "nsViewportInfo.h" #include "JavaScriptChild.h" -#include "APZCCallbackHelper.h" #include "nsILoadContext.h" #include "ipc/nsGUIEventIPC.h" #include "mozilla/gfx/Matrix.h" #include "UnitTransforms.h" #include "ClientLayerManager.h" -#include "ActiveElementManager.h" #include "nsColorPickerProxy.h" diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 13e3ebd94cd5..fdade87b0125 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -42,7 +42,7 @@ namespace layout { class RenderFrameChild; } -namespace widget { +namespace layers { class ActiveElementManager; } @@ -232,7 +232,7 @@ class TabChild : public PBrowserChild, typedef mozilla::dom::ClonedMessageData ClonedMessageData; typedef mozilla::layout::RenderFrameChild RenderFrameChild; typedef mozilla::layout::ScrollingBehavior ScrollingBehavior; - typedef mozilla::widget::ActiveElementManager ActiveElementManager; + typedef mozilla::layers::ActiveElementManager ActiveElementManager; public: /** diff --git a/dom/media/tests/mochitest/pc.js b/dom/media/tests/mochitest/pc.js index 63c31ada67cb..757899f086bc 100644 --- a/dom/media/tests/mochitest/pc.js +++ b/dom/media/tests/mochitest/pc.js @@ -493,12 +493,21 @@ function PeerConnectionTest(options) { PeerConnectionTest.prototype.close = function PCT_close(onSuccess) { info("Closing peer connections. Connection state=" + this.connected); + function signalingstatechangeClose(state) { + info("'onsignalingstatechange' event '" + state + "' received"); + is(state, "closed", "onsignalingstatechange event is closed"); + } + // There is no onclose event for the remote peer existent yet. So close it // side-by-side with the local peer. - if (this.pcLocal) + if (this.pcLocal) { + this.pcLocal.onsignalingstatechange = signalingstatechangeClose; this.pcLocal.close(); - if (this.pcRemote) + } + if (this.pcRemote) { + this.pcRemote.onsignalingstatechange = signalingstatechangeClose; this.pcRemote.close(); + } this.connected = false; onSuccess(); @@ -585,8 +594,9 @@ function PCT_setLocalDescription(peer, desc, onSuccess) { } } - peer.onsignalingstatechange = function () { - info(peer + ": 'onsignalingstatechange' event registered, signalingState: " + peer.signalingState); + peer.onsignalingstatechange = function (state) { + //info(peer + ": 'onsignalingstatechange' event registered, signalingState: " + peer.signalingState); + info(peer + ": 'onsignalingstatechange' event '" + state + "' received"); eventFired = true; check_next_test(); @@ -646,8 +656,8 @@ function PCT_setRemoteDescription(peer, desc, onSuccess) { } } - peer.onsignalingstatechange = function () { - info(peer + ": 'onsignalingstatechange' event registered, signalingState: " + peer.signalingState); + peer.onsignalingstatechange = function (state) { + info(peer + ": 'onsignalingstatechange' event '" + state + "' received"); eventFired = true; check_next_test(); @@ -1168,7 +1178,9 @@ function PeerConnectionWrapper(label, configuration) { this._pc.onsignalingstatechange = function (aEvent) { info(self + ": 'onsignalingstatechange' event fired"); - self.onsignalingstatechange(); + // this calls the eventhandler only once and then overwrites it with the + // default unexpectedEvent handler + self.onsignalingstatechange(aEvent); self.onsignalingstatechange = unexpectedEventAndFinish(self, 'onsignalingstatechange'); }; } diff --git a/dom/media/tests/mochitest/test_peerConnection_bug827843.html b/dom/media/tests/mochitest/test_peerConnection_bug827843.html index ea67db710a30..04e2d832ff5c 100644 --- a/dom/media/tests/mochitest/test_peerConnection_bug827843.html +++ b/dom/media/tests/mochitest/test_peerConnection_bug827843.html @@ -56,6 +56,11 @@ var description; var exception = null; + // handle the event which the close() triggers + test.pcLocal.onsignalingstatechange = function (state) { + is(state, "closed", "Received expected onsignalingstatechange event 'closed'"); + } + test.pcLocal.close(); try { description = test.pcLocal.localDescription; } catch (e) { exception = e; } @@ -66,6 +71,11 @@ ok(exception, "Attempt to access remoteDescription of pcLocal after close throws exception"); exception = null; + // handle the event which the close() triggers + test.pcRemote.onsignalingstatechange = function (state) { + is(state, "closed", "Received expected onsignalingstatechange event 'closed'"); + } + test.pcRemote.close(); try { description = test.pcRemote.localDescription; } catch (e) { exception = e; } diff --git a/dom/media/tests/mochitest/test_peerConnection_bug835370.html b/dom/media/tests/mochitest/test_peerConnection_bug835370.html index 9242bed26190..b375d0c71e2f 100644 --- a/dom/media/tests/mochitest/test_peerConnection_bug835370.html +++ b/dom/media/tests/mochitest/test_peerConnection_bug835370.html @@ -49,6 +49,8 @@ exception = null; try { pconnects.createOffer(step1, failed, { mandatory: { OfferToReceiveVideo: false, OfferToReceiveAudio: true, MozDontOfferDataChannel: true}, optional: [{ VoiceActivityDetection: true }, { FooBar: "42" }] }); } catch (e) { exception = e; } ok(!exception, "createOffer(step1, failed, { mandatory: { OfferToReceiveVideo: false, OfferToReceiveAudio: true, MozDontOfferDataChannel: true}, optional: [{ VoiceActivityDetection: true }, { FooBar: \"42\" }] }) succeeds"); + pconnect.close(); + pconnects.close(); pconnect = null; pconnects = null; SimpleTest.finish(); diff --git a/dom/promise/Promise.h b/dom/promise/Promise.h index e46822ca4467..a22853d58b98 100644 --- a/dom/promise/Promise.h +++ b/dom/promise/Promise.h @@ -78,15 +78,17 @@ public: JS::Handle aValue); // Helpers for using Promise from C++. - // Most DOM objects are handled already. To add a new type T, such as ints, - // or dictionaries, add a ToJSValue overload in ToJSValue.h. + // Most DOM objects are handled already. To add a new type T, add a + // ToJSValue overload in ToJSValue.h. + // aArg is a const reference so we can pass rvalues like integer constants template - void MaybeResolve(T& aArg) { + void MaybeResolve(const T& aArg) { MaybeSomething(aArg, &Promise::MaybeResolve); } + // aArg is a const reference so we can pass rvalues like NS_ERROR_* template - void MaybeReject(T& aArg) { + void MaybeReject(const T& aArg) { MaybeSomething(aArg, &Promise::MaybeReject); } diff --git a/dom/webidl/Console.webidl b/dom/webidl/Console.webidl index c069971e44ce..bdaac9c8cf81 100644 --- a/dom/webidl/Console.webidl +++ b/dom/webidl/Console.webidl @@ -47,7 +47,12 @@ dictionary ConsoleEvent { sequence styles; boolean private = false; - sequence stacktrace; + // stacktrace is handled via a getter in some cases so we can construct it + // lazily. Note that we're not making this whole thing an interface because + // consumers expect to see own properties on it, which would mean making the + // props unforgeable, which means lots of JSFunction allocations. Maybe we + // should fix those consumers, of course.... + // sequence stacktrace; DOMString groupName = ""; any timer = null; any counter = null; diff --git a/dom/webidl/HTMLLinkElement.webidl b/dom/webidl/HTMLLinkElement.webidl index 3f90d61330bc..30741c942b3c 100644 --- a/dom/webidl/HTMLLinkElement.webidl +++ b/dom/webidl/HTMLLinkElement.webidl @@ -28,8 +28,7 @@ interface HTMLLinkElement : HTMLElement { attribute DOMString hreflang; [SetterThrows, Pure] attribute DOMString type; -// Not supported yet: -// [PutForwards=value] readonly attribute DOMSettableTokenList sizes; + [PutForwards=value] readonly attribute DOMSettableTokenList sizes; }; HTMLLinkElement implements LinkStyle; diff --git a/gfx/layers/ipc/GeckoContentController.h b/gfx/layers/apz/public/GeckoContentController.h similarity index 100% rename from gfx/layers/ipc/GeckoContentController.h rename to gfx/layers/apz/public/GeckoContentController.h diff --git a/gfx/layers/composite/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp similarity index 99% rename from gfx/layers/composite/APZCTreeManager.cpp rename to gfx/layers/apz/src/APZCTreeManager.cpp index f14de3171d8c..8710769659cb 100644 --- a/gfx/layers/composite/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -4,7 +4,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "APZCTreeManager.h" -#include "AsyncCompositionManager.h" // for ViewTransform #include "Compositor.h" // for Compositor #include "CompositorParent.h" // for CompositorParent, etc #include "InputData.h" // for InputData, etc @@ -12,6 +11,7 @@ #include "gfx3DMatrix.h" // for gfx3DMatrix #include "mozilla/dom/Touch.h" // for Touch #include "mozilla/gfx/Point.h" // for Point +#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform #include "mozilla/layers/AsyncPanZoomController.h" #include "mozilla/MouseEvents.h" #include "mozilla/mozalloc.h" // for operator new diff --git a/gfx/layers/composite/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h similarity index 100% rename from gfx/layers/composite/APZCTreeManager.h rename to gfx/layers/apz/src/APZCTreeManager.h diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp similarity index 99% rename from gfx/layers/ipc/AsyncPanZoomController.cpp rename to gfx/layers/apz/src/AsyncPanZoomController.cpp index 7b976564cbc8..c8af51ad5ded 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -14,7 +14,6 @@ #include "FrameMetrics.h" // for FrameMetrics, etc #include "GestureEventListener.h" // for GestureEventListener #include "InputData.h" // for MultiTouchInput, etc -#include "LayerTransactionParent.h" // for LayerTransactionParent #include "Units.h" // for CSSRect, CSSPoint, etc #include "UnitTransforms.h" // for TransformTo #include "base/message_loop.h" // for MessageLoop @@ -40,7 +39,7 @@ #include "mozilla/layers/APZCTreeManager.h" // for ScrollableLayerGuid #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform #include "mozilla/layers/Axis.h" // for AxisX, AxisY, Axis, etc -#include "mozilla/layers/GeckoContentController.h" +#include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent #include "mozilla/layers/PCompositorParent.h" // for PCompositorParent #include "mozilla/layers/TaskThrottler.h" // for TaskThrottler #include "mozilla/mozalloc.h" // for operator new, etc @@ -619,9 +618,10 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent mX.StartTouch(point.x); mY.StartTouch(point.y); APZCTreeManager* treeManagerLocal = mTreeManager; - if (treeManagerLocal) { + nsRefPtr controller = GetGeckoContentController(); + if (treeManagerLocal && controller) { bool touchCanBePan = treeManagerLocal->CanBePanned(this); - mGeckoContentController->NotifyAPZStateChange( + controller->NotifyAPZStateChange( GetGuid(), APZStateChange::StartTouch, touchCanBePan); } SetState(TOUCHING); @@ -962,8 +962,10 @@ nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aP } void AsyncPanZoomController::OnTouchEndOrCancel() { - mGeckoContentController->NotifyAPZStateChange( - GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred); + if (nsRefPtr controller = GetGeckoContentController()) { + controller->NotifyAPZStateChange( + GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred); + } } nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) { @@ -1108,7 +1110,9 @@ nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent } if (IsPanningState(mState)) { - mGeckoContentController->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning); + if (nsRefPtr controller = GetGeckoContentController()) { + controller->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning); + } return nsEventStatus_eConsumeNoDefault; } // Don't consume an event that didn't trigger a panning. @@ -2007,12 +2011,12 @@ void AsyncPanZoomController::SetState(PanZoomState aNewState) { mState = aNewState; } - if (mGeckoContentController) { + if (nsRefPtr controller = GetGeckoContentController()) { if (!IsTransformingState(oldState) && IsTransformingState(aNewState)) { - mGeckoContentController->NotifyAPZStateChange( + controller->NotifyAPZStateChange( GetGuid(), APZStateChange::TransformBegin); } else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) { - mGeckoContentController->NotifyAPZStateChange( + controller->NotifyAPZStateChange( GetGuid(), APZStateChange::TransformEnd); } } diff --git a/gfx/layers/ipc/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h similarity index 99% rename from gfx/layers/ipc/AsyncPanZoomController.h rename to gfx/layers/apz/src/AsyncPanZoomController.h index c44a5d0a8552..996f8abd700f 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -8,7 +8,7 @@ #define mozilla_layers_AsyncPanZoomController_h #include "CrossProcessMutex.h" -#include "GeckoContentController.h" +#include "mozilla/layers/GeckoContentController.h" #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" #include "mozilla/Monitor.h" diff --git a/gfx/layers/ipc/Axis.cpp b/gfx/layers/apz/src/Axis.cpp similarity index 100% rename from gfx/layers/ipc/Axis.cpp rename to gfx/layers/apz/src/Axis.cpp diff --git a/gfx/layers/ipc/Axis.h b/gfx/layers/apz/src/Axis.h similarity index 100% rename from gfx/layers/ipc/Axis.h rename to gfx/layers/apz/src/Axis.h diff --git a/gfx/layers/ipc/GestureEventListener.cpp b/gfx/layers/apz/src/GestureEventListener.cpp similarity index 100% rename from gfx/layers/ipc/GestureEventListener.cpp rename to gfx/layers/apz/src/GestureEventListener.cpp diff --git a/gfx/layers/ipc/GestureEventListener.h b/gfx/layers/apz/src/GestureEventListener.h similarity index 100% rename from gfx/layers/ipc/GestureEventListener.h rename to gfx/layers/apz/src/GestureEventListener.h diff --git a/gfx/layers/ipc/TaskThrottler.cpp b/gfx/layers/apz/src/TaskThrottler.cpp similarity index 100% rename from gfx/layers/ipc/TaskThrottler.cpp rename to gfx/layers/apz/src/TaskThrottler.cpp diff --git a/gfx/layers/ipc/TaskThrottler.h b/gfx/layers/apz/src/TaskThrottler.h similarity index 100% rename from gfx/layers/ipc/TaskThrottler.h rename to gfx/layers/apz/src/TaskThrottler.h diff --git a/widget/xpwidgets/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp similarity index 99% rename from widget/xpwidgets/APZCCallbackHelper.cpp rename to gfx/layers/apz/util/APZCCallbackHelper.cpp index 2d37c1b263a5..a4b675d02468 100644 --- a/widget/xpwidgets/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -12,7 +12,7 @@ #include "nsIInterfaceRequestorUtils.h" namespace mozilla { -namespace widget { +namespace layers { bool APZCCallbackHelper::HasValidPresShellId(nsIDOMWindowUtils* aUtils, diff --git a/widget/xpwidgets/APZCCallbackHelper.h b/gfx/layers/apz/util/APZCCallbackHelper.h similarity index 97% rename from widget/xpwidgets/APZCCallbackHelper.h rename to gfx/layers/apz/util/APZCCallbackHelper.h index 505a3c11efd3..e5e0668c2841 100644 --- a/widget/xpwidgets/APZCCallbackHelper.h +++ b/gfx/layers/apz/util/APZCCallbackHelper.h @@ -3,8 +3,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef __mozilla_widget_APZCCallbackHelper_h__ -#define __mozilla_widget_APZCCallbackHelper_h__ +#ifndef mozilla_layers_APZCCallbackHelper_h +#define mozilla_layers_APZCCallbackHelper_h #include "FrameMetrics.h" #include "nsIContent.h" @@ -12,7 +12,7 @@ #include "nsIDOMWindowUtils.h" namespace mozilla { -namespace widget { +namespace layers { /* This class contains some helper methods that facilitate implementing the GeckoContentController callback interface required by the AsyncPanZoomController. @@ -100,4 +100,4 @@ public: } } -#endif /*__mozilla_widget_APZCCallbackHelper_h__ */ +#endif /* mozilla_layers_APZCCallbackHelper_h */ diff --git a/widget/xpwidgets/ActiveElementManager.cpp b/gfx/layers/apz/util/ActiveElementManager.cpp similarity index 99% rename from widget/xpwidgets/ActiveElementManager.cpp rename to gfx/layers/apz/util/ActiveElementManager.cpp index b9c1900ad7a7..6edb27c918db 100644 --- a/widget/xpwidgets/ActiveElementManager.cpp +++ b/gfx/layers/apz/util/ActiveElementManager.cpp @@ -15,7 +15,7 @@ #include "base/task.h" namespace mozilla { -namespace widget { +namespace layers { static int32_t sActivationDelayMs = 100; static bool sActivationDelayMsSet = false; @@ -148,4 +148,4 @@ ActiveElementManager::CancelTask() } } -} \ No newline at end of file +} diff --git a/widget/xpwidgets/ActiveElementManager.h b/gfx/layers/apz/util/ActiveElementManager.h similarity index 92% rename from widget/xpwidgets/ActiveElementManager.h rename to gfx/layers/apz/util/ActiveElementManager.h index 974f4449c2fd..b52df02303b1 100644 --- a/widget/xpwidgets/ActiveElementManager.h +++ b/gfx/layers/apz/util/ActiveElementManager.h @@ -3,8 +3,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef __mozilla_widget_ActiveElementManager_h__ -#define __mozilla_widget_ActiveElementManager_h__ +#ifndef mozilla_layers_ActiveElementManager_h +#define mozilla_layers_ActiveElementManager_h #include "nsCOMPtr.h" #include "nsISupportsImpl.h" @@ -15,7 +15,7 @@ class nsIDOMElement; class CancelableTask; namespace mozilla { -namespace widget { +namespace layers { /** * Manages setting and clearing the ':active' CSS pseudostate in the presence @@ -82,4 +82,4 @@ private: } } -#endif /*__mozilla_widget_ActiveElementManager_h__ */ +#endif /* mozilla_layers_ActiveElementManager_h */ diff --git a/gfx/layers/ipc/ImageBridgeParent.cpp b/gfx/layers/ipc/ImageBridgeParent.cpp index 44933140540c..8d3d30057120 100644 --- a/gfx/layers/ipc/ImageBridgeParent.cpp +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -34,7 +34,6 @@ #include "mozilla/layers/TextureHost.h" #include "nsThreadUtils.h" -using namespace base; using namespace mozilla::ipc; using namespace mozilla::gfx; @@ -118,7 +117,7 @@ ImageBridgeParent::RecvUpdateNoSwap(const EditArray& aEdits) static void ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge, Transport* aTransport, - ProcessHandle aOtherProcess) + base::ProcessHandle aOtherProcess) { aBridge->Open(aTransport, aOtherProcess, XRE_GetIOMessageLoop(), ipc::ParentSide); } @@ -126,7 +125,7 @@ ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge, /*static*/ PImageBridgeParent* ImageBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess) { - ProcessHandle processHandle; + base::ProcessHandle processHandle; if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) { return nullptr; } diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp index 4867b2bf7b5d..5bb409618055 100644 --- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -41,7 +41,7 @@ #include "GeckoProfiler.h" #include "mozilla/layers/TextureHost.h" #include "mozilla/layers/AsyncCompositionManager.h" -#include "AsyncPanZoomController.h" +#include "mozilla/layers/AsyncPanZoomController.h" typedef std::vector EditReplyVector; diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index b664db68a807..26378539f090 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -97,6 +97,16 @@ EXPORTS.gfxipc += [ ] EXPORTS.mozilla.layers += [ + 'apz/public/GeckoContentController.h', + # exporting things from apz/src is temporary until we extract a + # proper interface for the code there + 'apz/src/APZCTreeManager.h', + 'apz/src/AsyncPanZoomController.h', + 'apz/src/Axis.h', + 'apz/src/GestureEventListener.h', + 'apz/src/TaskThrottler.h', + 'apz/util/ActiveElementManager.h', + 'apz/util/APZCCallbackHelper.h', 'AtomicRefCountedWithFinalize.h', 'basic/BasicCompositor.h', 'basic/MacIOSurfaceTextureHostBasic.h', @@ -110,7 +120,6 @@ EXPORTS.mozilla.layers += [ 'client/TextureClient.h', 'client/TextureClientPool.h', 'client/TiledContentClient.h', - 'composite/APZCTreeManager.h', 'composite/AsyncCompositionManager.h', 'composite/CanvasLayerComposite.h', 'composite/ColorLayerComposite.h', @@ -126,15 +135,11 @@ EXPORTS.mozilla.layers += [ 'D3D9SurfaceImage.h', 'Effects.h', 'ImageDataSerializer.h', - 'ipc/AsyncPanZoomController.h', - 'ipc/Axis.h', 'ipc/CompositableForwarder.h', 'ipc/CompositableTransactionParent.h', 'ipc/CompositorChild.h', 'ipc/CompositorParent.h', 'ipc/FenceUtils.h', - 'ipc/GeckoContentController.h', - 'ipc/GestureEventListener.h', 'ipc/ImageBridgeChild.h', 'ipc/ImageBridgeParent.h', 'ipc/ISurfaceAllocator.h', @@ -144,7 +149,6 @@ EXPORTS.mozilla.layers += [ 'ipc/ShadowLayersManager.h', 'ipc/SharedPlanarYCbCrImage.h', 'ipc/SharedRGBImage.h', - 'ipc/TaskThrottler.h', 'LayersTypes.h', 'opengl/CompositingRenderTargetOGL.h', 'opengl/CompositorOGL.h', @@ -216,6 +220,13 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': ] UNIFIED_SOURCES += [ + 'apz/src/APZCTreeManager.cpp', + 'apz/src/AsyncPanZoomController.cpp', + 'apz/src/Axis.cpp', + 'apz/src/GestureEventListener.cpp', + 'apz/src/TaskThrottler.cpp', + 'apz/util/ActiveElementManager.cpp', + 'apz/util/APZCCallbackHelper.cpp', 'basic/BasicCanvasLayer.cpp', 'basic/BasicColorLayer.cpp', 'basic/BasicCompositor.cpp', @@ -242,7 +253,6 @@ UNIFIED_SOURCES += [ 'client/TextureClient.cpp', 'client/TextureClientPool.cpp', 'client/TiledContentClient.cpp', - 'composite/APZCTreeManager.cpp', 'composite/AsyncCompositionManager.cpp', 'composite/CanvasLayerComposite.cpp', 'composite/ColorLayerComposite.cpp', @@ -261,12 +271,9 @@ UNIFIED_SOURCES += [ 'Effects.cpp', 'ImageDataSerializer.cpp', 'ImageLayers.cpp', - 'ipc/AsyncPanZoomController.cpp', - 'ipc/Axis.cpp', 'ipc/CompositableTransactionParent.cpp', 'ipc/CompositorChild.cpp', 'ipc/CompositorParent.cpp', - 'ipc/GestureEventListener.cpp', 'ipc/ImageBridgeChild.cpp', 'ipc/ImageBridgeParent.cpp', 'ipc/ISurfaceAllocator.cpp', @@ -277,7 +284,6 @@ UNIFIED_SOURCES += [ 'ipc/ShadowLayers.cpp', 'ipc/SharedPlanarYCbCrImage.cpp', 'ipc/SharedRGBImage.cpp', - 'ipc/TaskThrottler.cpp', 'LayerScope.cpp', 'LayersLogging.cpp', 'LayerSorter.cpp', diff --git a/gfx/thebes/gfxMacPlatformFontList.mm b/gfx/thebes/gfxMacPlatformFontList.mm index c7a1199e67bb..f5bb856eba4c 100644 --- a/gfx/thebes/gfxMacPlatformFontList.mm +++ b/gfx/thebes/gfxMacPlatformFontList.mm @@ -830,13 +830,13 @@ gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(CFNotificatio return; } + gfxMacPlatformFontList* fl = static_cast(observer); + // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch - static_cast(observer)->UpdateFontList(); + fl->UpdateFontList(); // modify a preference that will trigger reflow everywhere - static const char kPrefName[] = "font.internaluseonly.changed"; - bool fontInternalChange = Preferences::GetBool(kPrefName, false); - Preferences::SetBool(kPrefName, !fontInternalChange); + fl->ForceGlobalReflow(); } gfxFontEntry* diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp index dee2446080d2..46a39cd7e003 100644 --- a/gfx/thebes/gfxPlatformFontList.cpp +++ b/gfx/thebes/gfxPlatformFontList.cpp @@ -9,6 +9,7 @@ #include "prlog.h" #include "gfxPlatformFontList.h" +#include "gfxUserFontSet.h" #include "nsUnicharUtils.h" #include "nsUnicodeRange.h" @@ -303,6 +304,21 @@ gfxPlatformFontList::ResolveFontName(const nsAString& aFontName, nsAString& aRes return false; } +static PLDHashOperator +RebuildLocalFonts(nsPtrHashKey* aKey, + void* aClosure) +{ + aKey->GetKey()->RebuildLocalRules(); + return PL_DHASH_NEXT; +} + +void +gfxPlatformFontList::UpdateFontList() +{ + InitFontList(); + mUserFontSetList.EnumerateEntries(RebuildLocalFonts, nullptr); +} + struct FontListData { FontListData(nsIAtom *aLangGroup, const nsACString& aGenericFamily, @@ -797,6 +813,11 @@ gfxPlatformFontList::LoadFontInfo() } #endif + if (done) { + mOtherFamilyNamesInitialized = true; + mFaceNamesInitialized = true; + } + return done; } @@ -820,8 +841,6 @@ gfxPlatformFontList::CleanupLoader() } #endif - mOtherFamilyNamesInitialized = true; - mFaceNamesInitialized = true; gfxFontInfoLoader::CleanupLoader(); } @@ -839,6 +858,15 @@ gfxPlatformFontList::GetPrefsAndStartLoader() StartLoader(delay, interval); } +void +gfxPlatformFontList::ForceGlobalReflow() +{ + // modify a preference that will trigger reflow everywhere + static const char kPrefName[] = "font.internaluseonly.changed"; + bool fontInternalChange = Preferences::GetBool(kPrefName, false); + Preferences::SetBool(kPrefName, !fontInternalChange); +} + // Support for memory reporting static size_t diff --git a/gfx/thebes/gfxPlatformFontList.h b/gfx/thebes/gfxPlatformFontList.h index 3cb1afcaa1e8..fa39bca4be90 100644 --- a/gfx/thebes/gfxPlatformFontList.h +++ b/gfx/thebes/gfxPlatformFontList.h @@ -82,6 +82,8 @@ struct FontListSizes { uint32_t mCharMapsSize; // memory used for cmap coverage info }; +class gfxUserFontSet; + class gfxPlatformFontList : public gfxFontInfoLoader { public: @@ -115,7 +117,7 @@ public: virtual bool ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName); - void UpdateFontList() { InitFontList(); } + void UpdateFontList(); void ClearPrefFonts() { mPrefFonts.Clear(); } @@ -178,6 +180,15 @@ public: // remove the cmap from the shared cmap set void RemoveCmap(const gfxCharacterMap *aCharMap); + // keep track of userfont sets to notify when global fontlist changes occur + void AddUserFontSet(gfxUserFontSet *aUserFontSet) { + mUserFontSetList.PutEntry(aUserFontSet); + } + + void RemoveUserFontSet(gfxUserFontSet *aUserFontSet) { + mUserFontSetList.RemoveEntry(aUserFontSet); + } + protected: class MemoryReporter MOZ_FINAL : public nsIMemoryReporter { @@ -254,6 +265,9 @@ protected: // read the loader initialization prefs, and start it void GetPrefsAndStartLoader(); + // for font list changes that affect all documents + void ForceGlobalReflow(); + // used by memory reporter to accumulate sizes of family names in the hash static size_t SizeOfFamilyNameEntryExcludingThis(const nsAString& aKey, @@ -305,6 +319,8 @@ protected: uint32_t mStartIndex; uint32_t mIncrement; uint32_t mNumFamilies; + + nsTHashtable > mUserFontSetList; }; #endif /* GFXPLATFORMFONTLIST_H_ */ diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index 996e013aef64..7ad4c0c92a2b 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -18,6 +18,7 @@ #include "gfxFontConstants.h" #include "mozilla/Services.h" #include "mozilla/gfx/2D.h" +#include "gfxPlatformFontList.h" #include "opentype-sanitiser.h" #include "ots-memory-stream.h" @@ -102,13 +103,21 @@ gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeed } gfxUserFontSet::gfxUserFontSet() - : mFontFamilies(5) + : mFontFamilies(5), mLocalRulesUsed(false) { IncrementGeneration(); + gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList(); + if (fp) { + fp->AddUserFontSet(this); + } } gfxUserFontSet::~gfxUserFontSet() { + gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList(); + if (fp) { + fp->RemoveUserFontSet(this); + } } gfxFontEntry* @@ -539,6 +548,7 @@ gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily, gfxFontEntry *fe = gfxPlatform::GetPlatform()->LookupLocalFont(aProxyEntry, currSrc.mLocalName); + mLocalRulesUsed = true; if (fe) { LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n", this, aProxyEntry->mSrcIndex, @@ -664,6 +674,13 @@ gfxUserFontSet::IncrementGeneration() mGeneration = sFontSetGeneration; } +void +gfxUserFontSet::RebuildLocalRules() +{ + if (mLocalRulesUsed) { + DoRebuildUserFontSet(); + } +} gfxFontEntry* gfxUserFontSet::LoadFont(gfxMixedFontFamily *aFamily, diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h index 86d17c61735a..74038df58d7f 100644 --- a/gfx/thebes/gfxUserFontSet.h +++ b/gfx/thebes/gfxUserFontSet.h @@ -242,6 +242,9 @@ public: // increment the generation on font load void IncrementGeneration(); + // rebuild if local rules have been used + void RebuildLocalRules(); + class UserFontCache { public: // Record a loaded user-font in the cache. This requires that the @@ -418,11 +421,17 @@ protected: static bool OTSMessage(void *aUserData, const char *format, ...); + // helper method for performing the actual userfont set rebuild + virtual void DoRebuildUserFontSet() = 0; + // font families defined by @font-face rules nsRefPtrHashtable mFontFamilies; uint64_t mGeneration; + // true when local names have been looked up, false otherwise + bool mLocalRulesUsed; + static PRLogModuleInfo* GetUserFontsLog(); private: diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index 33d214279c64..3cf02e8304a3 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -141,6 +141,66 @@ gfxUtils::UnpremultiplyImageSurface(gfxImageSurface *aSourceSurface, } } +TemporaryRef +gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* aSurface) +{ + // Only premultiply ARGB32 + if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) { + return aSurface; + } + + DataSourceSurface::MappedSurface map; + if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { + return nullptr; + } + + RefPtr dest = Factory::CreateDataSourceSurfaceWithStride(aSurface->GetSize(), + aSurface->GetFormat(), + map.mStride); + + DataSourceSurface::MappedSurface destMap; + if (!dest->Map(DataSourceSurface::MapType::WRITE, &destMap)) { + aSurface->Unmap(); + return nullptr; + } + + uint8_t *src = map.mData; + uint8_t *dst = destMap.mData; + + for (int32_t i = 0; i < aSurface->GetSize().height; ++i) { + uint8_t *srcRow = src + (i * map.mStride); + uint8_t *dstRow = dst + (i * destMap.mStride); + + for (int32_t j = 0; j < aSurface->GetSize().width; ++j) { +#ifdef IS_LITTLE_ENDIAN + uint8_t b = *srcRow++; + uint8_t g = *srcRow++; + uint8_t r = *srcRow++; + uint8_t a = *srcRow++; + + *dstRow++ = UnpremultiplyValue(a, b); + *dstRow++ = UnpremultiplyValue(a, g); + *dstRow++ = UnpremultiplyValue(a, r); + *dstRow++ = a; +#else + uint8_t a = *srcRow++; + uint8_t r = *srcRow++; + uint8_t g = *srcRow++; + uint8_t b = *srcRow++; + + *dstRow++ = a; + *dstRow++ = UnpremultiplyValue(a, r); + *dstRow++ = UnpremultiplyValue(a, g); + *dstRow++ = UnpremultiplyValue(a, b); +#endif + } + } + + aSurface->Unmap(); + dest->Unmap(); + return dest; +} + void gfxUtils::ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface, gfxImageSurface *aDestSurface) { @@ -186,6 +246,24 @@ gfxUtils::ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface, } } +void +gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength) +{ + uint8_t *src = aData; + uint8_t *srcEnd = src + aLength; + + uint8_t buffer[4]; + for (; src != srcEnd; src += 4) { + buffer[0] = src[2]; + buffer[1] = src[1]; + buffer[2] = src[0]; + + src[0] = buffer[0]; + src[1] = buffer[1]; + src[2] = buffer[2]; + } +} + static bool IsSafeImageTransformComponent(gfxFloat aValue) { diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index 2187748f1d37..551896752abc 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -44,9 +44,11 @@ public: gfxImageSurface *aDestSurface = nullptr); static void UnpremultiplyImageSurface(gfxImageSurface *aSurface, gfxImageSurface *aDestSurface = nullptr); + static mozilla::TemporaryRef UnpremultiplyDataSurface(DataSourceSurface* aSurface); static void ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface, gfxImageSurface *aDestSurface = nullptr); + static void ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength); /** * Draw something drawable while working around limitations like bad support diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 16ddb670459c..1550c4f708b4 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -23,6 +23,7 @@ #include "vm/GlobalObject.h" #include "vm/Interpreter.h" #include "vm/ProxyObject.h" +#include "vm/TraceLogging.h" #include "jscntxtinlines.h" #include "jsobjinlines.h" @@ -744,9 +745,9 @@ CountHeap(JSContext *cx, unsigned argc, jsval *vp) RootedValue startValue(cx, UndefinedValue()); if (args.length() > 0) { jsval v = args[0]; - if (JSVAL_IS_TRACEABLE(v)) { + if (v.isMarkable()) { startValue = v; - } else if (!JSVAL_IS_NULL(v)) { + } else if (!v.isNull()) { JS_ReportError(cx, "the first argument is not null or a heap-allocated " "thing"); @@ -771,11 +772,11 @@ CountHeap(JSContext *cx, unsigned argc, jsval *vp) return false; } traceValue = args[2]; - if (!JSVAL_IS_TRACEABLE(traceValue)){ + if (!traceValue.isMarkable()){ JS_ReportError(cx, "cannot trace this kind of value"); return false; } - traceThing = JSVAL_TO_TRACEABLE(traceValue); + traceThing = traceValue.toGCThing(); } else { for (size_t i = 0; ;) { if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) { @@ -1488,6 +1489,26 @@ TimesAccessed(JSContext *cx, unsigned argc, jsval *vp) return true; } +static bool +EnableTraceLogger(JSContext *cx, unsigned argc, jsval *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); + args.rval().setBoolean(TraceLoggerEnable(logger)); + + return true; +} + +static bool +DisableTraceLogger(JSContext *cx, unsigned argc, jsval *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); + args.rval().setBoolean(TraceLoggerDisable(logger)); + + return true; +} + static const JSFunctionSpecWithHelp TestingFunctions[] = { JS_FN_HELP("gc", ::GC, 0, 0, "gc([obj] | 'compartment')", @@ -1723,6 +1744,15 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = { "workerThreadCount()", " Returns the number of worker threads available for off-main-thread tasks."), + JS_FN_HELP("startTraceLogger", EnableTraceLogger, 0, 0, +"startTraceLogger()", +" Start logging the mainThread.\n" +" Note: tracelogging starts automatically. Disable it by setting environment variable\n" +" TLOPTIONS=disableMainThread"), + + JS_FN_HELP("stopTraceLogger", DisableTraceLogger, 0, 0, +"startTraceLogger()", +" Stop logging the mainThread."), JS_FS_HELP_END }; diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h index 062257c004d2..7617cf54a221 100644 --- a/js/src/ds/LifoAlloc.h +++ b/js/src/ds/LifoAlloc.h @@ -141,12 +141,6 @@ class BumpChunk return aligned; } - void *peek(size_t n) { - if (bump - bumpBase() < ptrdiff_t(n)) - return nullptr; - return bump - n; - } - static BumpChunk *new_(size_t chunkSize); static void delete_(BumpChunk *chunk); }; @@ -461,16 +455,6 @@ class LifoAlloc return Mark(chunk_, position_); } }; - - // Return a modifiable pointer to the most recently allocated bytes. The - // type of the thing must be known, so is only applicable to some special- - // purpose allocators. Will return a nullptr if nothing has been allocated. - template - T *peek() { - if (!latest) - return nullptr; - return static_cast(latest->peek(sizeof(T))); - } }; class LifoAllocScope diff --git a/js/src/gc/StoreBuffer.cpp b/js/src/gc/StoreBuffer.cpp index dd82440a27a9..7a3b4e4b7ac3 100644 --- a/js/src/gc/StoreBuffer.cpp +++ b/js/src/gc/StoreBuffer.cpp @@ -352,7 +352,7 @@ JS::HeapCellRelocate(js::gc::Cell **cellp) JS_PUBLIC_API(void) JS::HeapValuePostBarrier(JS::Value *valuep) { - JS_ASSERT(JSVAL_IS_TRACEABLE(*valuep)); + JS_ASSERT(valuep->isMarkable()); if (valuep->isString() && StringIsPermanentAtom(valuep->toString())) return; JSRuntime *runtime = static_cast(valuep->toGCThing())->runtimeFromMainThread(); @@ -363,7 +363,7 @@ JS_PUBLIC_API(void) JS::HeapValueRelocate(JS::Value *valuep) { /* Called with old contents of *valuep before overwriting. */ - JS_ASSERT(JSVAL_IS_TRACEABLE(*valuep)); + JS_ASSERT(valuep->isMarkable()); if (valuep->isString() && StringIsPermanentAtom(valuep->toString())) return; JSRuntime *runtime = static_cast(valuep->toGCThing())->runtimeFromMainThread(); diff --git a/js/src/gc/StoreBuffer.h b/js/src/gc/StoreBuffer.h index d65e5c9480ca..01f1696178dc 100644 --- a/js/src/gc/StoreBuffer.h +++ b/js/src/gc/StoreBuffer.h @@ -134,10 +134,6 @@ class StoreBuffer void put(StoreBuffer *owner, const T &t) { JS_ASSERT(storage_); - T *tip = storage_->peek(); - if (tip && tip->canMergeWith(t)) - return tip->mergeInplace(t); - T *tp = storage_->new_(t); if (!tp) CrashAtUnhandlableOOM("Failed to allocate for MonoTypeBuffer::put."); @@ -251,9 +247,6 @@ class StoreBuffer return !nursery.isInside(edge) && nursery.isInside(*edge); } - bool canMergeWith(const CellPtrEdge &other) const { return edge == other.edge; } - void mergeInplace(const CellPtrEdge &) {} - void mark(JSTracer *trc); CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); } @@ -277,9 +270,6 @@ class StoreBuffer return !nursery.isInside(edge) && nursery.isInside(deref()); } - bool canMergeWith(const ValueEdge &other) const { return edge == other.edge; } - void mergeInplace(const ValueEdge &) {} - void mark(JSTracer *trc); ValueEdge tagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) | 1)); } @@ -321,16 +311,6 @@ class StoreBuffer return !(*this == other); } - bool canMergeWith(const SlotsEdge &other) const { - return objectAndKind_ == other.objectAndKind_; - } - - void mergeInplace(const SlotsEdge &other) { - int32_t end = Max(start_ + count_, other.start_ + other.count_); - start_ = Min(start_, other.start_); - count_ = end - start_; - } - bool maybeInRememberedSet(const Nursery &nursery) const { return !nursery.isInside(object()); } @@ -360,9 +340,6 @@ class StoreBuffer static bool supportsDeduplication() { return true; } void *deduplicationKey() const { return (void *)edge; } - bool canMergeWith(const WholeCellEdges &other) const { return edge == other.edge; } - void mergeInplace(const WholeCellEdges &) {} - void mark(JSTracer *trc); typedef PointerEdgeHasher Hasher; diff --git a/js/src/jit-test/tests/asm.js/testSource.js b/js/src/jit-test/tests/asm.js/testSource.js index d5a575d40ce3..2e2ff5dc42b3 100644 --- a/js/src/jit-test/tests/asm.js/testSource.js +++ b/js/src/jit-test/tests/asm.js/testSource.js @@ -237,6 +237,33 @@ if (isAsmJSCompilationAvailable() && isCachingEnabled()) { })(); +/* Implicit "use strict" context */ +(function() { + +var funcHeader = 'function (glob, ffi, heap) {', + funcBody = '\n"use asm";\n\ + function g() {}\n\ + return g;\n\n' + funcFooter = '}', + funcSource = funcHeader + funcBody + funcFooter + useStrict = '\n"use strict";\n'; + +var f4 = eval("\"use strict\";\n(" + funcSource + ")"); + +var expectedToString = funcHeader + useStrict + funcBody + funcFooter +var expectedToSource = '(' + expectedToString + ')' + +assertEq(f4.toString(), expectedToString); +assertEq(f4.toSource(), expectedToSource); + +if (isAsmJSCompilationAvailable() && isCachingEnabled()) { + var f5 = eval("\"use strict\";\n(" + funcSource + ")"); + assertEq(isAsmJSModuleLoadedFromCache(f5), true); + assertEq(f5.toString(), expectedToString); + assertEq(f5.toSource(), expectedToSource); +} +})(); + /* Functions */ (function() { diff --git a/js/src/jit-test/tests/ion/bug995675.js b/js/src/jit-test/tests/ion/bug995675.js new file mode 100644 index 000000000000..81f9c1b0702a --- /dev/null +++ b/js/src/jit-test/tests/ion/bug995675.js @@ -0,0 +1,5 @@ +function f(x) { + return Math.cos(~(~Math.pow(Number.MAX_VALUE, x))) +} +f(-0) +assertEq(f(undefined - undefined), 1) diff --git a/js/src/jit-test/tests/ion/bug995826.js b/js/src/jit-test/tests/ion/bug995826.js new file mode 100644 index 000000000000..062869e8bf4c --- /dev/null +++ b/js/src/jit-test/tests/ion/bug995826.js @@ -0,0 +1,5 @@ +function f(x) { + return Math.round(-Math.tan(x > 0)) +} +f(2) +assertEq(f(-1), -0); diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index a196590a891f..d419d42d5a75 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -1143,7 +1143,13 @@ class MOZ_STACK_CLASS ModuleCompiler uint32_t funcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin; uint32_t offsetToEndOfUseAsm = parser_.tokenStream.currentToken().pos.end; - module_ = cx_->new_(parser_.ss, funcStart, offsetToEndOfUseAsm); + + // "use strict" should be added to the source if we are in an implicit + // strict context, see also comment above addUseStrict in + // js::FunctionToString. + bool strict = parser_.pc->sc->strict && !parser_.pc->sc->hasExplicitUseStrict(); + + module_ = cx_->new_(parser_.ss, funcStart, offsetToEndOfUseAsm, strict); if (!module_) return false; @@ -1514,7 +1520,6 @@ class MOZ_STACK_CLASS ModuleCompiler { module_->initFuncEnd(parser_.tokenStream.currentToken().pos.end, parser_.tokenStream.peekTokenPos().end); - masm_.finish(); if (masm_.oom()) return false; diff --git a/js/src/jit/AsmJSLink.cpp b/js/src/jit/AsmJSLink.cpp index 7142eb81ef10..bd71166a0e1a 100644 --- a/js/src/jit/AsmJSLink.cpp +++ b/js/src/jit/AsmJSLink.cpp @@ -800,8 +800,31 @@ js::AsmJSModuleToString(JSContext *cx, HandleFunction fun, bool addParenToLambda if (!src) return nullptr; - if (!out.append(src->chars(), src->length())) - return nullptr; + if (module.strict()) { + // We need to add "use strict" in the body right after the opening + // brace. + size_t bodyStart = 0, bodyEnd; + + // No need to test for functions created with the Function ctor as + // these doesn't implicitly inherit the "use strict" context. Strict mode is + // enabled for functions created with the Function ctor only if they begin with + // the "use strict" directive, but these functions won't validate as asm.js + // modules. + + ConstTwoByteChars chars(src->chars(), src->length()); + if (!FindBody(cx, fun, chars, src->length(), &bodyStart, &bodyEnd)) + return nullptr; + + if (!out.append(chars, bodyStart) || + !out.append("\n\"use strict\";\n") || + !out.append(chars + bodyStart, src->length() - bodyStart)) + { + return nullptr; + } + } else { + if (!out.append(src->chars(), src->length())) + return nullptr; + } if (funCtor && !out.append("\n}")) return nullptr; diff --git a/js/src/jit/AsmJSModule.cpp b/js/src/jit/AsmJSModule.cpp index f128d834fd9a..2e9de679561d 100644 --- a/js/src/jit/AsmJSModule.cpp +++ b/js/src/jit/AsmJSModule.cpp @@ -328,7 +328,8 @@ AsmJSModule::staticallyLink(ExclusiveContext *cx) } } -AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t funcStart, uint32_t offsetToEndOfUseAsm) +AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t funcStart, + uint32_t offsetToEndOfUseAsm, bool strict) : globalArgumentName_(nullptr), importArgumentName_(nullptr), bufferArgumentName_(nullptr), @@ -344,6 +345,7 @@ AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t funcStart, uint32_ mozilla::PodZero(&pod); scriptSource_->incref(); pod.minHeapLength_ = AsmJSAllocationGranularity; + pod.strict_ = strict; } AsmJSModule::~AsmJSModule() @@ -876,7 +878,7 @@ AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr *moduleOut) con { AutoUnprotectCodeForClone cloneGuard(cx, *this); - *moduleOut = cx->new_(scriptSource_, funcStart_, offsetToEndOfUseAsm_); + *moduleOut = cx->new_(scriptSource_, funcStart_, offsetToEndOfUseAsm_, pod.strict_); if (!*moduleOut) return false; @@ -1313,8 +1315,9 @@ js::LookupAsmJSModuleInCache(ExclusiveContext *cx, uint32_t funcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin; uint32_t offsetToEndOfUseAsm = parser.tokenStream.currentToken().pos.end; + bool strict = parser.pc->sc->strict && !parser.pc->sc->hasExplicitUseStrict(); ScopedJSDeletePtr module( - cx->new_(parser.ss, funcStart, offsetToEndOfUseAsm)); + cx->new_(parser.ss, funcStart, offsetToEndOfUseAsm, strict)); if (!module) return false; cursor = module->deserialize(cx, cursor); diff --git a/js/src/jit/AsmJSModule.h b/js/src/jit/AsmJSModule.h index d8d5199a0496..d9738588a474 100644 --- a/js/src/jit/AsmJSModule.h +++ b/js/src/jit/AsmJSModule.h @@ -431,6 +431,7 @@ class AsmJSModule struct Pod { uint32_t funcLength_; uint32_t funcLengthWithRightBrace_; + bool strict_; uint32_t numGlobalVars_; uint32_t numFFIs_; size_t funcPtrTableAndExitBytes_; @@ -464,7 +465,8 @@ class AsmJSModule mutable bool codeIsProtected_; public: - explicit AsmJSModule(ScriptSource *scriptSource, uint32_t functStart, uint32_t offsetToEndOfUseAsm); + explicit AsmJSModule(ScriptSource *scriptSource, uint32_t functStart, + uint32_t offsetToEndOfUseAsm, bool strict); ~AsmJSModule(); void trace(JSTracer *trc) { @@ -524,6 +526,9 @@ class AsmJSModule uint32_t funcEndAfterCurly() const { return funcStart_ + pod.funcLengthWithRightBrace_; } + bool strict() const { + return pod.strict_; + } bool addGlobalVarInit(const Value &v, AsmJSCoercion coercion, uint32_t *globalIndex) { JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 6c600dd38dbd..cb1a5ca2f994 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -586,13 +586,6 @@ IonBuilder::inlineMathAbs(CallInfo &callInfo) MInstruction *ins = MAbs::New(alloc(), callInfo.getArg(0), absType); current->add(ins); - if (IsFloatingPointType(argType) && returnType == MIRType_Int32) { - MToInt32 *toInt = MToInt32::New(alloc(), ins); - toInt->setCanBeNegativeZero(false); - current->add(toInt); - ins = toInt; - } - current->push(ins); return InliningStatus_Inlined; } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 7241b5c362fc..a441feb3933a 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -2328,12 +2328,6 @@ MToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers) } } - // Fold unnecessary numeric conversions. - if (input()->isToInt32()) { - replaceOperand(0, input()->getOperand(0)); - conversion_ = NonStringPrimitives; - } - return this; } diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index 561298713dfd..fe28d2b303a9 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -295,6 +295,7 @@ TypeBarrierPolicy::adjustInputs(TempAllocator &alloc, MInstruction *def) // We can't unbox a value to null/undefined. So keep output also a value. if (IsNullOrUndefined(outputType) || outputType == MIRType_Magic) { + JS_ASSERT(ins->defUseCount() == 0); ins->setResultType(MIRType_Value); return true; } diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index ee1e00855189..6b9af457d6dc 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -14,6 +14,7 @@ #include "jit/MIR.h" #include "jit/MIRGenerator.h" #include "jit/ParallelFunctions.h" +#include "vm/TraceLogging.h" #include "jit/IonFrames-inl.h" @@ -628,6 +629,11 @@ CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Regi } #endif +#ifdef JS_TRACE_LOGGING + if (!emitTracelogStartEvent(TraceLogger::VM)) + return false; +#endif + // Stack is: // ... frame ... // [args] @@ -667,6 +673,12 @@ CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Regi masm.implicitPop(fun.explicitStackSlots() * sizeof(void *) + framePop); // Stack is: // ... frame ... + +#ifdef JS_TRACE_LOGGING + if (!emitTracelogStopEvent(TraceLogger::VM)) + return false; +#endif + return true; } @@ -1028,6 +1040,9 @@ CodeGeneratorShared::emitTracelogScript(bool isStart) bool CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId) { + if (!TraceLogTextIdEnabled(textId)) + return true; + RegisterSet regs = RegisterSet::Volatile(); Register logger = regs.takeGeneral(); @@ -1037,28 +1052,15 @@ CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId) if (!patchableTraceLoggers_.append(patchLocation)) return false; - if (isStart) + if (isStart) { masm.tracelogStart(logger, textId); - else + } else { +#ifdef DEBUG masm.tracelogStop(logger, textId); - - masm.Pop(logger); - return true; -} - -bool -CodeGeneratorShared::emitTracelogStopEvent() -{ - RegisterSet regs = RegisterSet::Volatile(); - Register logger = regs.takeGeneral(); - - masm.Push(logger); - - CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger); - if (!patchableTraceLoggers_.append(patchLocation)) - return false; - - masm.tracelogStop(logger); +#else + masm.tracelogStop(logger); +#endif + } masm.Pop(logger); return true; diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index 67d52f5dcda3..acdbe6ad8ac1 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -467,13 +467,8 @@ class CodeGeneratorShared : public LInstructionVisitor return emitTracelogTree(/* isStart =*/ true, textId); } bool emitTracelogStopEvent(uint32_t textId) { -#ifdef DEBUG return emitTracelogTree(/* isStart =*/ false, textId); -#else - return emitTracelogScript(/* isStart =*/ false); -#endif } - bool emitTracelogStopEvent(); #endif }; diff --git a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h index 210bd66e9291..825977bf7f20 100644 --- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h +++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h @@ -760,6 +760,10 @@ struct AssemblerBufferWithConstantPool : public AssemblerBuffer IonSpew(IonSpew_Pools, "[%d]***Offset was still out of range!***", id, codeOffset - magicAlign); IonSpew(IonSpew_Pools, "[%d] Too complicated; bailingp", id); this->fail_bail(); + // only free up to the current offset + for (int pi = poolIdx; pi < numPoolKinds; pi++) + delete[] outcastEntries[pi]; + delete[] preservedEntries; return; } else { preservedEntries[idx] = true; @@ -783,12 +787,15 @@ struct AssemblerBufferWithConstantPool : public AssemblerBuffer } poolOffset += p->numEntries * p->immSize; delete[] preservedEntries; + preservedEntries = nullptr; } // bind the current pool to the perforation point. Pool **tmp = &perforatedNode->data; *tmp = static_cast(this->LifoAlloc_.alloc(sizeof(Pool) * numPoolKinds)); if (tmp == nullptr) { this->fail_oom(); + for (int pi = 0; pi < numPoolKinds; pi++) + delete[] outcastEntries[pi]; return; } // The above operations may have changed the size of pools! @@ -804,6 +811,8 @@ struct AssemblerBufferWithConstantPool : public AssemblerBuffer for (int poolIdx = 0; poolIdx < numPoolKinds; poolIdx++) { if (!pools[poolIdx].reset(this->LifoAlloc_)) { this->fail_oom(); + for (int pi = 0; pi < numPoolKinds; pi++) + delete[] outcastEntries[pi]; return; } } diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index 7993c472fa82..50fbfd5bb1db 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -396,16 +396,27 @@ MacroAssemblerX86::testNegativeZero(const FloatRegister ®, const Register &sc // Compare to zero. Lets through {0, -0}. xorpd(ScratchFloatReg, ScratchFloatReg); - // If reg is non-zero, then a test of Zero is false. + + // If reg is non-zero, jump to nonZero. + // Sets ZF=0 and PF=0. branchDouble(DoubleNotEqual, reg, ScratchFloatReg, &nonZero); - // Input register is either zero or negative zero. Test sign bit. + // Input register is either zero or negative zero. Retrieve sign of input. movmskpd(reg, scratch); - // If reg is -0, then a test of Zero is true. - cmpl(scratch, Imm32(1)); + + // If reg is 1 or 3, input is negative zero. + // If reg is 0 or 2, input is a normal zero. + // So the following test will set PF=1 for negative zero. + orl(Imm32(2), scratch); bind(&nonZero); - return Zero; + + // Here we need to be able to test if the input is a negative zero. + // - branchDouble joins here for non-zero values in which case it sets + // ZF=0 and PF=0. In that case the test should fail. + // - orl sets PF=1 on negative zero and PF=0 otherwise + // => So testing PF=1 will return if input is negative zero or not. + return Parity; } Assembler::Condition diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 20b58a9e1fda..b98231a59d1c 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1980,38 +1980,6 @@ JS_AddExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data); extern JS_PUBLIC_API(void) JS_RemoveExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data); -/* - * JS_CallTracer API and related macros for implementors of JSTraceOp, to - * enumerate all references to traceable things reachable via a property or - * other strong ref identified for debugging purposes by name or index or - * a naming callback. - * - * See the JSTraceOp typedef. - */ - -/* - * Use the following macros to check if a particular jsval is a traceable - * thing and to extract the thing and its kind to pass to JS_CallTracer. - */ -static MOZ_ALWAYS_INLINE bool -JSVAL_IS_TRACEABLE(jsval v) -{ - return JSVAL_IS_TRACEABLE_IMPL(JSVAL_TO_IMPL(v)); -} - -static MOZ_ALWAYS_INLINE void * -JSVAL_TO_TRACEABLE(jsval v) -{ - return JSVAL_TO_GCTHING(v); -} - -static MOZ_ALWAYS_INLINE JSGCTraceKind -JSVAL_TRACE_KIND(jsval v) -{ - JS_ASSERT(JSVAL_IS_GCTHING(v)); - return (JSGCTraceKind) JSVAL_TRACE_KIND_IMPL(JSVAL_TO_IMPL(v)); -} - #ifdef JS_DEBUG /* diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 347862d0259e..7f4639cd5f11 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -626,14 +626,18 @@ const Class JSFunction::class_ = { const Class* const js::FunctionClassPtr = &JSFunction::class_; /* Find the body of a function (not including braces). */ -static bool -FindBody(JSContext *cx, HandleFunction fun, ConstTwoByteChars chars, size_t length, +bool +js::FindBody(JSContext *cx, HandleFunction fun, ConstTwoByteChars chars, size_t length, size_t *bodyStart, size_t *bodyEnd) { // We don't need principals, since those are only used for error reporting. CompileOptions options(cx); - options.setFileAndLine("internal-findBody", 0) - .setVersion(fun->nonLazyScript()->getVersion()); + options.setFileAndLine("internal-findBody", 0); + + // For asm.js modules, there's no script. + if (fun->hasScript()) + options.setVersion(fun->nonLazyScript()->getVersion()); + AutoKeepAtoms keepAtoms(cx->perThreadData); TokenStream ts(cx, options, chars.get(), length, nullptr); int nest = 0; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 226ddc2666e1..d11f1ac52f73 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -554,6 +554,11 @@ CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, gc::AllocKind kind = JSFunction::FinalizeKind, NewObjectKind newKindArg = GenericObject); + +extern bool +FindBody(JSContext *cx, HandleFunction fun, ConstTwoByteChars chars, size_t length, + size_t *bodyStart, size_t *bodyEnd); + } // namespace js inline js::FunctionExtended * diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 80c2fde9eb38..173b5fadfaad 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -97,7 +97,7 @@ enum JSIterateOp { JSENUMERATE_DESTROY }; -/* See JSVAL_TRACE_KIND and JSTraceCallback in jsapi.h. */ +/* See Value::gcKind() and JSTraceCallback in Tracer.h. */ enum JSGCTraceKind { JSTRACE_OBJECT, JSTRACE_STRING, diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 2ef8726a7031..8fbd731fafb0 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -91,16 +91,43 @@ macro(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, JOF_BYTE) \ \ /* Long-standing JavaScript bytecodes. */ \ + /* + * Pushes 'undefined' onto the stack. + * Category: Literals + * Type: Constants + * Operands: + * Stack: => undefined + */ \ macro(JSOP_UNDEFINED, 1, js_undefined_str, "", 1, 0, 1, JOF_BYTE) \ macro(JSOP_UNUSED2, 2, "unused2", NULL, 1, 1, 0, JOF_BYTE) \ macro(JSOP_ENTERWITH, 3, "enterwith", NULL, 5, 1, 0, JOF_OBJECT) \ macro(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pops the top of stack value as 'rval', stops interpretation of current + * script and returns 'rval'. + * Category: Statements + * Type: Function + * Operands: + * Stack: rval => + */ \ macro(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, JOF_BYTE) \ macro(JSOP_GOTO, 6, "goto", NULL, 5, 0, 0, JOF_JUMP) \ macro(JSOP_IFEQ, 7, "ifeq", NULL, 5, 1, 0, JOF_JUMP|JOF_DETECTING) \ macro(JSOP_IFNE, 8, "ifne", NULL, 5, 1, 0, JOF_JUMP) \ \ - /* Get the arguments object for the current, lightweight function activation. */ \ + /* + * Pushes the 'arguments' object for the current function activation. + * + * If 'JSScript' is not marked 'needsArgsObj', then a + * JS_OPTIMIZED_ARGUMENTS magic value is pushed. Otherwise, a proper + * arguments object is constructed and pushed. + * + * This opcode requires that the function does not have rest parameter. + * Category: Variables and Scopes + * Type: Arguments + * Operands: + * Stack: => arguments + */ \ macro(JSOP_ARGUMENTS, 9, "arguments", NULL, 1, 0, 1, JOF_BYTE) \ \ /* @@ -139,6 +166,14 @@ * Stack: v1, v2 => v1, v2, v1, v2 */ \ macro(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, JOF_BYTE) \ + /* + * Defines a readonly property on the frame's current variables-object (the + * scope object on the scope chain designated to receive new variables). + * Category: Variables and Scopes + * Type: Variables + * Operands: uint32_t nameIndex + * Stack: val => val + */ \ macro(JSOP_SETCONST, 14, "setconst", NULL, 5, 1, 1, JOF_ATOM|JOF_NAME|JOF_SET) \ /* * Pops the top two values 'lval' and 'rval' from the stack, then pushes @@ -239,8 +274,36 @@ * Stack: val => (+val) */ \ macro(JSOP_POS, 35, "pos", "+ ", 1, 1, 1, JOF_BYTE|JOF_ARITH) \ + /* + * Looks up name on the scope chain and deletes it, pushes 'true' onto the + * stack if succeeded (if the property was present and deleted or if the + * property wasn't present in the first place), 'false' if not. + * + * Strict mode code should never contain this opcode. + * Category: Variables and Scopes + * Type: Variables + * Operands: uint32_t nameIndex + * Stack: => succeeded + */ \ macro(JSOP_DELNAME, 36, "delname", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME) \ + /* + * Pops the top of stack value, deletes property from it, pushes 'true' onto + * the stack if succeeded, 'false' if not. + * Category: Literals + * Type: Object + * Operands: uint32_t nameIndex + * Stack: obj => succeeded + */ \ macro(JSOP_DELPROP, 37, "delprop", NULL, 5, 1, 1, JOF_ATOM|JOF_PROP) \ + /* + * Pops the top two values on the stack as 'propval' and 'obj', + * deletes 'propval' property from 'obj', pushes 'true' onto the stack if + * succeeded, 'false' if not. + * Category: Literals + * Type: Object + * Operands: + * Stack: obj, propval => succeeded + */ \ macro(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, JOF_BYTE |JOF_ELEM) \ /* * Pops the value 'val' from the stack, then pushes 'typeof val'. @@ -259,11 +322,42 @@ */ \ macro(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, JOF_BYTE) \ \ - /* spreadcall variant of JSOP_CALL */ \ + /* + * spreadcall variant of JSOP_CALL. + * + * Invokes 'callee' with 'this' and 'args', pushes the return value onto + * the stack. + * + * 'args' is an Array object which contains actual arguments. + * Category: Statements + * Type: Function + * Operands: + * Stack: callee, this, args => rval + */ \ macro(JSOP_SPREADCALL,41, "spreadcall", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) \ - /* spreadcall variant of JSOP_NEW */ \ + /* + * spreadcall variant of JSOP_NEW + * + * Invokes 'callee' as a constructor with 'this' and 'args', pushes the + * return value onto the stack. + * Category: Statements + * Type: Function + * Operands: + * Stack: callee, this, args => rval + */ \ macro(JSOP_SPREADNEW, 42, "spreadnew", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) \ - /* spreadcall variant of JSOP_EVAL */ \ + /* + * spreadcall variant of JSOP_EVAL + * + * Invokes 'eval' with 'args' and pushes the return value onto the stack. + * + * If 'eval' in global scope is not original one, invokes the function + * with 'this' and 'args', and pushes return value onto the stack. + * Category: Statements + * Type: Function + * Operands: + * Stack: callee, this, args => rval + */ \ macro(JSOP_SPREADEVAL,43, "spreadeval", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) \ \ /* @@ -285,19 +379,116 @@ macro(JSOP_UNUSED51, 51, "unused51", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED52, 52, "unused52", NULL, 1, 0, 0, JOF_BYTE) \ \ + /* + * Pops the top of stack value, pushes property of it onto the stack. + * Category: Literals + * Type: Object + * Operands: uint32_t nameIndex + * Stack: obj => obj[name] + */ \ macro(JSOP_GETPROP, 53, "getprop", NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) \ + /* + * Pops the top two values on the stack as 'val' and 'obj', sets property of + * 'obj' as 'val', pushes 'obj' onto the stack. + * Category: Literals + * Type: Object + * Operands: uint32_t nameIndex + * Stack: obj, val => val + */ \ macro(JSOP_SETPROP, 54, "setprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \ + /* + * Pops the top two values on the stack as 'propval' and 'obj', pushes + * 'propval' property of 'obj' onto the stack. + * Category: Literals + * Type: Object + * Operands: + * Stack: obj, propval => obj[propval] + */ \ macro(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC) \ + /* + * Pops the top three values on the stack as 'val', 'propval' and 'obj', + * sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the + * stack. + * Category: Literals + * Type: Object + * Operands: + * Stack: obj, propval, val => val + */ \ macro(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) \ macro(JSOP_UNUSED57, 57, "unused57", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Invokes 'callee' with 'this' and 'args', pushes return value onto the + * stack. + * Category: Statements + * Type: Function + * Operands: uint16_t argc + * Stack: callee, this, args[0], ..., args[argc-1] => rval + * nuses: (argc+2) + */ \ macro(JSOP_CALL, 58, "call", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \ + /* + * Looks up name on the scope chain and pushes its value onto the stack. + * Category: Variables and Scopes + * Type: Variables + * Operands: uint32_t nameIndex + * Stack: => val + */ \ macro(JSOP_NAME, 59, "name", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_TYPESET) \ + /* + * Pushes numeric constant onto the stack. + * Category: Literals + * Type: Constants + * Operands: uint32_t constIndex + * Stack: => val + */ \ macro(JSOP_DOUBLE, 60, "double", NULL, 5, 0, 1, JOF_DOUBLE) \ + /* + * Pushes string constant onto the stack. + * Category: Literals + * Type: Constants + * Operands: uint32_t atomIndex + * Stack: => string + */ \ macro(JSOP_STRING, 61, "string", NULL, 5, 0, 1, JOF_ATOM) \ + /* + * Pushes '0' onto the stack. + * Category: Literals + * Type: Constants + * Operands: + * Stack: => 0 + */ \ macro(JSOP_ZERO, 62, "zero", "0", 1, 0, 1, JOF_BYTE) \ + /* + * Pushes '1' onto the stack. + * Category: Literals + * Type: Constants + * Operands: + * Stack: => 1 + */ \ macro(JSOP_ONE, 63, "one", "1", 1, 0, 1, JOF_BYTE) \ + /* + * Pushes 'null' onto the stack. + * Category: Literals + * Type: Constants + * Operands: + * Stack: => null + */ \ macro(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, JOF_BYTE) \ + /* + * Pushes 'this' value for current stack frame onto the stack. + * Category: Variables and Scopes + * Type: This + * Operands: + * Stack: => this + */ \ macro(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, JOF_BYTE) \ + /* + * Pushes boolean value onto the stack. + * Category: Literals + * Type: Constants + * Operands: + * Stack: => true/false + */ \ macro(JSOP_FALSE, 66, js_false_str, js_false_str, 1, 0, 1, JOF_BYTE) \ macro(JSOP_TRUE, 67, js_true_str, js_true_str, 1, 0, 1, JOF_BYTE) \ macro(JSOP_OR, 68, "or", NULL, 5, 1, 1, JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC) \ @@ -307,8 +498,12 @@ macro(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, JOF_TABLESWITCH|JOF_DETECTING) \ \ /* - * Prologue emitted in scripts expected to run once, which deoptimizes code if - * it executes multiple times. + * Prologue emitted in scripts expected to run once, which deoptimizes code + * if it executes multiple times. + * Category: Statements + * Type: Function + * Operands: + * Stack: => */ \ macro(JSOP_RUNONCE, 71, "runonce", NULL, 1, 0, 0, JOF_BYTE) \ \ @@ -326,8 +521,12 @@ \ /* * Sometimes web pages do 'o.Item(i) = j'. This is not an early SyntaxError, - * for web compatibility. Instead we emit JSOP_SETCALL after the function call, - * an opcode that always throws. + * for web compatibility. Instead we emit JSOP_SETCALL after the function + * call, an opcode that always throws. + * Category: Statements + * Type: Function + * Operands: + * Stack: => */ \ macro(JSOP_SETCALL, 74, "setcall", NULL, 1, 0, 0, JOF_BYTE) \ \ @@ -348,9 +547,26 @@ macro(JSOP_ITERNEXT, 77, "iternext", "", 1, 0, 1, JOF_BYTE) \ macro(JSOP_ENDITER, 78, "enditer", NULL, 1, 1, 0, JOF_BYTE) \ \ + /* + * Invokes 'callee' with 'this' and 'args', pushes return value onto the + * stack. + * + * This is for 'f.apply'. + * Category: Statements + * Type: Function + * Operands: uint16_t argc + * Stack: callee, this, args[0], ..., args[argc-1] => rval + * nuses: (argc+2) + */ \ macro(JSOP_FUNAPPLY, 79, "funapply", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \ \ - /* Push object initializer literal. */ \ + /* + * Pushes deep-cloned object literal or singleton onto the stack. + * Category: Literals + * Type: Object + * Operands: uint32_t objectIndex + * Stack: => obj + */ \ macro(JSOP_OBJECT, 80, "object", NULL, 5, 0, 1, JOF_OBJECT) \ \ /* @@ -362,50 +578,219 @@ */ \ macro(JSOP_POP, 81, "pop", NULL, 1, 1, 0, JOF_BYTE) \ \ - /* Call a function as a constructor; operand is argc. */ \ + /* + * Invokes 'callee' as a constructor with 'this' and 'args', pushes return + * value onto the stack. + * Category: Statements + * Type: Function + * Operands: uint16_t argc + * Stack: callee, this, args[0], ..., args[argc-1] => rval + * nuses: (argc+2) + */ \ macro(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \ - \ + /* + * Pops the top three values on the stack as 'iterable', 'index' and 'obj', + * iterates over 'iterable' and stores the iteration values as 'index + i' + * elements of 'obj', pushes 'obj' and 'index + iteration count' onto the + * stack. + * + * This opcode is used in Array literals with spread and spreadcall + * arguments. + * Category: Literals + * Type: Array + * Operands: + * Stack: obj, index, iterable => obj, (index + iteration count) + */ \ macro(JSOP_SPREAD, 83, "spread", NULL, 1, 3, 2, JOF_BYTE|JOF_ELEM|JOF_SET) \ \ - /* Fast get/set ops for function arguments and local variables. */ \ + /* + * Fast get op for function arguments and local variables. + * + * Pushes 'arguments[argno]' onto the stack. + * Category: Variables and Scopes + * Type: Arguments + * Operands: uint16_t argno + * Stack: => arguments[argno] + */ \ macro(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, JOF_QARG |JOF_NAME) \ + /* + * Fast set op for function arguments and local variables. + * + * Sets 'arguments[argno]' as the top of stack value. + * Category: Variables and Scopes + * Type: Arguments + * Operands: uint16_t argno + * Stack: v => v + */ \ macro(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, JOF_QARG |JOF_NAME|JOF_SET) \ + /* + * Pushes the value of local variable onto the stack. + * Category: Variables and Scopes + * Type: Local Variables + * Operands: uint32_t localno + * Stack: => val + */ \ macro(JSOP_GETLOCAL, 86,"getlocal", NULL, 4, 0, 1, JOF_LOCAL|JOF_NAME) \ + /* + * Stores the top stack value to the given local. + * Category: Variables and Scopes + * Type: Local Variables + * Operands: uint32_t localno + * Stack: v => v + */ \ macro(JSOP_SETLOCAL, 87,"setlocal", NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) \ \ - /* Push unsigned 16-bit int constant. */ \ + /* + * Pushes unsigned 16-bit int immediate integer operand onto the stack. + * Category: Literals + * Type: Constants + * Operands: uint16_t val + * Stack: => val + */ \ macro(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, JOF_UINT16) \ \ + /* Object and array literal support. */ \ /* - * Object and array literal support. NEWINIT takes the kind of initializer - * (JSProto_Array or JSProto_Object). NEWARRAY is an array initializer - * taking the final length, which can be filled in at the start and initialized - * directly. NEWOBJECT is an object initializer taking an object with the final - * shape, which can be set at the start and slots then filled in directly. - * NEWINIT has an extra byte so it can be exchanged with NEWOBJECT during emit. + * Pushes newly created object onto the stack. + * + * This opcode takes the kind of initializer (JSProto_Array or + * JSProto_Object). + * + * This opcode has an extra byte so it can be exchanged with JSOP_NEWOBJECT + * during emit. + * Category: Literals + * Type: Object + * Operands: uint8_t kind (, uint24_t extra) + * Stack: => obj */ \ macro(JSOP_NEWINIT, 89, "newinit", NULL, 5, 0, 1, JOF_UINT8) \ + /* + * Pushes newly created array onto the stack. + * + * This opcode takes the final length, which is preallocated. + * Category: Literals + * Type: Array + * Operands: uint24_t length + * Stack: => obj + */ \ macro(JSOP_NEWARRAY, 90, "newarray", NULL, 4, 0, 1, JOF_UINT24) \ + /* + * Pushes newly created object onto the stack. + * + * This opcode takes an object with the final shape, which can be set at + * the start and slots then filled in directly. + * Category: Literals + * Type: Object + * Operands: uint32_t baseobjIndex + * Stack: => obj + */ \ macro(JSOP_NEWOBJECT, 91, "newobject", NULL, 5, 0, 1, JOF_OBJECT) \ + /* + * A no-operation bytecode. + * + * Indicates the end of object/array initialization, and used for + * Type-Inference, decompile, etc. + * Category: Literals + * Type: Object + * Operands: + * Stack: => + */ \ macro(JSOP_ENDINIT, 92, "endinit", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Initialize a named property in an object literal, like '{a: x}'. + * + * Pops the top two values on the stack as 'val' and 'obj', defines + * 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack. + * Category: Literals + * Type: Object + * Operands: uint32_t nameIndex + * Stack: obj, val => obj + */ \ macro(JSOP_INITPROP, 93, "initprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \ \ - /* Initialize a numeric property in an object literal, like {1: x}. */ \ + /* + * Initialize a numeric property in an object literal, like '{1: x}'. + * + * Pops the top three values on the stack as 'val', 'id' and 'obj', defines + * 'id' property of 'obj' as 'val', pushes 'obj' onto the stack. + * Category: Literals + * Type: Object + * Operands: + * Stack: obj, id, val => obj + */ \ macro(JSOP_INITELEM, 94, "initelem", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \ \ - /* Used in array literals with spread. */ \ + /* + * Pops the top three values on the stack as 'val', 'index' and 'obj', sets + * 'index' property of 'obj' as 'val', pushes 'obj' and 'index + 1' onto + * the stack. + * + * This opcode is used in Array literals with spread and spreadcall + * arguments. + * Category: Literals + * Type: Array + * Operands: + * Stack: obj, index, val => obj, (index + 1) + */ \ macro(JSOP_INITELEM_INC,95, "initelem_inc", NULL, 1, 3, 2, JOF_BYTE|JOF_ELEM|JOF_SET) \ \ - /* Initialize an array element. */ \ + /* + * Initialize an array element. + * + * Pops the top two values on the stack as 'val' and 'obj', sets 'index' + * property of 'obj' as 'val', pushes 'obj' onto the stack. + * Category: Literals + * Type: Array + * Operands: uint24_t index + * Stack: obj, val => obj + */ \ macro(JSOP_INITELEM_ARRAY,96, "initelem_array", NULL, 4, 2, 1, JOF_UINT24|JOF_ELEM|JOF_SET|JOF_DETECTING) \ \ /* - * Initialize a getter/setter in an object literal. The INITELEM* ops are used - * for numeric properties like {get 2() {}}. + * Initialize a getter in an object literal. + * + * Pops the top two values on the stack as 'val' and 'obj', defines getter + * of 'obj' as 'val', pushes 'obj' onto the stack. + * Category: Literals + * Type: Object + * Operands: uint32_t nameIndex + * Stack: obj, val => obj */ \ macro(JSOP_INITPROP_GETTER, 97, "initprop_getter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \ + /* + * Initialize a setter in an object literal. + * + * Pops the top two values on the stack as 'val' and 'obj', defines setter + * of 'obj' as 'val', pushes 'obj' onto the stack. + * Category: Literals + * Type: Object + * Operands: uint32_t nameIndex + * Stack: obj, val => obj + */ \ macro(JSOP_INITPROP_SETTER, 98, "initprop_setter", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \ + /* + * Initialize a numeric getter in an object literal like + * '{get 2() {}}'. + * + * Pops the top three values on the stack as 'val', 'id' and 'obj', defines + * 'id' getter of 'obj' as 'val', pushes 'obj' onto the stack. + * Category: Literals + * Type: Object + * Operands: + * Stack: obj, id, val => obj + */ \ macro(JSOP_INITELEM_GETTER, 99, "initelem_getter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \ + /* + * Initialize a numeric setter in an object literal like + * '{set 2(v) {}}'. + * + * Pops the top three values on the stack as 'val', 'id' and 'obj', defines + * 'id' setter of 'obj' as 'val', pushes 'obj' onto the stack. + * Category: Literals + * Type: Object + * Operands: + * Stack: obj, id, val => obj + */ \ macro(JSOP_INITELEM_SETTER, 100, "initelem_setter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \ \ macro(JSOP_UNUSED101, 101, "unused101", NULL, 1, 0, 0, JOF_BYTE) \ @@ -419,14 +804,46 @@ \ macro(JSOP_UNUSED107, 107,"unused107", NULL, 1, 0, 0, JOF_BYTE) \ \ - /* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */ \ + /* + * Invokes 'callee' with 'this' and 'args', pushes return value onto the + * stack. + * + * If 'callee' is determined to be the canonical 'Function.prototype.call' + * function, then this operation is optimized to directly call 'callee' + * with 'args[0]' as 'this', and the remaining arguments as formal args + * to 'callee'. + * + * Like JSOP_FUNAPPLY but for 'f.call' instead of 'f.apply'. + * Category: Statements + * Type: Function + * Operands: uint16_t argc + * Stack: callee, this, args[0], ..., args[argc-1] => rval + * nuses: (argc+2) + */ \ macro(JSOP_FUNCALL, 108,"funcall", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \ \ /* This opcode is the target of the backwards jump for some loop. */ \ macro(JSOP_LOOPHEAD, 109,"loophead", NULL, 1, 0, 0, JOF_BYTE) \ \ /* ECMA-compliant assignment ops. */ \ + /* + * Looks up name on the scope chain and pushes the scope which contains + * the name onto the stack. If not found, pushes global scope onto the + * stack. + * Category: Variables and Scopes + * Type: Variables + * Operands: uint32_t nameIndex + * Stack: => scope + */ \ macro(JSOP_BINDNAME, 110,"bindname", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_SET) \ + /* + * Pops a scope and value from the stack, assigns value to the given name, + * and pushes the value back on the stack + * Category: Variables and Scopes + * Type: Variables + * Operands: uint32_t nameIndex + * Stack: scope, val => val + */ \ macro(JSOP_SETNAME, 111,"setname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) \ \ /* Exception handling ops. */ \ @@ -454,7 +871,13 @@ */ \ macro(JSOP_INSTANCEOF,114,js_instanceof_str,js_instanceof_str,1,2,1,JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT) \ \ - /* debugger op */ \ + /* + * Invokes debugger. + * Category: Statements + * Type: Debugger + * Operands: + * Stack: => + */ \ macro(JSOP_DEBUGGER, 115,"debugger", NULL, 1, 0, 0, JOF_BYTE) \ \ /* gosub/retsub for finally handling */ \ @@ -464,7 +887,12 @@ /* More exception handling ops. */ \ macro(JSOP_EXCEPTION, 118,"exception", NULL, 1, 0, 1, JOF_BYTE) \ \ - /* Embedded lineno to speedup pc->line mapping. */ \ + /* + * Embedded lineno to speedup 'pc->line' mapping. + * Category: Other + * Operands: uint32_t lineno + * Stack: => + */ \ macro(JSOP_LINENO, 119,"lineno", NULL, 3, 0, 0, JOF_UINT16) \ \ /* @@ -476,8 +904,17 @@ macro(JSOP_CASE, 121,"case", NULL, 5, 2, 1, JOF_JUMP) \ macro(JSOP_DEFAULT, 122,"default", NULL, 5, 1, 0, JOF_JUMP) \ \ + /* ECMA-compliant call to eval op. */ \ /* - * ECMA-compliant call to eval op + * Invokes 'eval' with 'args' and pushes return value onto the stack. + * + * If 'eval' in global scope is not original one, invokes the function + * with 'this' and 'args', and pushes return value onto the stack. + * Category: Statements + * Type: Function + * Operands: uint16_t argc + * Stack: callee, this, args[0], ..., args[argc-1] => rval + * nuses: (argc+2) */ \ macro(JSOP_EVAL, 123,"eval", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \ \ @@ -485,16 +922,71 @@ macro(JSOP_UNUSED125, 125, "unused125", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED126, 126, "unused126", NULL, 1, 0, 0, JOF_BYTE) \ \ - /* Prolog bytecodes for defining function, var, and const names. */ \ + /* + * Defines the given function on the current scope. + * + * This is used for global scripts and also in some cases for function + * scripts where use of dynamic scoping inhibits optimization. + * Category: Variables and Scopes + * Type: Variables + * Operands: uint32_t funcIndex + * Stack: => + */ \ macro(JSOP_DEFFUN, 127,"deffun", NULL, 5, 0, 0, JOF_OBJECT) \ + /* + * Defines the new binding on the frame's current variables-object (the + * scope object on the scope chain designated to receive new variables) + * with 'READONLY' attribute. + * + * This is used for global scripts and also in some cases for function + * scripts where use of dynamic scoping inhibits optimization. + * Category: Variables and Scopes + * Type: Variables + * Operands: uint32_t nameIndex + * Stack: => + */ \ macro(JSOP_DEFCONST, 128,"defconst", NULL, 5, 0, 0, JOF_ATOM) \ + /* + * Defines the new binding on the frame's current variables-object (the + * scope object on the scope chain designated to receive new variables). + * + * This is used for global scripts and also in some cases for function + * scripts where use of dynamic scoping inhibits optimization. + * Category: Variables and Scopes + * Type: Variables + * Operands: uint32_t nameIndex + * Stack: => + */ \ macro(JSOP_DEFVAR, 129,"defvar", NULL, 5, 0, 0, JOF_ATOM) \ \ - /* Push a closure for a named or anonymous function expression. */ \ + /* + * Pushes a closure for a named or anonymous function expression onto the + * stack. + * Category: Statements + * Type: Function + * Operands: uint32_t funcIndex + * Stack: => obj + */ \ macro(JSOP_LAMBDA, 130, "lambda", NULL, 5, 0, 1, JOF_OBJECT) \ + /* + * Pops the top of stack value as 'this', pushes an arrow function with + * 'this' onto the stack. + * Category: Statements + * Type: Function + * Operands: uint32_t funcIndex + * Stack: this => obj + */ \ macro(JSOP_LAMBDA_ARROW, 131, "lambda_arrow", NULL, 5, 1, 1, JOF_OBJECT) \ \ - /* Used for named function expression self-naming, if lightweight. */ \ + /* + * Pushes current callee onto the stack. + * + * Used for named function expression self-naming, if lightweight. + * Category: Variables and Scopes + * Type: Arguments + * Operands: + * Stack: => callee + */ \ macro(JSOP_CALLEE, 132, "callee", NULL, 1, 0, 1, JOF_BYTE) \ \ /* @@ -515,19 +1007,32 @@ macro(JSOP_FINALLY, 135,"finally", NULL, 1, 0, 2, JOF_BYTE) \ \ /* - * An "aliased variable" is a var, let, or formal arg that is aliased. Sources - * of aliasing include: nested functions accessing the vars of an enclosing - * function, function statements that are conditionally executed, 'eval', - * 'with', and 'arguments'. All of these cases require creating a CallObject to - * own the aliased variable. + * Pushes aliased variable onto the stack. + * + * An "aliased variable" is a var, let, or formal arg that is aliased. + * Sources of aliasing include: nested functions accessing the vars of an + * enclosing function, function statements that are conditionally executed, + * 'eval', 'with', and 'arguments'. All of these cases require creating a + * CallObject to own the aliased variable. * * An ALIASEDVAR opcode contains the following immediates: * uint8 hops: the number of scope objects to skip to find the ScopeObject * containing the variable being accessed * uint24 slot: the slot containing the variable in the ScopeObject (this * 'slot' does not include RESERVED_SLOTS). + * Category: Variables and Scopes + * Type: Aliased Variables + * Operands: uint8_t hops, uint24_t slot + * Stack: => aliasedVar */ \ macro(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL, 5, 0, 1, JOF_SCOPECOORD|JOF_NAME|JOF_TYPESET) \ + /* + * Sets aliased variable as the top of stack value. + * Category: Variables and Scopes + * Type: Aliased Variables + * Operands: uint8_t hops, uint24_t slot + * Stack: v => v + */ \ macro(JSOP_SETALIASEDVAR, 137,"setaliasedvar",NULL, 5, 1, 1, JOF_SCOPECOORD|JOF_NAME|JOF_SET|JOF_DETECTING) \ \ macro(JSOP_UNUSED138, 138, "unused138", NULL, 1, 0, 0, JOF_BYTE) \ @@ -537,14 +1042,37 @@ macro(JSOP_UNUSED142, 142, "unused142", NULL, 1, 0, 0, JOF_BYTE) \ \ /* + * Pushes the value of the intrinsic onto the stack. + * * Intrinsic names are emitted instead of JSOP_*NAME ops when the - * CompileOptions flag "selfHostingMode" is set. + * 'CompileOptions' flag 'selfHostingMode' is set. * * They are used in self-hosted code to access other self-hosted values and * intrinsic functions the runtime doesn't give client JS code access to. + * Category: Variables and Scopes + * Type: Intrinsics + * Operands: uint32_t nameIndex + * Stack: => intrinsic[name] */ \ macro(JSOP_GETINTRINSIC, 143, "getintrinsic", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_TYPESET) \ + /* + * Pops the top two values on the stack as 'val' and 'scope', sets intrinsic + * as 'val', and pushes 'val' onto the stack. + * + * 'scope' is not used. + * Category: Variables and Scopes + * Type: Intrinsics + * Operands: uint32_t nameIndex + * Stack: scope, val => val + */ \ macro(JSOP_SETINTRINSIC, 144, "setintrinsic", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) \ + /* + * Pushes 'intrinsicHolder' onto the stack. + * Category: Variables and Scopes + * Type: Intrinsics + * Operands: uint32_t nameIndex + * Stack: => intrinsicHolder + */ \ macro(JSOP_BINDINTRINSIC, 145, "bindintrinsic", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_SET) \ \ /* Unused. */ \ @@ -559,17 +1087,49 @@ /* Set pending exception from the stack, to trigger rethrow. */ \ macro(JSOP_THROWING, 151,"throwing", NULL, 1, 1, 0, JOF_BYTE) \ \ - /* Set the return value pseudo-register in stack frame. */ \ + /* + * Pops the top of stack value as 'rval', sets the return value in stack + * frame as 'rval'. + * Category: Statements + * Type: Function + * Operands: + * Stack: rval => + */ \ macro(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, JOF_BYTE) \ /* - * Stop interpretation and return value set by JSOP_SETRVAL. When not set, - * returns UndefinedValue. Also emitted at end of script so interpreter - * don't need to check if opcode is still in script range. + * Stops interpretation and returns value set by JSOP_SETRVAL. When not set, + * returns 'undefined'. + * + * Also emitted at end of script so interpreter don't need to check if + * opcode is still in script range. + * Category: Statements + * Type: Function + * Operands: + * Stack: => */ \ macro(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, JOF_BYTE) \ \ - /* Free variable references that must either be found on the global or a ReferenceError */ \ + /* + * Looks up name on global scope and pushes its value onto the stack. + * + * Free variable references that must either be found on the global or a + * ReferenceError. + * Category: Variables and Scopes + * Type: Free Variables + * Operands: uint32_t nameIndex + * Stack: => val + */ \ macro(JSOP_GETGNAME, 154,"getgname", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_GNAME) \ + /* + * Pops the top two values on the stack as 'val' and 'scope', sets property + * of 'scope' as 'val' and pushes 'val' back on the stack. + * + * 'scope' should be the global scope. + * Category: Variables and Scopes + * Type: Free Variables + * Operands: uint32_t nameIndex + * Stack: scope, val => val + */ \ macro(JSOP_SETGNAME, 155,"setgname", NULL, 5, 2, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME) \ \ macro(JSOP_UNUSED156, 156, "unused156", NULL, 1, 0, 0, JOF_BYTE) \ @@ -577,7 +1137,14 @@ macro(JSOP_UNUSED158, 158, "unused158", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED159, 159, "unused159", NULL, 1, 0, 0, JOF_BYTE) \ \ - /* Regular expression literal requiring special "fork on exec" handling. */ \ + /* + * Pushes a regular expression literal onto the stack. + * It requires special "clone on exec" handling. + * Category: Literals + * Type: RegExp + * Operands: uint32_t regexpIndex + * Stack: => regexp + */ \ macro(JSOP_REGEXP, 160,"regexp", NULL, 5, 0, 1, JOF_REGEXP) \ \ macro(JSOP_UNUSED161, 161,"unused161", NULL, 1, 0, 0, JOF_BYTE) \ @@ -604,13 +1171,28 @@ macro(JSOP_UNUSED182, 182,"unused182", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED183, 183,"unused183", NULL, 1, 0, 0, JOF_BYTE) \ \ + /* + * Pops the top of stack value, pushes property of it onto the stack. + * + * Like JSOP_GETPROP but for call context. + * Category: Literals + * Type: Object + * Operands: uint32_t nameIndex + * Stack: obj => obj[name] + */ \ macro(JSOP_CALLPROP, 184,"callprop", NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) \ \ macro(JSOP_UNUSED185, 185,"unused185", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED186, 186,"unused186", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED187, 187,"unused187", NULL, 1, 0, 0, JOF_BYTE) \ \ - /* Opcode to hold 24-bit immediate integer operands. */ \ + /* + * Pushes unsigned 24-bit int immediate integer operand onto the stack. + * Category: Literals + * Type: Constants + * Operands: uint24_t val + * Stack: => val + */ \ macro(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, JOF_UINT24) \ \ macro(JSOP_UNUSED189, 189,"unused189", NULL, 1, 0, 0, JOF_BYTE) \ @@ -618,14 +1200,38 @@ macro(JSOP_UNUSED191, 191,"unused191", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED192, 192,"unused192", NULL, 1, 0, 0, JOF_BYTE) \ \ + /* + * Pops the top two values on the stack as 'propval' and 'obj', pushes + * 'propval' property of 'obj' onto the stack. + * + * Like JSOP_GETELEM but for call context. + * Category: Literals + * Type: Object + * Operands: + * Stack: obj, propval => obj[propval] + */ \ macro(JSOP_CALLELEM, 193, "callelem", NULL, 1, 2, 1, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC) \ \ - /* __proto__: v inside an object initializer. */ \ + /* + * '__proto__: v' inside an object initializer. + * + * Pops the top two values on the stack as 'newProto' and 'obj', sets + * prototype of 'obj' as 'newProto', pushes 'true' onto the stack if + * succeeded, 'false' if not. + * Category: Literals + * Type: Object + * Operands: + * Stack: obj, newProto => succeeded + */ \ macro(JSOP_MUTATEPROTO, 194, "mutateproto",NULL, 1, 2, 1, JOF_BYTE) \ \ /* - * Get an extant property value, throwing ReferenceError if the identified - * property does not exist. + * Pops the top of stack value, gets an extant property value of it, + * throwing ReferenceError if the identified property does not exist. + * Category: Literals + * Type: Object + * Operands: uint32_t nameIndex + * Stack: obj => obj[name] */ \ macro(JSOP_GETXPROP, 195,"getxprop", NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET) \ \ @@ -644,15 +1250,62 @@ macro(JSOP_TYPEOFEXPR, 197,"typeofexpr", NULL, 1, 1, 1, JOF_BYTE|JOF_DETECTING) \ \ /* Block-local scope support. */ \ + /* + * Pushes block onto the scope chain. + * Category: Variables and Scopes + * Type: Block-local Scope + * Operands: uint32_t staticBlockObjectIndex + * Stack: => + */ \ macro(JSOP_PUSHBLOCKSCOPE,198,"pushblockscope", NULL, 5, 0, 0, JOF_OBJECT) \ + /* + * Pops block from the scope chain. + * Category: Variables and Scopes + * Type: Block-local Scope + * Operands: + * Stack: => + */ \ macro(JSOP_POPBLOCKSCOPE, 199,"popblockscope", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * The opcode to assist the debugger. + * Category: Statements + * Type: Debugger + * Operands: + * Stack: => + */ \ macro(JSOP_DEBUGLEAVEBLOCK, 200,"debugleaveblock", NULL, 1, 0, 0, JOF_BYTE) \ \ macro(JSOP_UNUSED201, 201,"unused201", NULL, 1, 0, 0, JOF_BYTE) \ \ - /* Generator and array comprehension support. */ \ + /* + * Initializes generator frame, creates a generator, sets 'YIELDING' flag, + * stops interpretation and returns the generator. + * Category: Statements + * Type: Generator + * Operands: + * Stack: => + */ \ macro(JSOP_GENERATOR, 202,"generator", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pops the top of stack value as 'rval1', sets 'YIELDING' flag, + * stops interpretation and returns 'rval1', pushes sent value from + * 'send()' onto the stack. + * Category: Statements + * Type: Generator + * Operands: + * Stack: rval1 => rval2 + */ \ macro(JSOP_YIELD, 203,"yield", NULL, 1, 1, 1, JOF_BYTE) \ + /* + * Pops the top two values on the stack as 'obj' and 'v', pushes 'v' to + * 'obj'. + * + * This opcode is used for Array Comprehension. + * Category: Literals + * Type: Array + * Operands: + * Stack: v, obj => + */ \ macro(JSOP_ARRAYPUSH, 204,"arraypush", NULL, 1, 2, 0, JOF_BYTE) \ \ macro(JSOP_UNUSED205, 205, "unused205", NULL, 1, 0, 0, JOF_BYTE) \ @@ -665,19 +1318,53 @@ macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED212, 212, "unused212", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED213, 213, "unused213", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pushes the global scope onto the stack. + * + * 'nameIndex' is not used. + * Category: Variables and Scopes + * Type: Free Variables + * Operands: uint32_t nameIndex + * Stack: => global + */ \ macro(JSOP_BINDGNAME, 214, "bindgname", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) \ \ - /* Opcodes to hold 8-bit and 32-bit immediate integer operands. */ \ + /* + * Pushes 8-bit int immediate integer operand onto the stack. + * Category: Literals + * Type: Constants + * Operands: int8_t val + * Stack: => val + */ \ macro(JSOP_INT8, 215, "int8", NULL, 2, 0, 1, JOF_INT8) \ + /* + * Pushes 32-bit int immediate integer operand onto the stack. + * Category: Literals + * Type: Constants + * Operands: int32_t val + * Stack: => val + */ \ macro(JSOP_INT32, 216, "int32", NULL, 5, 0, 1, JOF_INT32) \ \ - /* Get the value of the 'length' property from a stacked value. */ \ + /* + * Pops the top of stack value, pushes the 'length' property of it onto the + * stack. + * Category: Literals + * Type: Array + * Operands: uint32_t nameIndex + * Stack: obj => obj['length'] + */ \ macro(JSOP_LENGTH, 217, "length", NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) \ \ /* - * Push a JSVAL_HOLE value onto the stack, representing an omitted property in - * an array literal (e.g. property 0 in the array [, 1]). This opcode is used - * with the JSOP_NEWARRAY opcode. + * Pushes a JS_ELEMENTS_HOLE value onto the stack, representing an omitted + * property in an array literal (e.g. property 0 in the array '[, 1]'). + * + * This opcode is used with the JSOP_NEWARRAY opcode. + * Category: Literals + * Type: Array + * Operands: + * Stack: => hole */ \ macro(JSOP_HOLE, 218, "hole", NULL, 1, 0, 1, JOF_BYTE) \ \ @@ -687,12 +1374,34 @@ macro(JSOP_UNUSED222, 222,"unused222", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED223, 223,"unused223", NULL, 1, 0, 0, JOF_BYTE) \ \ + /* + * Creates rest parameter array for current function call, and pushes it + * onto the stack. + * Category: Variables and Scopes + * Type: Arguments + * Operands: + * Stack: => rest + */ \ macro(JSOP_REST, 224, "rest", NULL, 1, 0, 1, JOF_BYTE|JOF_TYPESET) \ \ - /* Pop the stack, convert to a jsid (int or string), and push back. */ \ + /* + * Pops the top of stack value, converts it into a jsid (int or string), and + * pushes it onto the stack. + * Category: Literals + * Type: Object + * Operands: + * Stack: obj, id => obj, (jsid of id) + */ \ macro(JSOP_TOID, 225, "toid", NULL, 1, 1, 1, JOF_BYTE) \ \ - /* Push the implicit 'this' value for calls to the associated name. */ \ + /* + * Pushes the implicit 'this' value for calls to the associated name onto + * the stack. + * Category: Variables and Scopes + * Type: This + * Operands: uint32_t nameIndex + * Stack: => this + */ \ macro(JSOP_IMPLICITTHIS, 226, "implicitthis", "", 5, 0, 1, JOF_ATOM) \ \ /* diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp index c9546c28eeea..348dae49e56a 100644 --- a/js/src/vm/TraceLogging.cpp +++ b/js/src/vm/TraceLogging.cpp @@ -90,6 +90,7 @@ const char* const text[] = { "YarrCompile", "YarrInterpret", "YarrJIT", + "VM", "SplitCriticalEdges", "RenumberBlocks", "DominatorTree", @@ -109,6 +110,7 @@ const char* const text[] = { TraceLogger::TraceLogger() : enabled(false), + enabledTimes(0), failed(false), nextTextId(0), treeOffset(0), @@ -127,7 +129,7 @@ TraceLogger::init(uint32_t loggerId) if (!events.init()) return false; - JS_ASSERT(loggerId <= 999); + MOZ_ASSERT(loggerId <= 999); char dictFilename[sizeof TRACE_LOG_DIR "tl-dict.100.json"]; sprintf(dictFilename, TRACE_LOG_DIR "tl-dict.%d.json", loggerId); @@ -158,16 +160,16 @@ TraceLogger::init(uint32_t loggerId) uint64_t start = rdtsc() - traceLoggers.startupTime; TreeEntry &treeEntry = tree.pushUninitialized(); - treeEntry.start = start; - treeEntry.stop = 0; - treeEntry.u.s.textId = 0; - treeEntry.u.s.hasChildren = false; - treeEntry.nextId = 0; + treeEntry.setStart(start); + treeEntry.setStop(0); + treeEntry.setTextId(0); + treeEntry.setHasChildren(false); + treeEntry.setNextId(0); StackEntry &stackEntry = stack.pushUninitialized(); - stackEntry.treeId = 0; - stackEntry.lastChildId = 0; - stackEntry.active = true; + stackEntry.setTreeId(0); + stackEntry.setLastChildId(0); + stackEntry.setActive(true); int written = fprintf(dictFile, "["); if (written < 0) @@ -176,17 +178,127 @@ TraceLogger::init(uint32_t loggerId) // Eagerly create the default textIds, to match their Tracelogger::TextId. for (uint32_t i = 0; i < LAST; i++) { mozilla::DebugOnly textId = createTextId(text[i]); - JS_ASSERT(textId == i); + MOZ_ASSERT(textId == i); } enabled = true; + enabledTimes = 1; + return true; +} + +bool +TraceLogger::enable() +{ + if (enabled) { + enabledTimes++; + return true; + } + + if (failed) + return false; + + if (!tree.ensureSpaceBeforeAdd(stack.size())) { + if (!flush()) { + fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n"); + failed = true; + return false; + } + if (!tree.ensureSpaceBeforeAdd(stack.size())) { + fprintf(stderr, "TraceLogging: Couldn't reserve enough space.\n"); + failed = true; + return false; + } + } + + uint64_t start = rdtsc() - traceLoggers.startupTime; + StackEntry *parent = &stack[0]; + for (uint32_t i = 1; i < stack.size(); i++) { + if (!traceLoggers.isTextIdEnabled(stack[i].textId())) + continue; +#ifdef DEBUG + TreeEntry entry; + if (!getTreeEntry(parent->treeId(), &entry)) + return false; +#endif + + if (parent->lastChildId() == 0) { + MOZ_ASSERT(!entry.hasChildren()); + MOZ_ASSERT(parent->treeId() == tree.currentId() + treeOffset); + if (!updateHasChildren(parent->treeId())) { + fprintf(stderr, "TraceLogging: Couldn't update an entry.\n"); + failed = true; + return false; + } + } else { + MOZ_ASSERT(entry.hasChildren() == 1); + if (!updateNextId(parent->lastChildId(), tree.nextId() + treeOffset)) { + fprintf(stderr, "TraceLogging: Couldn't update an entry.\n"); + failed = true; + return false; + } + } + + TreeEntry &treeEntry = tree.pushUninitialized(); + treeEntry.setStart(start); + treeEntry.setStop(0); + treeEntry.setTextId(stack[i].textId()); + treeEntry.setHasChildren(false); + treeEntry.setNextId(0); + + stack[i].setActive(true); + stack[i].setTreeId(tree.currentId() + treeOffset); + + parent->setLastChildId(tree.currentId() + treeOffset); + + parent = &stack[i]; + } + + enabled = true; + enabledTimes = 1; + + return true; +} + +bool +TraceLogger::disable() +{ + if (failed) + return false; + + if (!enabled) + return true; + + if (enabledTimes > 1) { + enabledTimes--; + return true; + } + + uint64_t stop = rdtsc() - traceLoggers.startupTime; + for (uint32_t i = 1; i < stack.size(); i++) { + if (!stack[i].active()) + continue; + + if (!updateStop(stack[i].treeId(), stop)) { + fprintf(stderr, "TraceLogging: Failed to stop an event.\n"); + failed = true; + enabled = false; + return false; + } + + stack[i].setActive(false); + } + + + enabled = false; + enabledTimes = 0; + return true; } bool TraceLogger::flush() { - JS_ASSERT(!failed); + MOZ_ASSERT(!failed); if (treeFile) { // Format data in big endian. @@ -363,23 +475,23 @@ TraceLogger::logTimestamp(uint32_t id) void TraceLogger::entryToBigEndian(TreeEntry *entry) { - entry->start = htobe64(entry->start); - entry->stop = htobe64(entry->stop); - entry->u.value = htobe32((entry->u.s.textId << 1) + entry->u.s.hasChildren); - entry->nextId = htobe32(entry->nextId); + entry->start_ = htobe64(entry->start_); + entry->stop_ = htobe64(entry->stop_); + entry->u.value_ = htobe32((entry->u.s.textId_ << 1) + entry->u.s.hasChildren_); + entry->nextId_ = htobe32(entry->nextId_); } void TraceLogger::entryToSystemEndian(TreeEntry *entry) { - entry->start = be64toh(entry->start); - entry->stop = be64toh(entry->stop); + entry->start_ = be64toh(entry->start_); + entry->stop_ = be64toh(entry->stop_); - uint32_t data = be32toh(entry->u.value); - entry->u.s.textId = data >> 1; - entry->u.s.hasChildren = data & 0x1; + uint32_t data = be32toh(entry->u.value_); + entry->u.s.textId_ = data >> 1; + entry->u.s.hasChildren_ = data & 0x1; - entry->nextId = be32toh(entry->nextId); + entry->nextId_ = be32toh(entry->nextId_); } bool @@ -426,13 +538,13 @@ TraceLogger::updateHasChildren(uint32_t treeId, bool hasChildren) TreeEntry entry; if (!getTreeEntry(treeId, &entry)) return false; - entry.u.s.hasChildren = hasChildren; + entry.setHasChildren(hasChildren); if (!saveTreeEntry(treeId, &entry)) return false; return true; } - tree[treeId - treeOffset].u.s.hasChildren = hasChildren; + tree[treeId - treeOffset].setHasChildren(hasChildren); return true; } @@ -443,13 +555,13 @@ TraceLogger::updateNextId(uint32_t treeId, uint32_t nextId) TreeEntry entry; if (!getTreeEntry(treeId, &entry)) return false; - entry.nextId = nextId; + entry.setNextId(nextId); if (!saveTreeEntry(treeId, &entry)) return false; return true; } - tree[treeId - treeOffset].nextId = nextId; + tree[treeId - treeOffset].setNextId(nextId); return true; } @@ -460,20 +572,20 @@ TraceLogger::updateStop(uint32_t treeId, uint64_t timestamp) TreeEntry entry; if (!getTreeEntry(treeId, &entry)) return false; - entry.stop = timestamp; + entry.setStop(timestamp); if (!saveTreeEntry(treeId, &entry)) return false; return true; } - tree[treeId - treeOffset].stop = timestamp; + tree[treeId - treeOffset].setStop(timestamp); return true; } void TraceLogger::startEvent(uint32_t id) { - if (!enabled) + if (failed) return; if (!stack.ensureSpaceBeforeAdd()) { @@ -483,6 +595,15 @@ TraceLogger::startEvent(uint32_t id) return; } + if (!enabled) { + StackEntry &stackEntry = stack.pushUninitialized(); + stackEntry.setTreeId(tree.currentId() + treeOffset); + stackEntry.setLastChildId(0); + stackEntry.setTextId(id); + stackEntry.setActive(false); + return; + } + if (!tree.ensureSpaceBeforeAdd()) { uint64_t start = rdtsc() - traceLoggers.startupTime; if (!flush()) { @@ -516,7 +637,7 @@ TraceLogger::StackEntry & TraceLogger::getActiveAncestor() { uint32_t parentId = stack.currentId(); - while (!stack[parentId].active) + while (!stack[parentId].active()) parentId--; return stack[parentId]; } @@ -529,7 +650,7 @@ TraceLogger::startEvent(uint32_t id, uint64_t timestamp) // the stop event. if (!traceLoggers.isTextIdEnabled(id)) { StackEntry &stackEntry = stack.pushUninitialized(); - stackEntry.active = false; + stackEntry.setActive(false); return true; } @@ -540,39 +661,39 @@ TraceLogger::startEvent(uint32_t id, uint64_t timestamp) StackEntry &parent = getActiveAncestor(); #ifdef DEBUG TreeEntry entry; - if (!getTreeEntry(parent.treeId, &entry)) + if (!getTreeEntry(parent.treeId(), &entry)) return false; #endif - if (parent.lastChildId == 0) { - JS_ASSERT(entry.u.s.hasChildren == 0); - JS_ASSERT(parent.treeId == tree.currentId() + treeOffset); + if (parent.lastChildId() == 0) { + MOZ_ASSERT(!entry.hasChildren()); + MOZ_ASSERT(parent.treeId() == tree.currentId() + treeOffset); - if (!updateHasChildren(parent.treeId)) + if (!updateHasChildren(parent.treeId())) return false; } else { - JS_ASSERT(entry.u.s.hasChildren == 1); + MOZ_ASSERT(entry.hasChildren()); - if (!updateNextId(parent.lastChildId, tree.nextId() + treeOffset)) + if (!updateNextId(parent.lastChildId(), tree.nextId() + treeOffset)) return false; } // Add a new tree entry. TreeEntry &treeEntry = tree.pushUninitialized(); - treeEntry.start = timestamp; - treeEntry.stop = 0; - treeEntry.u.s.textId = id; - treeEntry.u.s.hasChildren = false; - treeEntry.nextId = 0; + treeEntry.setStart(timestamp); + treeEntry.setStop(0); + treeEntry.setTextId(id); + treeEntry.setHasChildren(false); + treeEntry.setNextId(0); // Add a new stack entry. StackEntry &stackEntry = stack.pushUninitialized(); - stackEntry.treeId = tree.currentId() + treeOffset; - stackEntry.lastChildId = 0; - stackEntry.active = true; + stackEntry.setTreeId(tree.currentId() + treeOffset); + stackEntry.setLastChildId(0); + stackEntry.setActive(true); // Set the last child of the parent to this newly added entry. - parent.lastChildId = tree.currentId() + treeOffset; + parent.setLastChildId(tree.currentId() + treeOffset); return true; } @@ -582,8 +703,8 @@ TraceLogger::stopEvent(uint32_t id) { #ifdef DEBUG TreeEntry entry; - JS_ASSERT(getTreeEntry(stack.current().treeId, &entry)); - JS_ASSERT(entry.u.s.textId == id); + MOZ_ASSERT_IF(stack.current().active(), getTreeEntry(stack.current().treeId(), &entry)); + MOZ_ASSERT_IF(stack.current().active(), entry.textId() == id); #endif stopEvent(); } @@ -591,12 +712,9 @@ TraceLogger::stopEvent(uint32_t id) void TraceLogger::stopEvent() { - if (!enabled) - return; - - if (stack.current().active) { + if (enabled && stack.current().active()) { uint64_t stop = rdtsc() - traceLoggers.startupTime; - if (!updateStop(stack.current().treeId, stop)) { + if (!updateStop(stack.current().treeId(), stop)) { fprintf(stderr, "TraceLogging: Failed to stop an event.\n"); enabled = false; failed = true; @@ -610,6 +728,8 @@ TraceLogging::TraceLogging() { initialized = false; enabled = false; + mainThreadEnabled = true; + offThreadEnabled = true; loggerId = 0; #ifdef JS_THREADSAFE @@ -682,7 +802,7 @@ TraceLogging::lazyInit() const char *env = getenv("TLLOG"); if (!env) - return false; + env = ""; if (strstr(env, "help")) { fflush(nullptr); @@ -749,6 +869,28 @@ TraceLogging::lazyInit() enabledTextIds[TraceLogger::EliminateRedundantChecks] = true; } + const char *options = getenv("TLOPTIONS"); + if (options) { + if (strstr(options, "help")) { + fflush(nullptr); + printf( + "\n" + "usage: TLOPTIONS=option,option,option,... where options can be:\n" + "\n" + " DisableMainThread Don't start logging the mainThread automatically.\n" + " DisableOffThread Don't start logging the off mainThread automatically.\n" + ); + printf("\n"); + exit(0); + /*NOTREACHED*/ + } + + if (strstr(options, "DisableMainThread")) + mainThreadEnabled = false; + if (strstr(options, "DisableOffThread")) + offThreadEnabled = false; + } + startupTime = rdtsc(); enabled = true; return true; @@ -792,6 +934,9 @@ TraceLogging::forMainThread(PerThreadData *mainThread) if (!mainThreadLoggers.append(logger)) return nullptr; + + if (!mainThreadEnabled) + logger->disable(); } return mainThread->traceLogger; @@ -830,6 +975,9 @@ TraceLogging::forThread(PRThread *thread) return nullptr; } + if (!offThreadEnabled) + logger->disable(); + return logger; } #endif // JS_THREADSAFE @@ -867,3 +1015,9 @@ TraceLogging::create() return logger; } + +bool +js::TraceLogTextIdEnabled(uint32_t textId) +{ + return traceLoggers.isTextIdEnabled(textId); +} diff --git a/js/src/vm/TraceLogging.h b/js/src/vm/TraceLogging.h index cd2a251e37a0..c6a7fd920967 100644 --- a/js/src/vm/TraceLogging.h +++ b/js/src/vm/TraceLogging.h @@ -152,7 +152,7 @@ class ContinuousSpace { } uint32_t currentId() { - JS_ASSERT(next_ > 0); + MOZ_ASSERT(next_ > 0); return next_ - 1; } @@ -160,11 +160,13 @@ class ContinuousSpace { return data()[currentId()]; } - bool ensureSpaceBeforeAdd() { - if (next_ < capacity_) + bool ensureSpaceBeforeAdd(uint32_t count = 1) { + if (next_ + count <= capacity_) return true; uint32_t nCapacity = capacity_ * 2; + if (next_ + count > nCapacity) + nCapacity = next_ + count; T *entries = (T *) js_realloc(data_, nCapacity * sizeof(T)); if (!entries) @@ -187,11 +189,12 @@ class ContinuousSpace { } T &pushUninitialized() { + MOZ_ASSERT(next_ < capacity_); return data()[next_++]; } void pop() { - JS_ASSERT(next_ > 0); + MOZ_ASSERT(next_ > 0); next_--; } @@ -227,6 +230,7 @@ class TraceLogger YarrCompile, YarrInterpret, YarrJIT, + VM, // Specific passes during ion compilation: SplitCriticalEdges, @@ -258,40 +262,102 @@ class TraceLogger // The layout of the tree in memory and in the log file. Readable by JS // using TypedArrays. struct TreeEntry { - uint64_t start; - uint64_t stop; + uint64_t start_; + uint64_t stop_; union { struct { - uint32_t textId: 31; - uint32_t hasChildren: 1; + uint32_t textId_: 31; + uint32_t hasChildren_: 1; } s; - uint32_t value; + uint32_t value_; } u; - uint32_t nextId; + uint32_t nextId_; TreeEntry(uint64_t start, uint64_t stop, uint32_t textId, bool hasChildren, uint32_t nextId) { - this->start = start; - this->stop = stop; - this->u.s.textId = textId; - this->u.s.hasChildren = hasChildren; - this->nextId = nextId; + start_ = start; + stop_ = stop; + u.s.textId_ = textId; + u.s.hasChildren_ = hasChildren; + nextId_ = nextId; } TreeEntry() { } + uint64_t start() { + return start_; + } + uint64_t stop() { + return stop_; + } + uint32_t textId() { + return u.s.textId_; + } + bool hasChildren() { + return u.s.hasChildren_; + } + uint32_t nextId() { + return nextId_; + } + void setStart(uint64_t start) { + start_ = start; + } + void setStop(uint64_t stop) { + stop_ = stop; + } + void setTextId(uint32_t textId) { + MOZ_ASSERT(textId < uint32_t(1<<31) ); + u.s.textId_ = textId; + } + void setHasChildren(bool hasChildren) { + u.s.hasChildren_ = hasChildren; + } + void setNextId(uint32_t nextId) { + nextId_ = nextId; + } }; // Helper structure for keeping track of the current entries in // the tree. Pushed by `start(id)`, popped by `stop(id)`. The active flag // is used to know if a subtree doesn't need to get logged. struct StackEntry { - uint32_t treeId; - uint32_t lastChildId; - bool active; + uint32_t treeId_; + uint32_t lastChildId_; + struct { + uint32_t textId_: 31; + uint32_t active_: 1; + } s; StackEntry(uint32_t treeId, uint32_t lastChildId, bool active = true) - : treeId(treeId), lastChildId(lastChildId), active(active) - { } + : treeId_(treeId), lastChildId_(lastChildId) + { + s.textId_ = 0; + s.active_ = active; + } + uint32_t treeId() { + return treeId_; + } + uint32_t lastChildId() { + return lastChildId_; + } + uint32_t textId() { + return s.textId_; + } + bool active() { + return s.active_; + } + void setTreeId(uint32_t treeId) { + treeId_ = treeId; + } + void setLastChildId(uint32_t lastChildId) { + lastChildId_ = lastChildId; + } + void setTextId(uint32_t textId) { + MOZ_ASSERT(textId < uint32_t(1<<31) ); + s.textId_ = textId; + } + void setActive(bool active) { + s.active_ = active; + } }; // The layout of the event log in memory and in the log file. @@ -309,6 +375,7 @@ class TraceLogger FILE *eventFile; bool enabled; + uint32_t enabledTimes; bool failed; uint32_t nextTextId; @@ -355,6 +422,9 @@ class TraceLogger bool init(uint32_t loggerId); + bool enable(); + bool disable(); + // The createTextId functions map a unique input to a logger ID. // This ID can be used to log something. Calls to these functions should be // limited if possible, because of the overhead. @@ -396,6 +466,8 @@ class TraceLogging bool initialized; bool enabled; bool enabledTextIds[TraceLogger::LAST]; + bool mainThreadEnabled; + bool offThreadEnabled; #ifdef JS_THREADSAFE ThreadLoggerHashMap threadLoggers; #endif // JS_THREADSAFE @@ -447,6 +519,21 @@ inline TraceLogger *TraceLoggerForCurrentThread() { }; #endif +inline bool TraceLoggerEnable(TraceLogger *logger) { +#ifdef JS_TRACE_LOGGING + if (logger) + return logger->enable(); +#endif + return false; +} +inline bool TraceLoggerDisable(TraceLogger *logger) { +#ifdef JS_TRACE_LOGGING + if (logger) + return logger->disable(); +#endif + return false; +} + inline uint32_t TraceLogCreateTextId(TraceLogger *logger, JSScript *script) { #ifdef JS_TRACE_LOGGING if (logger) @@ -470,6 +557,13 @@ inline uint32_t TraceLogCreateTextId(TraceLogger *logger, const char *text) { #endif return TraceLogger::TL_Error; } +#ifdef JS_TRACE_LOGGING +bool TraceLogTextIdEnabled(uint32_t textId); +#else +inline bool TraceLogTextIdEnabled(uint32_t textId) { + return false; +} +#endif inline void TraceLogTimestamp(TraceLogger *logger, uint32_t textId) { #ifdef JS_TRACE_LOGGING if (logger) diff --git a/js/src/vm/make_opcode_doc.py b/js/src/vm/make_opcode_doc.py index 9ed192a324e1..6e79be829547 100644 --- a/js/src/vm/make_opcode_doc.py +++ b/js/src/vm/make_opcode_doc.py @@ -285,7 +285,7 @@ def format_flags(flags): return ' ({flags})'.format(flags=flags) def print_opcode(opcode): - names_template = '{name} [-{nuses}, +{ndefs}] ({flags})' + names_template = '{name} [-{nuses}, +{ndefs}]{flags}' names = map(lambda code: names_template.format(name=escape(code.name), nuses=override(code.nuses, opcode.nuses_override), diff --git a/js/xpconnect/src/XPCVariant.cpp b/js/xpconnect/src/XPCVariant.cpp index 5516b0605827..00ef92498e82 100644 --- a/js/xpconnect/src/XPCVariant.cpp +++ b/js/xpconnect/src/XPCVariant.cpp @@ -69,7 +69,7 @@ XPCTraceableVariant::~XPCTraceableVariant() void XPCTraceableVariant::TraceJS(JSTracer* trc) { - MOZ_ASSERT(JSVAL_IS_TRACEABLE(mJSVal)); + MOZ_ASSERT(mJSVal.isMarkable()); JS_SET_TRACING_DETAILS(trc, GetTraceName, this, 0); JS_CallHeapValueTracer(trc, &mJSVal, "XPCTraceableVariant::mJSVal"); } @@ -115,7 +115,7 @@ XPCVariant::newVariant(JSContext* cx, jsval aJSVal) { nsRefPtr variant; - if (!JSVAL_IS_TRACEABLE(aJSVal)) + if (!aJSVal.isMarkable()) variant = new XPCVariant(cx, aJSVal); else variant = new XPCTraceableVariant(cx, aJSVal); diff --git a/layout/base/nsDisplayListInvalidation.cpp b/layout/base/nsDisplayListInvalidation.cpp index 8ff8ad324849..514f652eed54 100644 --- a/layout/base/nsDisplayListInvalidation.cpp +++ b/layout/base/nsDisplayListInvalidation.cpp @@ -5,6 +5,7 @@ #include "nsDisplayListInvalidation.h" #include "nsDisplayList.h" +#include "nsIFrame.h" nsDisplayItemGeometry::nsDisplayItemGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder) { diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 05f9db31ea35..e92a39bc1f40 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -5387,8 +5387,10 @@ nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement, uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE; if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION) frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION; - if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) + if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) { frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; + result.mIsPremultiplied = false; + } int32_t imgWidth, imgHeight; rv = imgContainer->GetWidth(&imgWidth); @@ -5450,52 +5452,28 @@ nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement* aElement, DrawTarget* aTarget) { SurfaceFromElementResult result; - nsresult rv; - bool premultAlpha = (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) == 0; + bool* isPremultiplied = nullptr; + if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) { + isPremultiplied = &result.mIsPremultiplied; + } gfxIntSize size = aElement->GetSize(); - if (premultAlpha && aElement->CountContexts() == 1) { - nsICanvasRenderingContextInternal *srcCanvas = aElement->GetContextAtIndex(0); - result.mSourceSurface = srcCanvas->GetSurfaceSnapshot(); - } - + result.mSourceSurface = aElement->GetSurfaceSnapshot(isPremultiplied); if (!result.mSourceSurface) { - nsRefPtr ctx; - RefPtr dt; - if (premultAlpha) { - if (aTarget) { - dt = aTarget->CreateSimilarDrawTarget(IntSize(size.width, size.height), SurfaceFormat::B8G8R8A8); - } else { - dt = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(size.width, size.height), - SurfaceFormat::B8G8R8A8); - } - if (!dt) { - return result; - } - ctx = new gfxContext(dt); - } else { - // TODO: RenderContextsExternal expects to get a gfxImageFormat - // so that it can un-premultiply. - RefPtr data = Factory::CreateDataSourceSurface(IntSize(size.width, size.height), - SurfaceFormat::B8G8R8A8); - memset(data->GetData(), 0, data->Stride() * size.height); - result.mSourceSurface = data; - nsRefPtr image = new gfxImageSurface(data->GetData(), - gfxIntSize(size.width, size.height), - data->Stride(), - gfxImageFormat::ARGB32); - ctx = new gfxContext(image); - } - // XXX shouldn't use the external interface, but maybe we can layerify this - uint32_t flags = premultAlpha ? HTMLCanvasElement::RenderFlagPremultAlpha : 0; - rv = aElement->RenderContextsExternal(ctx, GraphicsFilter::FILTER_NEAREST, flags); - if (NS_FAILED(rv)) - return result; - - if (premultAlpha) { - result.mSourceSurface = dt->Snapshot(); + // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just + // draw nothing, so return an empty surface. + DrawTarget *ref = aTarget ? aTarget : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); + RefPtr dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height), + SurfaceFormat::B8G8R8A8); + if (dt) { + result.mSourceSurface = dt->Snapshot(); + } + } else if (aTarget) { + RefPtr opt = aTarget->OptimizeSourceSurface(result.mSourceSurface); + if (opt) { + result.mSourceSurface = opt; } } @@ -5517,7 +5495,7 @@ nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement, { SurfaceFromElementResult result; - NS_WARN_IF_FALSE((aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!"); + NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!"); uint16_t readyState; if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) && @@ -5541,6 +5519,13 @@ nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement, if (!result.mSourceSurface) return result; + if (aTarget) { + RefPtr opt = aTarget->OptimizeSourceSurface(result.mSourceSurface); + if (opt) { + result.mSourceSurface = opt; + } + } + result.mCORSUsed = aElement->GetCORSMode() != CORS_NONE; result.mSize = ThebesIntSize(size); result.mPrincipal = principal.forget(); @@ -6509,7 +6494,10 @@ nsLayoutUtils::WantSubAPZC() nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult() // Use safe default values here - : mIsWriteOnly(true), mIsStillLoading(false), mCORSUsed(false) + : mIsWriteOnly(true) + , mIsStillLoading(false) + , mCORSUsed(false) + , mIsPremultiplied(true) { } diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 1f4e9aa1fa3f..519be8dd52f7 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1695,10 +1695,10 @@ public: SFE_WANT_FIRST_FRAME = 1 << 1, /* Whether we should skip colorspace/gamma conversion */ SFE_NO_COLORSPACE_CONVERSION = 1 << 2, - /* Whether we should skip premultiplication -- the resulting - image will always be an image surface, and must not be given to - Thebes for compositing! */ - SFE_NO_PREMULTIPLY_ALPHA = 1 << 3, + /* Specifies that the caller wants unpremultiplied pixel data. + If this is can be done efficiently, the result will be a + DataSourceSurface and mIsPremultiplied with be set to false. */ + SFE_PREFER_NO_PREMULTIPLY_ALPHA = 1 << 3, /* Whether we should skip getting a surface for vector images and return a DirectDrawInfo containing an imgIContainer instead. */ SFE_NO_RASTERIZING_VECTORS = 1 << 4 @@ -1736,6 +1736,8 @@ public: bool mIsStillLoading; /* Whether the element used CORS when loading. */ bool mCORSUsed; + /* Whether the returned image contains premultiplied pixel data */ + bool mIsPremultiplied; }; static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::Element *aElement, diff --git a/layout/generic/crashtests/455407.html b/layout/generic/crashtests/455407.html deleted file mode 100644 index 54d389ce1848..000000000000 --- a/layout/generic/crashtests/455407.html +++ /dev/null @@ -1,13 +0,0 @@ - -Crash [@ nsSubDocumentFrame::Reflow] with generated content and resizing iframe - - - - - - diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index b5f5c9a78438..f9956f5edd35 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -300,7 +300,6 @@ load 453762-1.html load 455171-1.html load 455171-2.html load 455171-3.html -load 455407.html load 455643-1.xhtml load 457375.html load 457380-1.html diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 8f0915c615f0..536921a7776a 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -6690,8 +6690,8 @@ nsFrame::BreakWordBetweenPunctuation(const PeekWordState* aState, } if (!Preferences::GetBool("layout.word_select.stop_at_punctuation")) { // When this pref is false, we never stop at a punctuation boundary unless - // it's after whitespace - return false; + // it's followed by whitespace (in the relevant direction). + return aWhitespaceAfter; } if (!aIsKeyboardSelect) { // mouse caret movement (e.g. word selection) always stops at every punctuation boundary diff --git a/layout/generic/test/test_movement_by_words.html b/layout/generic/test/test_movement_by_words.html index b71820d8fa03..8f5402b5d501 100644 --- a/layout/generic/test/test_movement_by_words.html +++ b/layout/generic/test/test_movement_by_words.html @@ -14,7 +14,7 @@

Catch-all