From d8dd019790cbeb5a0607ce3ae9941f261e267379 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 16 Oct 2014 13:54:35 -0400 Subject: [PATCH 01/81] Bug 1083931. Remove pedantic filtering that snuck back in. The ANGLE update from bug 801158 accidentally added this back. --HG-- extra : rebase_source : 492105a4a518c0c4cb78e0ccc64efbe0ffc0d78e --- gfx/angle/Makefile.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gfx/angle/Makefile.in b/gfx/angle/Makefile.in index 6beb97fd2477..da63a55ca4b3 100644 --- a/gfx/angle/Makefile.in +++ b/gfx/angle/Makefile.in @@ -17,8 +17,3 @@ endif include $(topsrcdir)/config/rules.mk -# We have to filter out -pedantic, because of -# comma-at-end-of-enumerator list failures. We can try to get this fixed -# upstream at some point. -CXXFLAGS := $(filter-out -pedantic,$(CXXFLAGS)) -CFLAGS := $(filter-out -pedantic,$(CFLAGS)) From 2239ca42294085c6d9a5e270a39b5858e3ff220b Mon Sep 17 00:00:00 2001 From: George Wright Date: Fri, 10 Oct 2014 11:46:27 -0400 Subject: [PATCH 02/81] Bug 1049551 - Add a tab delay spinner that's visible if we switch tabs before the content has been drawn r=dao --- browser/base/content/tabbrowser.css | 10 ++++++++ browser/base/content/tabbrowser.xml | 36 +++++++++++++++++++++++++-- toolkit/themes/linux/global/jar.mn | 1 + toolkit/themes/osx/global/jar.mn | 1 + toolkit/themes/shared/spinner.png | Bin 0 -> 158914 bytes toolkit/themes/windows/global/jar.mn | 1 + 6 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 toolkit/themes/shared/spinner.png diff --git a/browser/base/content/tabbrowser.css b/browser/base/content/tabbrowser.css index bc5860e7114a..da3b1e3d779c 100644 --- a/browser/base/content/tabbrowser.css +++ b/browser/base/content/tabbrowser.css @@ -74,3 +74,13 @@ tabpanels { browser[pending] { display: none; } + +browser[pendingpaint] { + opacity: 0; +} + +tabbrowser[pendingpaint] { + background-image: url(chrome://global/skin/spinner.png); + background-repeat: no-repeat; + background-position: center center; +} diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 014b8a1762f8..f48e358dea7f 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -136,6 +136,10 @@ "" + + 0 + + + + + + + + + + + + @@ -3266,16 +3295,19 @@ let timeoutPromise = new Promise((aResolve, aReject) => { timeoutId = setTimeout(() => { + this._showBusySpinnerRemoteBrowser(toBrowser); attemptTabSwitch(aResolve, aReject); }, kTabSwitchTimeout); }); let paintPromise = new Promise((aResolve, aReject) => { - toBrowser.addEventListener("MozAfterRemotePaint", function onRemotePaint() { + let onRemotePaint = () => { toBrowser.removeEventListener("MozAfterRemotePaint", onRemotePaint); + this._hideBusySpinnerRemoteBrowser(toBrowser); clearTimeout(timeoutId); attemptTabSwitch(aResolve, aReject); - }); + }; + toBrowser.addEventListener("MozAfterRemotePaint", onRemotePaint); toBrowser.QueryInterface(Ci.nsIFrameLoaderOwner) .frameLoader .requestNotifyAfterRemotePaint(); diff --git a/toolkit/themes/linux/global/jar.mn b/toolkit/themes/linux/global/jar.mn index 36d1f7535453..7e01edce4feb 100644 --- a/toolkit/themes/linux/global/jar.mn +++ b/toolkit/themes/linux/global/jar.mn @@ -67,3 +67,4 @@ toolkit.jar: skin/classic/global/in-content/sorter.png (../../shared/in-content/sorter.png) skin/classic/global/in-content/sorter@2x.png (../../shared/in-content/sorter@2x.png) + skin/classic/global/toolbar/spring.png (toolbar/spring.png) + skin/classic/global/spinner.png (../../shared/spinner.png) diff --git a/toolkit/themes/osx/global/jar.mn b/toolkit/themes/osx/global/jar.mn index 02f70da4f060..03b25d1814c0 100644 --- a/toolkit/themes/osx/global/jar.mn +++ b/toolkit/themes/osx/global/jar.mn @@ -206,3 +206,4 @@ toolkit.jar: skin/classic/global/tree/columnpicker.gif (tree/columnpicker.gif) skin/classic/global/tree/folder.png (tree/folder.png) skin/classic/global/tree/folder@2x.png (tree/folder@2x.png) + skin/classic/global/spinner.png (../../shared/spinner.png) diff --git a/toolkit/themes/shared/spinner.png b/toolkit/themes/shared/spinner.png new file mode 100644 index 0000000000000000000000000000000000000000..82bac53254238649a49d41e984d65ee9e41331e5 GIT binary patch literal 158914 zcmZs@byQSu*fl&fA}Og-3Ia+iodQzQA>G~GEea?IC%ef7qDtj1)2Mh8KP} zNi8=uM+-MkV;6ITn1i*wv75OWEqszz!o}R!&C!L{gOiPi?J+Hrr>7^Ila-UHow2K{ zxhtEai{(SNp3dk0=OpUprdAHtrp9)()(#eqE^r=eM+a8~H`^1o$NYk{ChiVqc5r5< z$84Nz9JHp6_D*nwiJkewTcYY4a1v}|Q+0WG#f3kFd;=>Hyxg()pZBic3i{u_>whB< zI%sPM1o<49GXg>JN%onTx>w3}YM>s8`(=md{;TQnaeeyxPoL4o92lEfOnew_wMfW5 zW?c;b%~BcLFW+!WTx~uhug&;jD3S3!j1leG+I_{o2=<$BJ#1+ z)alg3i%X&d<)-EUkHqou*Q@DUz7__*>McdmkI(i`>P4|BUT%$*ON>rT)c)OD?v6!4 z&~kAdtc{hMe$^<}IeaxW;fHs>8etflnyT-z(DwELWk-hQRVppE!$hU^-C3{2$Q~E# z*jGf$2;FLj3sVJ!o}-q44i0&*KP^~+=^sA)B{$rNAs%YziWHNTygr=pjlwuL%h7=WqDI%%UM)kbJuInrXnd4W6a;pRGED8I6rA`R zKbzkPG-G2W>@Sd#lD@RJw+~58O(jU~S&=E$nDic0;Jsj^r#G|r@i`4#8e-tl{brXq zI5kzDT~&4H@pWAOnUs{rkG3|-cM%bCa!N|8OTQmXl7B8LvJ=y4ymoVyaJWR@Rb%;pVMmg=8LKIh7$>#a!3s zS@<+kDNRUw*;KoUHUf==oLtoQaboC}wIC&ad&o`lpMKf0j~dT*>K0n414L&7uP#2G zpP!@C$kt+N|F>wI6m>^OM_rPVlI(KD(vL<;^pj^NChjct_Aa};gnKz@>~|Bc;6rpc z-z)p3prBCm;K73m4~>S5&!1@zIzP}c8*fwLV;u++5o=Qh(Y~QEwr3@iD;u_9eXkl# zk|ON;ZSQ9sGkQ^jPO%6>c3R8(c9cq;Z8k6fiS(SDoNRb}P_?-gTdZ3x;&iY!5N5nx zrBh7D$LCf3F$q(G3`<5%PVg=jRRhh)%)ApJ6%|!e68=1a==q=Wx~SRVU!OQlcWp}> zEaH`af9@p1jG_|ciKLgkS#)u-ze*FZ+i*-Bry5IPW@*V@Tv{6avWfr?&%Cv@HC~R% zTE@&ZXlQ7tWNmFNwd^REHfVpfuZb=@XI@oRHJC=wgQYLlG)E~tEjBT+OD4OEsv-&_ zZ+Ii+^XJbVTE4MeTrKIy)*nB9n0zWLO_z;jNS&_t-sgG9T_d4^B?FdWVrpu5xb&&9 ztbhA^W9j%edtG&V`|bN$XF)*-LK>Q@`qwd^-}`epjaQhbOG!ylzpSd(VrdZ;7Ou-0 zTEaU#JPfp;z!Wg#^glhg@?MPOZTz50j9VcHe*f&*vn)(>^rvAVmc1$3S~@xcvd
    06OVO|M&I2H&0LQ3A1Vj+Ppx&&!VSI!y7o>gi2A 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

    qlMr;%u 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 literal 0 HcmV?d00001 diff --git a/toolkit/themes/windows/global/jar.mn b/toolkit/themes/windows/global/jar.mn index 9494faf9851f..070f60f11fe9 100644 --- a/toolkit/themes/windows/global/jar.mn +++ b/toolkit/themes/windows/global/jar.mn @@ -201,6 +201,7 @@ toolkit.jar: skin/classic/global/tree/sort-dsc-classic.png (tree/sort-dsc-classic.png) skin/classic/global/tree/twisty-clsd.png (tree/twisty-clsd.png) skin/classic/global/tree/twisty-open.png (tree/twisty-open.png) + skin/classic/global/spinner.png (../../shared/spinner.png) #ifdef XP_WIN toolkit.jar: From d90c3a1c7df3c9523c72298dc4e359ffe8a62515 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Thu, 16 Oct 2014 19:08:32 +0200 Subject: [PATCH 03/81] Bug 1077301 - Simplify the gralloc texture code. r=sotaro --- gfx/layers/Compositor.h | 15 - gfx/layers/composite/CompositableHost.cpp | 36 +- gfx/layers/composite/CompositableHost.h | 50 --- gfx/layers/composite/ContentHost.cpp | 271 ++++++++++++--- gfx/layers/composite/ContentHost.h | 46 ++- gfx/layers/composite/ImageHost.cpp | 61 ++-- gfx/layers/composite/ImageHost.h | 5 +- gfx/layers/composite/TextureHost.cpp | 31 +- gfx/layers/composite/TextureHost.h | 84 ++++- gfx/layers/opengl/GrallocTextureHost.cpp | 398 +++++++++++----------- gfx/layers/opengl/GrallocTextureHost.h | 43 ++- gfx/layers/opengl/TextureHostOGL.cpp | 231 +++---------- gfx/layers/opengl/TextureHostOGL.h | 128 ++----- 13 files changed, 681 insertions(+), 718 deletions(-) diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h index 035bae513080..868d67e695af 100644 --- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -135,17 +135,6 @@ enum SurfaceInitMode INIT_MODE_CLEAR }; -/** - * A base class for a platform-dependent helper for use by TextureHost. - */ -class CompositorBackendSpecificData -{ - NS_INLINE_DECL_REFCOUNTING(CompositorBackendSpecificData) - -protected: - virtual ~CompositorBackendSpecificData() {} -}; - /** * Common interface for compositor backends. * @@ -481,10 +470,6 @@ public: return fillRatio; } - virtual CompositorBackendSpecificData* GetCompositorBackendSpecificData() { - return nullptr; - } - ScreenRotation GetScreenRotation() const { return mScreenRotation; } diff --git a/gfx/layers/composite/CompositableHost.cpp b/gfx/layers/composite/CompositableHost.cpp index 2bbbd78d82ad..645e0f813cb6 100644 --- a/gfx/layers/composite/CompositableHost.cpp +++ b/gfx/layers/composite/CompositableHost.cpp @@ -24,14 +24,6 @@ namespace layers { class Compositor; -CompositableBackendSpecificData::CompositableBackendSpecificData() - : mAllowSharingTextureHost(false) -{ - static uint64_t sNextID = 1; - ++sNextID; - mId = sNextID; -} - /** * IPDL actor used by CompositableHost to match with its corresponding * CompositableClient on the content side. @@ -87,9 +79,6 @@ CompositableHost::CompositableHost(const TextureInfo& aTextureInfo) CompositableHost::~CompositableHost() { MOZ_COUNT_DTOR(CompositableHost); - if (mBackendData) { - mBackendData->ClearData(); - } } PCompositableParent* @@ -121,7 +110,6 @@ CompositableHost::UseTextureHost(TextureHost* aTexture) return; } aTexture->SetCompositor(GetCompositor()); - aTexture->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); } void @@ -130,17 +118,12 @@ CompositableHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack, { MOZ_ASSERT(aTextureOnBlack && aTextureOnWhite); aTextureOnBlack->SetCompositor(GetCompositor()); - aTextureOnBlack->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); aTextureOnWhite->SetCompositor(GetCompositor()); - aTextureOnWhite->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); } void CompositableHost::RemoveTextureHost(TextureHost* aTexture) -{ - // Clear strong refrence to CompositableBackendSpecificData - aTexture->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); -} +{} void CompositableHost::SetCompositor(Compositor* aCompositor) @@ -153,7 +136,7 @@ CompositableHost::AddMaskEffect(EffectChain& aEffects, const gfx::Matrix4x4& aTransform, bool aIs3D) { - RefPtr source; + CompositableTextureSourceRef source; RefPtr host = GetAsTextureHost(); if (!host) { @@ -166,14 +149,12 @@ CompositableHost::AddMaskEffect(EffectChain& aEffects, return false; } - source = host->GetTextureSources(); - MOZ_ASSERT(source); - - if (!source) { + if (!host->BindTextureSource(source)) { NS_WARNING("The TextureHost was successfully locked but can't provide a TextureSource"); host->Unlock(); return false; } + MOZ_ASSERT(source); RefPtr effect = new EffectMask(source, source->GetSize(), @@ -192,9 +173,6 @@ CompositableHost::RemoveMaskEffect() } } -// implemented in TextureHostOGL.cpp -TemporaryRef CreateCompositableBackendSpecificDataOGL(); - /* static */ TemporaryRef CompositableHost::Create(const TextureInfo& aTextureInfo) { @@ -227,12 +205,6 @@ CompositableHost::Create(const TextureInfo& aTextureInfo) default: NS_ERROR("Unknown CompositableType"); } - // We know that Tiled buffers don't use the compositable backend-specific - // data, so don't bother creating it. - if (result && aTextureInfo.mCompositableType != CompositableType::BUFFER_TILED) { - RefPtr data = CreateCompositableBackendSpecificDataOGL(); - result->SetCompositableBackendSpecificData(data); - } return result; } diff --git a/gfx/layers/composite/CompositableHost.h b/gfx/layers/composite/CompositableHost.h index 4a2862813ef2..0c6543ad51b1 100644 --- a/gfx/layers/composite/CompositableHost.h +++ b/gfx/layers/composite/CompositableHost.h @@ -49,42 +49,6 @@ class CompositableParentManager; class PCompositableParent; struct EffectChain; -/** - * A base class for doing CompositableHost and platform dependent task on TextureHost. - */ -class CompositableBackendSpecificData -{ -protected: - virtual ~CompositableBackendSpecificData() {} - -public: - NS_INLINE_DECL_REFCOUNTING(CompositableBackendSpecificData) - - CompositableBackendSpecificData(); - - virtual void ClearData() {} - virtual void SetCompositor(Compositor* aCompositor) {} - - bool IsAllowingSharingTextureHost() - { - return mAllowSharingTextureHost; - } - - void SetAllowSharingTextureHost(bool aAllow) - { - mAllowSharingTextureHost = aAllow; - } - - uint64_t GetId() - { - return mId; - } - -public: - bool mAllowSharingTextureHost; - uint64_t mId; -}; - /** * The compositor-side counterpart to CompositableClient. Responsible for * updating textures and data about textures from IPC and how textures are @@ -112,16 +76,6 @@ public: virtual CompositableType GetType() = 0; - virtual CompositableBackendSpecificData* GetCompositableBackendSpecificData() - { - return mBackendData; - } - - virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) - { - mBackendData = aBackendData; - } - // If base class overrides, it should still call the parent implementation virtual void SetCompositor(Compositor* aCompositor); @@ -252,9 +206,6 @@ public: SetLayer(nullptr); mAttached = false; mKeepAttached = false; - if (mBackendData) { - mBackendData->ClearData(); - } } } bool IsAttached() { return mAttached; } @@ -314,7 +265,6 @@ protected: uint64_t mCompositorID; RefPtr mCompositor; Layer* mLayer; - RefPtr mBackendData; uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true. bool mAttached; bool mKeepAttached; diff --git a/gfx/layers/composite/ContentHost.cpp b/gfx/layers/composite/ContentHost.cpp index 09b0a0550b8a..d537bd15beb7 100644 --- a/gfx/layers/composite/ContentHost.cpp +++ b/gfx/layers/composite/ContentHost.cpp @@ -35,12 +35,12 @@ ContentHostBase::~ContentHostBase() } void -ContentHostBase::Composite(EffectChain& aEffectChain, - float aOpacity, - const gfx::Matrix4x4& aTransform, - const Filter& aFilter, - const Rect& aClipRect, - const nsIntRegion* aVisibleRegion) +ContentHostTexture::Composite(EffectChain& aEffectChain, + float aOpacity, + const gfx::Matrix4x4& aTransform, + const Filter& aFilter, + const Rect& aClipRect, + const nsIntRegion* aVisibleRegion) { NS_ASSERTION(aVisibleRegion, "Requires a visible region"); @@ -49,14 +49,18 @@ ContentHostBase::Composite(EffectChain& aEffectChain, return; } - RefPtr source = GetTextureSource(); - RefPtr sourceOnWhite = GetTextureSourceOnWhite(); + if (!mTextureHost->BindTextureSource(mTextureSource)) { + return; + } + MOZ_ASSERT(mTextureSource.get()); - if (!source) { + if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) { return; } - RefPtr effect = GenEffect(aFilter); + RefPtr effect = CreateTexturedEffect(mTextureSource.get(), + mTextureSourceOnWhite.get(), + aFilter, true); if (!effect) { return; } @@ -81,7 +85,7 @@ ContentHostBase::Composite(EffectChain& aEffectChain, region.MoveBy(-origin); // Figure out the intersecting draw region - gfx::IntSize texSize = source->GetSize(); + gfx::IntSize texSize = mTextureSource->GetSize(); nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height); textureRect.MoveBy(region.GetBounds().TopLeft()); nsIntRegion subregion; @@ -105,14 +109,14 @@ ContentHostBase::Composite(EffectChain& aEffectChain, regionRects.Or(regionRects, regionRect); } - BigImageIterator* bigImgIter = source->AsBigImageIterator(); + BigImageIterator* bigImgIter = mTextureSource->AsBigImageIterator(); BigImageIterator* iterOnWhite = nullptr; if (bigImgIter) { bigImgIter->BeginBigImageIteration(); } - if (sourceOnWhite) { - iterOnWhite = sourceOnWhite->AsBigImageIterator(); + if (mTextureSourceOnWhite) { + iterOnWhite = mTextureSourceOnWhite->AsBigImageIterator(); MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(), "Tile count mismatch on component alpha texture"); if (iterOnWhite) { @@ -205,32 +209,39 @@ ContentHostBase::Composite(EffectChain& aEffectChain, aTransform, mFlashCounter); } -TemporaryRef -ContentHostBase::GenEffect(const gfx::Filter& aFilter) -{ - RefPtr source = GetTextureSource(); - RefPtr sourceOnWhite = GetTextureSourceOnWhite(); - if (!source) { - return nullptr; - } - return CreateTexturedEffect(source, sourceOnWhite, aFilter, true); -} - void ContentHostTexture::UseTextureHost(TextureHost* aTexture) { + if (mTextureHost && mTextureHost != aTexture) { + mTextureHost->UnbindTextureSource(); + } ContentHostBase::UseTextureHost(aTexture); mTextureHost = aTexture; mTextureHostOnWhite = nullptr; + if (mTextureHost) { + mTextureHost->PrepareTextureSource(mTextureSource); + } } void ContentHostTexture::UseComponentAlphaTextures(TextureHost* aTextureOnBlack, TextureHost* aTextureOnWhite) { + if (mTextureHost && mTextureHost != aTextureOnBlack) { + mTextureHost->UnbindTextureSource(); + } + if (mTextureHostOnWhite && mTextureHostOnWhite != aTextureOnWhite) { + mTextureHostOnWhite->UnbindTextureSource(); + } ContentHostBase::UseComponentAlphaTextures(aTextureOnBlack, aTextureOnWhite); mTextureHost = aTextureOnBlack; mTextureHostOnWhite = aTextureOnWhite; + if (mTextureHost) { + mTextureHost->PrepareTextureSource(mTextureSource); + } + if (mTextureHostOnWhite) { + mTextureHost->PrepareTextureSource(mTextureSourceOnWhite); + } } void @@ -417,6 +428,176 @@ ContentHostIncremental::UpdateIncremental(TextureIdentifier aTextureId, FlushUpdateQueue(); } +void +ContentHostIncremental::Composite(EffectChain& aEffectChain, + float aOpacity, + const gfx::Matrix4x4& aTransform, + const Filter& aFilter, + const Rect& aClipRect, + const nsIntRegion* aVisibleRegion) +{ + NS_ASSERTION(aVisibleRegion, "Requires a visible region"); + + AutoLockCompositableHost lock(this); + if (lock.Failed()) { + return; + } + + if (!mSource) { + return; + } + + RefPtr effect = CreateTexturedEffect(mSource.get(), + mSourceOnWhite.get(), + aFilter, true); + if (!effect) { + return; + } + + aEffectChain.mPrimaryEffect = effect; + + nsIntRegion tmpRegion; + const nsIntRegion* renderRegion; + if (PaintWillResample()) { + // If we're resampling, then the texture image will contain exactly the + // entire visible region's bounds, and we should draw it all in one quad + // to avoid unexpected aliasing. + tmpRegion = aVisibleRegion->GetBounds(); + renderRegion = &tmpRegion; + } else { + renderRegion = aVisibleRegion; + } + + nsIntRegion region(*renderRegion); + nsIntPoint origin = GetOriginOffset(); + // translate into TexImage space, buffer origin might not be at texture (0,0) + region.MoveBy(-origin); + + // Figure out the intersecting draw region + gfx::IntSize texSize = mSource->GetSize(); + nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height); + textureRect.MoveBy(region.GetBounds().TopLeft()); + nsIntRegion subregion; + subregion.And(region, textureRect); + if (subregion.IsEmpty()) { + // Region is empty, nothing to draw + return; + } + + nsIntRegion screenRects; + nsIntRegion regionRects; + + // Collect texture/screen coordinates for drawing + nsIntRegionRectIterator iter(subregion); + while (const nsIntRect* iterRect = iter.Next()) { + nsIntRect regionRect = *iterRect; + nsIntRect screenRect = regionRect; + screenRect.MoveBy(origin); + + screenRects.Or(screenRects, screenRect); + regionRects.Or(regionRects, regionRect); + } + + BigImageIterator* bigImgIter = mSource->AsBigImageIterator(); + BigImageIterator* iterOnWhite = nullptr; + if (bigImgIter) { + bigImgIter->BeginBigImageIteration(); + } + + if (mSourceOnWhite) { + iterOnWhite = mSourceOnWhite->AsBigImageIterator(); + MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(), + "Tile count mismatch on component alpha texture"); + if (iterOnWhite) { + iterOnWhite->BeginBigImageIteration(); + } + } + + bool usingTiles = (bigImgIter && bigImgIter->GetTileCount() > 1); + do { + if (iterOnWhite) { + MOZ_ASSERT(iterOnWhite->GetTileRect() == bigImgIter->GetTileRect(), + "component alpha textures should be the same size."); + } + + nsIntRect texRect = bigImgIter ? bigImgIter->GetTileRect() + : nsIntRect(0, 0, + texSize.width, + texSize.height); + + // Draw texture. If we're using tiles, we do repeating manually, as texture + // repeat would cause each individual tile to repeat instead of the + // compound texture as a whole. This involves drawing at most 4 sections, + // 2 for each axis that has texture repeat. + for (int y = 0; y < (usingTiles ? 2 : 1); y++) { + for (int x = 0; x < (usingTiles ? 2 : 1); x++) { + nsIntRect currentTileRect(texRect); + currentTileRect.MoveBy(x * texSize.width, y * texSize.height); + + nsIntRegionRectIterator screenIter(screenRects); + nsIntRegionRectIterator regionIter(regionRects); + + const nsIntRect* screenRect; + const nsIntRect* regionRect; + while ((screenRect = screenIter.Next()) && + (regionRect = regionIter.Next())) { + nsIntRect tileScreenRect(*screenRect); + nsIntRect tileRegionRect(*regionRect); + + // When we're using tiles, find the intersection between the tile + // rect and this region rect. Tiling is then handled by the + // outer for-loops and modifying the tile rect. + if (usingTiles) { + tileScreenRect.MoveBy(-origin); + tileScreenRect = tileScreenRect.Intersect(currentTileRect); + tileScreenRect.MoveBy(origin); + + if (tileScreenRect.IsEmpty()) + continue; + + tileRegionRect = regionRect->Intersect(currentTileRect); + tileRegionRect.MoveBy(-currentTileRect.TopLeft()); + } + gfx::Rect rect(tileScreenRect.x, tileScreenRect.y, + tileScreenRect.width, tileScreenRect.height); + + effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width, + Float(tileRegionRect.y) / texRect.height, + Float(tileRegionRect.width) / texRect.width, + Float(tileRegionRect.height) / texRect.height); + GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform); + if (usingTiles) { + DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE; + if (iterOnWhite) { + diagnostics |= DiagnosticFlags::COMPONENT_ALPHA; + } + GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect, + aTransform, mFlashCounter); + } + } + } + } + + if (iterOnWhite) { + iterOnWhite->NextTile(); + } + } while (usingTiles && bigImgIter->NextTile()); + + if (bigImgIter) { + bigImgIter->EndBigImageIteration(); + } + if (iterOnWhite) { + iterOnWhite->EndBigImageIteration(); + } + + DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT; + if (iterOnWhite) { + diagnostics |= DiagnosticFlags::COMPONENT_ALPHA; + } + GetCompositor()->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect, + aTransform, mFlashCounter); +} + void ContentHostIncremental::FlushUpdateQueue() { @@ -439,20 +620,6 @@ ContentHostIncremental::ProcessTextureUpdates() mUpdateList.Clear(); } -TextureSource* -ContentHostIncremental::GetTextureSource() -{ - MOZ_ASSERT(mLocked); - return mSource; -} - -TextureSource* -ContentHostIncremental::GetTextureSourceOnWhite() -{ - MOZ_ASSERT(mLocked); - return mSourceOnWhite; -} - void ContentHostIncremental::TextureCreationRequest::Execute(ContentHostIncremental* aHost) { @@ -683,6 +850,32 @@ ContentHostTexture::GetRenderState() return result; } +TemporaryRef +ContentHostTexture::GenEffect(const gfx::Filter& aFilter) +{ + if (!mTextureHost) { + return nullptr; + } + if (!mTextureHost->BindTextureSource(mTextureSource)) { + return nullptr; + } + if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) { + return nullptr; + } + return CreateTexturedEffect(mTextureSource.get(), + mTextureSourceOnWhite.get(), + aFilter, true); +} + +TemporaryRef +ContentHostIncremental::GenEffect(const gfx::Filter& aFilter) +{ + if (!mSource) { + return nullptr; + } + return CreateTexturedEffect(mSource, mSourceOnWhite, aFilter, true); +} + #ifdef MOZ_DUMP_PAINTING TemporaryRef ContentHostTexture::GetAsSurface() diff --git a/gfx/layers/composite/ContentHost.h b/gfx/layers/composite/ContentHost.h index b445287ef20c..e7676d798c39 100644 --- a/gfx/layers/composite/ContentHost.h +++ b/gfx/layers/composite/ContentHost.h @@ -96,18 +96,6 @@ public: explicit ContentHostBase(const TextureInfo& aTextureInfo); virtual ~ContentHostBase(); - virtual void Composite(EffectChain& aEffectChain, - float aOpacity, - const gfx::Matrix4x4& aTransform, - const gfx::Filter& aFilter, - const gfx::Rect& aClipRect, - const nsIntRegion* aVisibleRegion = nullptr); - - virtual TextureSource* GetTextureSource() = 0; - virtual TextureSource* GetTextureSourceOnWhite() = 0; - - virtual TemporaryRef GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE; - protected: virtual nsIntPoint GetOriginOffset() { @@ -132,6 +120,13 @@ public: , mLocked(false) { } + virtual void Composite(EffectChain& aEffectChain, + float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Filter& aFilter, + const gfx::Rect& aClipRect, + const nsIntRegion* aVisibleRegion = nullptr); + virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE; #ifdef MOZ_DUMP_PAINTING @@ -173,23 +168,15 @@ public: mLocked = false; } - virtual TextureSource* GetTextureSource() MOZ_OVERRIDE { - MOZ_ASSERT(mLocked); - return mTextureHost->GetTextureSources(); - } - virtual TextureSource* GetTextureSourceOnWhite() MOZ_OVERRIDE { - MOZ_ASSERT(mLocked); - if (mTextureHostOnWhite) { - return mTextureHostOnWhite->GetTextureSources(); - } - return nullptr; - } - LayerRenderState GetRenderState(); + virtual TemporaryRef GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE; + protected: RefPtr mTextureHost; RefPtr mTextureHostOnWhite; + CompositableTextureSourceRef mTextureSource; + CompositableTextureSourceRef mTextureSourceOnWhite; bool mLocked; }; @@ -277,6 +264,13 @@ public: return false; } + virtual void Composite(EffectChain& aEffectChain, + float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Filter& aFilter, + const gfx::Rect& aClipRect, + const nsIntRegion* aVisibleRegion = nullptr); + virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) MOZ_OVERRIDE; virtual bool Lock() MOZ_OVERRIDE { @@ -291,8 +285,8 @@ public: mLocked = false; } - virtual TextureSource* GetTextureSource() MOZ_OVERRIDE; - virtual TextureSource* GetTextureSourceOnWhite() MOZ_OVERRIDE; + virtual TemporaryRef + GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE; private: diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index 6fd06a4d8a0b..3a07fcf677db 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -37,28 +37,21 @@ ImageHost::ImageHost(const TextureInfo& aTextureInfo) ImageHost::~ImageHost() { if (mFrontBuffer) { - mFrontBuffer->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); - } -} - -void -ImageHost::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) -{ - CompositableHost::SetCompositableBackendSpecificData(aBackendData); - // ImageHost allows TextureHost sharing among ImageHosts. - if (aBackendData) { - aBackendData->SetAllowSharingTextureHost(true); + mFrontBuffer->UnbindTextureSource(); } } void ImageHost::UseTextureHost(TextureHost* aTexture) { - CompositableHost::UseTextureHost(aTexture); - if (mFrontBuffer) { - mFrontBuffer->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); + if (mFrontBuffer && mFrontBuffer != aTexture) { + mFrontBuffer->UnbindTextureSource(); } + CompositableHost::UseTextureHost(aTexture); mFrontBuffer = aTexture; + if (mFrontBuffer) { + mFrontBuffer->PrepareTextureSource(mTextureSource); + } } void @@ -66,7 +59,8 @@ ImageHost::RemoveTextureHost(TextureHost* aTexture) { CompositableHost::RemoveTextureHost(aTexture); if (aTexture && mFrontBuffer == aTexture) { - aTexture->SetCompositableBackendSpecificData(nullptr); + mFrontBuffer->UnbindTextureSource(); + mTextureSource = nullptr; mFrontBuffer = nullptr; } } @@ -97,25 +91,34 @@ ImageHost::Composite(EffectChain& aEffectChain, // Make sure the front buffer has a compositor mFrontBuffer->SetCompositor(GetCompositor()); - mFrontBuffer->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); AutoLockCompositableHost autoLock(this); if (autoLock.Failed()) { NS_WARNING("failed to lock front buffer"); return; } - RefPtr source = GetTextureSource(); - if (!source) { + + if (!mFrontBuffer->BindTextureSource(mTextureSource)) { return; } - RefPtr effect = GenEffect(aFilter); + if (!mTextureSource) { + // BindTextureSource above should have returned false! + MOZ_ASSERT(false); + return; + } + + bool isAlphaPremultiplied = !(mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED); + RefPtr effect = CreateTexturedEffect(mFrontBuffer->GetFormat(), + mTextureSource.get(), + aFilter, + isAlphaPremultiplied); if (!effect) { return; } aEffectChain.mPrimaryEffect = effect; - IntSize textureSize = source->GetSize(); + IntSize textureSize = mTextureSource->GetSize(); gfx::Rect gfxPictureRect = mHasPictureRect ? gfx::Rect(0, 0, mPictureRect.width, mPictureRect.height) : gfx::Rect(0, 0, textureSize.width, textureSize.height); @@ -123,7 +126,7 @@ ImageHost::Composite(EffectChain& aEffectChain, gfx::Rect pictureRect(0, 0, mPictureRect.width, mPictureRect.height); - BigImageIterator* it = source->AsBigImageIterator(); + BigImageIterator* it = mTextureSource->AsBigImageIterator(); if (it) { // This iteration does not work if we have multiple texture sources here @@ -139,7 +142,7 @@ ImageHost::Composite(EffectChain& aEffectChain, // the corresponding source tiles from all planes, with appropriate // per-plane per-tile texture coords. // DrawQuad currently assumes that all planes use the same texture coords. - MOZ_ASSERT(it->GetTileCount() == 1 || !source->GetNextSibling(), + MOZ_ASSERT(it->GetTileCount() == 1 || !mTextureSource->GetNextSibling(), "Can't handle multi-plane BigImages"); it->BeginBigImageIteration(); @@ -170,7 +173,7 @@ ImageHost::Composite(EffectChain& aEffectChain, gfxPictureRect, aClipRect, aTransform, mFlashCounter); } else { - IntSize textureSize = source->GetSize(); + IntSize textureSize = mTextureSource->GetSize(); gfx::Rect rect; if (mHasPictureRect) { effect->mTextureCoords = Rect(Float(mPictureRect.x) / textureSize.width, @@ -273,18 +276,10 @@ ImageHost::Unlock() mLocked = false; } -TemporaryRef -ImageHost::GetTextureSource() -{ - MOZ_ASSERT(mLocked); - return mFrontBuffer->GetTextureSources(); -} - TemporaryRef ImageHost::GenEffect(const gfx::Filter& aFilter) { - RefPtr source = GetTextureSource(); - if (!source) { + if (!mFrontBuffer->BindTextureSource(mTextureSource)) { return nullptr; } bool isAlphaPremultiplied = true; @@ -292,7 +287,7 @@ ImageHost::GenEffect(const gfx::Filter& aFilter) isAlphaPremultiplied = false; return CreateTexturedEffect(mFrontBuffer->GetFormat(), - source, + mTextureSource, aFilter, isAlphaPremultiplied); } diff --git a/gfx/layers/composite/ImageHost.h b/gfx/layers/composite/ImageHost.h index ad12d229f049..e7524ebf7d20 100644 --- a/gfx/layers/composite/ImageHost.h +++ b/gfx/layers/composite/ImageHost.h @@ -45,8 +45,6 @@ public: virtual CompositableType GetType() { return mTextureInfo.mCompositableType; } - virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE; - virtual void Composite(EffectChain& aEffectChain, float aOpacity, const gfx::Matrix4x4& aTransform, @@ -84,13 +82,12 @@ public: virtual void Unlock() MOZ_OVERRIDE; - virtual TemporaryRef GetTextureSource(); - virtual TemporaryRef GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE; protected: RefPtr mFrontBuffer; + CompositableTextureSourceRef mTextureSource; nsIntRect mPictureRect; bool mHasPictureRect; bool mLocked; diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index 1397c69f6c4e..e22dfd8aceb5 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -144,6 +144,13 @@ TextureHost::GetIPDLActor() return mActor; } +bool +TextureHost::BindTextureSource(CompositableTextureSourceRef& texture) +{ + texture = GetTextureSources(); + return !!texture; +} + FenceHandle TextureHost::GetAndResetReleaseFenceHandle() { @@ -277,18 +284,6 @@ TextureHost::CompositorRecycle() static_cast(mActor)->CompositorRecycle(); } -void -TextureHost::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) -{ - mCompositableBackendData = aBackendData; -} - -void -TextureHost::UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) -{ - mCompositableBackendData = nullptr; -} - TextureHost::TextureHost(TextureFlags aFlags) : mActor(nullptr) , mFlags(aFlags) @@ -322,9 +317,11 @@ TextureHost::PrintInfo(std::stringstream& aStream, const char* aPrefix) } TextureSource::TextureSource() +: mCompositableCount(0) { MOZ_COUNT_CTOR(TextureSource); } + TextureSource::~TextureSource() { MOZ_COUNT_DTOR(TextureSource); @@ -843,8 +840,9 @@ SharedSurfaceToTexSource(gl::SharedSurface* abstractSurf, Compositor* compositor GLenum target = surf->ConsTextureTarget(); GLuint tex = surf->ConsTexture(gl); - texSource = new GLTextureSource(compositorOGL, tex, format, target, - surf->mSize); + texSource = new GLTextureSource(compositorOGL, tex, target, + surf->mSize, format, + true/*externally owned*/); break; } case gl::SharedSurfaceType::EGLImageShare: { @@ -859,8 +857,9 @@ SharedSurfaceToTexSource(gl::SharedSurface* abstractSurf, Compositor* compositor GLuint tex = 0; surf->AcquireConsumerTexture(gl, &tex, &target); - texSource = new GLTextureSource(compositorOGL, tex, format, target, - surf->mSize); + texSource = new GLTextureSource(compositorOGL, tex, target, + surf->mSize, format, + true/*externally owned*/); break; } #ifdef XP_MACOSX diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h index b6e00bf4c861..f44fd1c467fa 100644 --- a/gfx/layers/composite/TextureHost.h +++ b/gfx/layers/composite/TextureHost.h @@ -46,7 +46,6 @@ namespace layers { class Compositor; class CompositableHost; -class CompositableBackendSpecificData; class CompositableParentManager; class SurfaceDescriptor; class SharedSurfaceDescriptor; @@ -150,10 +149,69 @@ public: return nullptr; } + void AddCompositableRef() { ++mCompositableCount; } + + void ReleaseCompositableRef() { + --mCompositableCount; + MOZ_ASSERT(mCompositableCount >= 0); + } + + int NumCompositableRefs() const { return mCompositableCount; } + protected: virtual ~TextureSource(); RefPtr mNextSibling; + int mCompositableCount; +}; + +/** + * equivalent of a RefPtr, that calls AddCompositableRef and + * ReleaseCompositableRef in addition to the usual AddRef and Release. + */ +class CompositableTextureSourceRef { +public: + CompositableTextureSourceRef() {} + + ~CompositableTextureSourceRef() + { + if (mRef) { + mRef->ReleaseCompositableRef(); + } + } + + CompositableTextureSourceRef& operator=(const TemporaryRef& aOther) + { + RefPtr temp = aOther; + if (temp) { + temp->AddCompositableRef(); + } + if (mRef) { + mRef->ReleaseCompositableRef(); + } + mRef = temp; + return *this; + } + + CompositableTextureSourceRef& operator=(TextureSource* aOther) + { + if (aOther) { + aOther->AddCompositableRef(); + } + if (mRef) { + mRef->ReleaseCompositableRef(); + } + mRef = aOther; + return *this; + } + + TextureSource* get() const { return mRef; } + operator TextureSource*() const { return mRef; } + TextureSource* operator->() const { return mRef; } + TextureSource& operator*() const { return *mRef; } + +private: + RefPtr mRef; }; /** @@ -302,6 +360,25 @@ public: */ virtual TextureSource* GetTextureSources() = 0; + /** + * Called during the transaction. The TextureSource may or may not be composited. + * + * Note that this is called outside of lock/unlock. + */ + virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {} + + /** + * Called at composition time, just before compositing the TextureSource composited. + * + * Note that this is called only withing lock/unlock. + */ + virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture); + + /** + * Called when another TextureHost will take over. + */ + virtual void UnbindTextureSource() {} + /** * Is called before compositing if the shared data has changed since last * composition. @@ -406,10 +483,6 @@ public: return LayerRenderState(); } - virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData); - - virtual void UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData); - // If a texture host holds a reference to shmem, it should override this method // to forget about the shmem _without_ releasing it. virtual void OnShutdown() {} @@ -435,7 +508,6 @@ public: protected: PTextureParent* mActor; TextureFlags mFlags; - RefPtr mCompositableBackendData; friend class TextureParent; }; diff --git a/gfx/layers/opengl/GrallocTextureHost.cpp b/gfx/layers/opengl/GrallocTextureHost.cpp index a4b87ca385fe..3e6871bc5886 100644 --- a/gfx/layers/opengl/GrallocTextureHost.cpp +++ b/gfx/layers/opengl/GrallocTextureHost.cpp @@ -130,17 +130,6 @@ GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) gl()->fActiveTexture(aTextureUnit); gl()->fBindTexture(textureTarget, tex); - if (mTextureBackendSpecificData) { - // There are two paths for locking/unlocking - if mTextureBackendSpecificData is - // set, we use the texture on there, otherwise we use - // CompositorBackendSpecificData from the compositor and bind the EGLImage - // only in Lock(). - if (!mEGLImage) { - mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); - } - BindEGLImage(); - } - ApplyFilterToBoundTexture(gl(), aFilter, textureTarget); #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 @@ -153,10 +142,6 @@ GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) bool GrallocTextureSourceOGL::Lock() { - if (mTextureBackendSpecificData) { - return true; - } - MOZ_ASSERT(IsValid()); if (!IsValid()) { return false; @@ -182,7 +167,7 @@ bool GrallocTextureSourceOGL::Lock() bool GrallocTextureSourceOGL::IsValid() const { - return !!gl() && !!mGraphicBuffer.get() && (!!mCompositor || !!mTextureBackendSpecificData); + return !!gl() && !!mGraphicBuffer.get() && !!mCompositor; } gl::GLContext* @@ -224,62 +209,6 @@ GrallocTextureSourceOGL::GetTextureTarget() const return TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat()); } -void -GrallocTextureSourceOGL::SetTextureBackendSpecificData(TextureSharedDataGonkOGL* aBackendData) -{ - if (!aBackendData) { - DeallocateDeviceData(); - // Update mTextureBackendSpecificData after calling DeallocateDeviceData(). - mTextureBackendSpecificData = nullptr; - return; - } - - if (mTextureBackendSpecificData != aBackendData) { - mNeedsReset = true; - } - - if (!gl() || !gl()->MakeCurrent()) { - NS_WARNING("Failed to make the context current"); - return; - } - - if (!mNeedsReset) { - // Update binding to the EGLImage - GLuint tex = GetGLTexture(); - GLuint textureTarget = GetTextureTarget(); - gl()->fActiveTexture(LOCAL_GL_TEXTURE0); - gl()->fBindTexture(textureTarget, tex); - BindEGLImage(); - return; - } - - if (!mCompositor) { - mTextureBackendSpecificData = aBackendData; - return; - } - - // delete old EGLImage - DeallocateDeviceData(); - - // Update mTextureBackendSpecificData after calling DeallocateDeviceData(). - mTextureBackendSpecificData = aBackendData; - - GLuint tex = GetGLTexture(); - GLuint textureTarget = GetTextureTarget(); - - gl()->fActiveTexture(LOCAL_GL_TEXTURE0); - gl()->fBindTexture(textureTarget, tex); - - // Setup texure parameters at the first binding. - gl()->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, GetWrapMode()); - gl()->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, GetWrapMode()); - - // create new EGLImage - mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); - BindEGLImage(); - mNeedsReset = false; -} - gfx::IntSize GrallocTextureSourceOGL::GetSize() const { @@ -298,9 +227,6 @@ GrallocTextureSourceOGL::DeallocateDeviceData() if (!gl() || !gl()->MakeCurrent()) { return; } - if (mTextureBackendSpecificData) { - mTextureBackendSpecificData->ClearBoundEGLImage(mEGLImage); - } EGLImageDestroy(gl(), mEGLImage); mEGLImage = EGL_NO_IMAGE; } @@ -309,49 +235,48 @@ GrallocTextureSourceOGL::DeallocateDeviceData() GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags, const NewSurfaceDescriptorGralloc& aDescriptor) : TextureHost(aFlags) + , mGrallocHandle(aDescriptor) + , mSize(0, 0) + , mDescriptorSize(aDescriptor.size()) + , mFormat(gfx::SurfaceFormat::UNKNOWN) + , mEGLImage(EGL_NO_IMAGE) { - gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN; - mGrallocHandle = aDescriptor; - android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); MOZ_ASSERT(graphicBuffer); - mSize = aDescriptor.size(); if (graphicBuffer) { - format = + mFormat = SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(), aFlags & TextureFlags::RB_SWAPPED); - mTextureSource = new GrallocTextureSourceOGL(nullptr, - this, - graphicBuffer, - format); + mSize = gfx::IntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight()); } else { printf_stderr("gralloc buffer is nullptr"); } } GrallocTextureHostOGL::~GrallocTextureHostOGL() -{ - MOZ_ASSERT(!mTextureSource || (mFlags & TextureFlags::DEALLOCATE_CLIENT), - "Leaking our buffer"); -} +{} void GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor) { - if (mTextureSource) { - mTextureSource->SetCompositor(static_cast(aCompositor)); + mCompositor = static_cast(aCompositor); + if (mTilingTextureSource) { + mTilingTextureSource->SetCompositor(mCompositor); + } + if (mGLTextureSource) { + mGLTextureSource->SetCompositor(mCompositor); + } + + if (mCompositor && aCompositor != mCompositor) { + DestroyEGLImage(); } } bool GrallocTextureHostOGL::Lock() { - if (IsValid()) { - mTextureSource->Lock(); - return true; - } - return false; + return IsValid(); } void @@ -363,28 +288,29 @@ GrallocTextureHostOGL::Unlock() bool GrallocTextureHostOGL::IsValid() const { - if (!mTextureSource) { - return false; - } - return mTextureSource->IsValid(); + android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); + return graphicBuffer != nullptr; } gfx::SurfaceFormat GrallocTextureHostOGL::GetFormat() const { - if (!mTextureSource) { - return gfx::SurfaceFormat::UNKNOWN; - } - return mTextureSource->GetFormat(); + return mFormat; } void GrallocTextureHostOGL::DeallocateSharedData() { - if (mTextureSource) { - mTextureSource->ForgetBuffer(); - mTextureSource = nullptr; + if (mTilingTextureSource) { + mTilingTextureSource->ForgetBuffer(); + mTilingTextureSource = nullptr; } + if (mGLTextureSource) { + mGLTextureSource = nullptr; + } + + DestroyEGLImage(); + if (mGrallocHandle.buffer().type() != SurfaceDescriptor::Tnull_t) { MaybeMagicGrallocBufferHandle handle = mGrallocHandle.buffer(); base::ProcessId owner; @@ -402,24 +328,33 @@ GrallocTextureHostOGL::DeallocateSharedData() void GrallocTextureHostOGL::ForgetSharedData() { - if (mTextureSource) { - mTextureSource->ForgetBuffer(); - mTextureSource = nullptr; + if (mTilingTextureSource) { + mTilingTextureSource->ForgetBuffer(); + mTilingTextureSource = nullptr; + } + if (mGLTextureSource) { + mGLTextureSource = nullptr; } } void GrallocTextureHostOGL::DeallocateDeviceData() { - if (mTextureSource) { - mTextureSource->DeallocateDeviceData(); + if (mTilingTextureSource) { + mTilingTextureSource->DeallocateDeviceData(); } + if (mGLTextureSource) { + mGLTextureSource = nullptr; + } + DestroyEGLImage(); } LayerRenderState GrallocTextureHostOGL::GetRenderState() { - if (IsValid()) { + android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); + + if (graphicBuffer) { LayerRenderStateFlags flags = LayerRenderStateFlags::LAYER_RENDER_STATE_DEFAULT; if (mFlags & TextureFlags::NEEDS_Y_FLIP) { flags |= LayerRenderStateFlags::Y_FLIPPED; @@ -427,8 +362,8 @@ GrallocTextureHostOGL::GetRenderState() if (mFlags & TextureFlags::RB_SWAPPED) { flags |= LayerRenderStateFlags::FORMAT_RB_SWAP; } - return LayerRenderState(mTextureSource->mGraphicBuffer.get(), - gfx::ThebesIntSize(mSize), + return LayerRenderState(graphicBuffer, + gfx::ThebesIntSize(mDescriptorSize), flags, this); } @@ -438,8 +373,8 @@ GrallocTextureHostOGL::GetRenderState() TemporaryRef GrallocTextureHostOGL::GetAsSurface() { - return mTextureSource ? mTextureSource->GetAsSurface() - : nullptr; + return mTilingTextureSource ? mTilingTextureSource->GetAsSurface() + : nullptr; } TemporaryRef @@ -467,103 +402,186 @@ GrallocTextureSourceOGL::GetAsSurface() { GLuint GrallocTextureSourceOGL::GetGLTexture() { - if (mTextureBackendSpecificData) { - mTextureBackendSpecificData->SetCompositor(mCompositor); - return mTextureBackendSpecificData->GetTexture(); - } - return mTexture; } void GrallocTextureSourceOGL::BindEGLImage() { - if (mTextureBackendSpecificData) { - mTextureBackendSpecificData->BindEGLImage(GetTextureTarget(), mEGLImage); + gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage); +} + +TextureSource* +GrallocTextureHostOGL::GetTextureSources() +{ + // This is now only used with tiled layers, and will eventually be removed. + // Other layer types use BindTextureSource instead. + MOZ_ASSERT(!mGLTextureSource); + if (!mTilingTextureSource) { + android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); + MOZ_ASSERT(graphicBuffer); + if (!graphicBuffer) { + return nullptr; + } + mTilingTextureSource = new GrallocTextureSourceOGL(mCompositor, this, + graphicBuffer, mFormat); + } + mTilingTextureSource->Lock(); + return mTilingTextureSource; +} + +void +GrallocTextureHostOGL::UnbindTextureSource() +{ + // Clear the reference to the TextureSource (if any), because we know that + // another TextureHost is being bound to the TextureSource. This means that + // we will have to re-do gl->fEGLImageTargetTexture2D next time we go through + // BindTextureSource (otherwise we would have skipped it). + // Note that this doesn't "unlock" the gralloc buffer or force it to be + // detached, Although decreasing the refcount of the TextureSource may lead + // to the gl handle being destroyed, which would unlock the gralloc buffer. + // That said, this method is called before another TextureHost attaches to the + // TextureSource, which has the effect of unlocking the gralloc buffer. So when + // this is called we know we are going to be unlocked soon. + mGLTextureSource = nullptr; +} + +GLenum GetTextureTarget(gl::GLContext* aGL, android::PixelFormat aFormat) { + MOZ_ASSERT(aGL); + if (aGL->Renderer() == gl::GLRenderer::SGX530 || + aGL->Renderer() == gl::GLRenderer::SGX540) { + // SGX has a quirk that only TEXTURE_EXTERNAL works and any other value will + // result in black pixels when trying to draw from bound textures. + // Unfortunately, using TEXTURE_EXTERNAL on Adreno has a terrible effect on + // performance. + // See Bug 950050. + return LOCAL_GL_TEXTURE_EXTERNAL; } else { - gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage); + return TextureTargetForAndroidPixelFormat(aFormat); } } void -GrallocTextureHostOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) +GrallocTextureHostOGL::DestroyEGLImage() { - if(!aBackendData) { - return; + // Only called when we want to get rid of the gralloc buffer, usually + // around the end of life of the TextureHost. + if (mEGLImage != EGL_NO_IMAGE && GetGLContext()) { + EGLImageDestroy(GetGLContext(), mEGLImage); + mEGLImage = EGL_NO_IMAGE; } - - // Update mTextureBackendSpecificData if it is not set yet. - if (!mTextureBackendSpecificData) { - MOZ_ASSERT(!mCompositableBackendData); - mCompositableBackendData = aBackendData; - CompositableDataGonkOGL* backend = static_cast(mCompositableBackendData.get()); - mTextureBackendSpecificData = backend->GetTextureBackendSpecificData(); - } - - // If TextureHost sharing by multiple CompositableHosts are detected, - // enable mBackendDatas usage. - if (!mBackendDatas && - mCompositableBackendData && - mCompositableBackendData != aBackendData && - mTextureBackendSpecificData->IsAllowingSharingTextureHost()) - { - mBackendDatas = MakeUnique > >(); - (*mBackendDatas)[mCompositableBackendData->GetId()] = mCompositableBackendData; - mCompositableBackendData = nullptr; - - // Get new mTextureBackendSpecificData - mTextureBackendSpecificData = - mTextureBackendSpecificData->GetNewTextureBackendSpecificData(mTextureSource->GetEGLImage()); - mTextureBackendSpecificData->SetOwnedByTextureHost(); - } - - // Update mCompositableBackendData. - if (mBackendDatas) - { - // Handle a case that TextureHost has ownership of TextureSharedDataGonkOGL. - MOZ_ASSERT(aBackendData->IsAllowingSharingTextureHost()); - (*mBackendDatas)[aBackendData->GetId()] = aBackendData; - if (mBackendDatas->size() > 200) { - NS_WARNING("Too many CompositableBackends"); - } - } else { - // Handle a case that CompositableHost has ownership of TextureSharedDataGonkOGL. - mCompositableBackendData = aBackendData; - CompositableDataGonkOGL* backend = static_cast(mCompositableBackendData.get()); - mTextureBackendSpecificData = backend->GetTextureBackendSpecificData(); - } - - if (mTextureSource) { - mTextureSource->SetTextureBackendSpecificData(mTextureBackendSpecificData); - } - } void -GrallocTextureHostOGL::UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) +GrallocTextureHostOGL::PrepareTextureSource(CompositableTextureSourceRef& aTextureSource) { - if(!aBackendData || - !mTextureBackendSpecificData) { + // This happens during the layers transaction. + // All of the gralloc magic goes here. The only thing that happens externally + // and that is good to keep in mind is that when the TextureSource is deleted, + // it destroys its gl texture handle which is important for genlock. + + // If this TextureHost's mGLTextureSource member is non-null, it means we are + // still bound to the TextureSource, in which case we can skip the driver + // overhead of binding the texture again (fEGLImageTargetTexture2D) + // As a result, if the TextureHost is used with several CompositableHosts, + // it will be bound to only one TextureSource, and we'll do the driver work + // only once, which is great. This means that all of the compositables that + // use this TextureHost will keep a reference to this TextureSource at least + // for the duration of this frame. + + // If the compositable already has a TextureSource (the aTextureSource parameter), + // that is compatible and is not in use by several compositable, we try to + // attach to it. This has the effect of unlocking the previous TextureHost that + // we attached to the TextureSource (the previous frame) + + // If the TextureSource used by the compositable is also used by other + // compositables (see NumCompositableRefs), we have to create a new TextureSource, + // because otherwise we would be modifying the content of every layer that uses + // the TextureSource in question, even thoug they don't use this TextureHost. + + MOZ_ASSERT(!mTilingTextureSource); + + android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); + + MOZ_ASSERT(graphicBuffer); + if (!graphicBuffer) { + mGLTextureSource = nullptr; return; } - if (mBackendDatas) - { - // Handle a case that TextureHost has ownership of TextureSharedDataGonkOGL. - mBackendDatas->erase(aBackendData->GetId()); - if (mBackendDatas->size() == 0) { - mCompositableBackendData = nullptr; - mTextureBackendSpecificData = nullptr; - } - } else { - // Handle a case that CompositableHost has ownership of TextureSharedDataGonkOGL. - mCompositableBackendData = nullptr; - mTextureBackendSpecificData = nullptr; + if (mGLTextureSource && !mGLTextureSource->IsValid()) { + mGLTextureSource = nullptr; } - if (mTextureSource) { - mTextureSource->SetTextureBackendSpecificData(mTextureBackendSpecificData); + if (mGLTextureSource) { + // We are already attached to a TextureSource, nothing to do except tell + // the compositable to use it. + aTextureSource = mGLTextureSource.get(); + return; } + + gl::GLContext* gl = GetGLContext(); + if (!gl || !gl->MakeCurrent()) { + mGLTextureSource = nullptr; + return; + } + + if (mEGLImage == EGL_NO_IMAGE) { + // Should only happen the first time. + mEGLImage = EGLImageCreateFromNativeBuffer(gl, graphicBuffer->getNativeBuffer()); + } + + GLenum textureTarget = GetTextureTarget(gl, graphicBuffer->getPixelFormat()); + + GLTextureSource* glSource = aTextureSource.get() ? + aTextureSource->AsSourceOGL()->AsGLTextureSource() : nullptr; + + bool shouldCreateTextureSource = !glSource || !glSource->IsValid() + || glSource->NumCompositableRefs() > 1 + || glSource->GetTextureTarget() != textureTarget; + + if (shouldCreateTextureSource) { + GLuint textureHandle; + gl->fGenTextures(1, &textureHandle); + gl->fBindTexture(textureTarget, textureHandle); + gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); + gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); + gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage); + + mGLTextureSource = new GLTextureSource(mCompositor, textureHandle, textureTarget, + mSize, mFormat); + aTextureSource = mGLTextureSource.get(); + } else { + gl->fBindTexture(textureTarget, glSource->GetTextureHandle()); + + gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage); + glSource->SetSize(mSize); + glSource->SetFormat(mFormat); + mGLTextureSource = glSource; + } +} + +bool +GrallocTextureHostOGL::BindTextureSource(CompositableTextureSourceRef& aTextureSource) +{ + // This happens at composition time. + + // If mGLTextureSource is null it means PrepareTextureSource failed. + if (!mGLTextureSource) { + return false; + } + + // If Prepare didn't fail, we expect our TextureSource to be the same as aTextureSource, + // otherwise it means something has fiddled with the TextureSource between Prepare and + // now. + MOZ_ASSERT(mGLTextureSource == aTextureSource); + aTextureSource = mGLTextureSource; + +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 + // Wait until it's ready. + WaitAcquireFenceSyncComplete(); +#endif + return true; } } // namepsace layers diff --git a/gfx/layers/opengl/GrallocTextureHost.h b/gfx/layers/opengl/GrallocTextureHost.h index 55500c6ec06a..aad2799b74e0 100644 --- a/gfx/layers/opengl/GrallocTextureHost.h +++ b/gfx/layers/opengl/GrallocTextureHost.h @@ -17,6 +17,7 @@ namespace layers { class GrallocTextureHostOGL; +// Progressively getting replaced by GLTextureSource class GrallocTextureSourceOGL : public TextureSource , public TextureSourceOGL { @@ -47,8 +48,6 @@ public: return LOCAL_GL_CLAMP_TO_EDGE; } - virtual void SetTextureBackendSpecificData(TextureSharedDataGonkOGL* aBackendData); - void DeallocateDeviceData(); gl::GLContext* gl() const; @@ -75,7 +74,6 @@ public: bool Lock(); protected: - RefPtr mTextureBackendSpecificData; RefPtr mCompositor; GrallocTextureHostOGL* mTextureHost; android::sp mGraphicBuffer; @@ -113,14 +111,17 @@ public: virtual gfx::SurfaceFormat GetFormat() const; - virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; } + virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mDescriptorSize; } virtual LayerRenderState GetRenderState() MOZ_OVERRIDE; - virtual TextureSource* GetTextureSources() MOZ_OVERRIDE - { - return mTextureSource; - } + virtual void PrepareTextureSource(CompositableTextureSourceRef& aTextureSource) MOZ_OVERRIDE; + + virtual bool BindTextureSource(CompositableTextureSourceRef& aTextureSource) MOZ_OVERRIDE; + + virtual void UnbindTextureSource() MOZ_OVERRIDE; + + virtual TextureSource* GetTextureSources() MOZ_OVERRIDE; #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 virtual TextureHostOGL* AsHostOGL() MOZ_OVERRIDE @@ -131,21 +132,27 @@ public: virtual TemporaryRef GetAsSurface() MOZ_OVERRIDE; - virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE; - - virtual void UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE; - bool IsValid() const; virtual const char* Name() MOZ_OVERRIDE { return "GrallocTextureHostOGL"; } -private: - NewSurfaceDescriptorGralloc mGrallocHandle; - RefPtr mTextureSource; - gfx::IntSize mSize; // See comment in textureClientOGL.h + gl::GLContext* GetGLContext() const { return mCompositor ? mCompositor->gl() : nullptr; } - RefPtr mTextureBackendSpecificData; - UniquePtr > > mBackendDatas; +private: + void DestroyEGLImage(); + + NewSurfaceDescriptorGralloc mGrallocHandle; + RefPtr mGLTextureSource; + RefPtr mCompositor; + // only used for tiling, will be removed. + RefPtr mTilingTextureSource; + // Size reported by the GraphicBuffer + gfx::IntSize mSize; + // Size reported by TextureClient, can be different in some cases (video?), + // used by LayerRenderState. + gfx::IntSize mDescriptorSize; + gfx::SurfaceFormat mFormat; + EGLImage mEGLImage; }; } // namespace layers diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp index 0094d3f0107a..4fa926f1891f 100644 --- a/gfx/layers/opengl/TextureHostOGL.cpp +++ b/gfx/layers/opengl/TextureHostOGL.cpp @@ -40,16 +40,6 @@ namespace layers { class Compositor; -TemporaryRef -CreateCompositableBackendSpecificDataOGL() -{ -#ifdef MOZ_WIDGET_GONK - return new CompositableDataGonkOGL(); -#else - return nullptr; -#endif -} - TemporaryRef CreateTextureHostOGL(const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator, @@ -119,174 +109,6 @@ FlagsToGLFlags(TextureFlags aFlags) return static_cast(result); } -CompositableDataGonkOGL::CompositableDataGonkOGL() -{ -} - -CompositableDataGonkOGL::~CompositableDataGonkOGL() -{ - ClearData(); -} - -void -CompositableDataGonkOGL::ClearData() -{ - CompositableBackendSpecificData::ClearData(); - mTextureBackendSpecificData = nullptr; - mCompositor = nullptr; -} - -void -CompositableDataGonkOGL::SetCompositor(Compositor* aCompositor) -{ - mCompositor = static_cast(aCompositor); - if (mTextureBackendSpecificData) { - mTextureBackendSpecificData->SetCompositor(aCompositor); - } -} - -TextureSharedDataGonkOGL* -CompositableDataGonkOGL::GetTextureBackendSpecificData() -{ - if (!mTextureBackendSpecificData) { - mTextureBackendSpecificData = new TextureSharedDataGonkOGL(); - mTextureBackendSpecificData->SetCompositor(mCompositor); - mTextureBackendSpecificData->SetAllowSharingTextureHost(IsAllowingSharingTextureHost()); - } - return mTextureBackendSpecificData; -} - -TextureSharedDataGonkOGL::TextureSharedDataGonkOGL() - : mOwnedByCompositableHost(true) - , mAllowSharingTextureHost(false) - , mTexture(0) - , mBoundEGLImage(EGL_NO_IMAGE) -{ -} - -TextureSharedDataGonkOGL::TextureSharedDataGonkOGL(GLuint aTexture, EGLImage aImage, CompositorOGL* aCompositor) - : mOwnedByCompositableHost(true) - , mAllowSharingTextureHost(false) - , mCompositor(aCompositor) - , mTexture(aTexture) - , mBoundEGLImage(aImage) -{ -} - -TextureSharedDataGonkOGL::~TextureSharedDataGonkOGL() -{ - DeleteTextureIfPresent(); -} - -gl::GLContext* -TextureSharedDataGonkOGL::gl() const -{ - return mCompositor ? mCompositor->gl() : nullptr; -} - -void -TextureSharedDataGonkOGL::SetCompositor(Compositor* aCompositor) -{ - if (gl() && mCompositor != aCompositor) { - DeleteTextureIfPresent(); - } - mCompositor = static_cast(aCompositor); -} - -void -TextureSharedDataGonkOGL::ClearData() -{ - DeleteTextureIfPresent(); -} - -TemporaryRef -TextureSharedDataGonkOGL::GetNewTextureBackendSpecificData(EGLImage aImage) -{ - MOZ_ASSERT(IsAllowingSharingTextureHost()); - - if (IsEGLImageBound(aImage)) - { - // If EGLImage is already bound to OpenGL Texture, - // handover the OpenGL Texture to caller - GLuint textureId = GetAndResetGLTextureOwnership(); - RefPtr data = new TextureSharedDataGonkOGL(textureId, aImage, mCompositor); - data->SetCompositor(mCompositor); - data->SetAllowSharingTextureHost(true); - return data; - } - - // Create brand new TextureSharedDataGonkOGL - RefPtr data = new TextureSharedDataGonkOGL(); - data->SetCompositor(mCompositor); - data->SetAllowSharingTextureHost(true); - return data; -} - -GLuint -TextureSharedDataGonkOGL::GetTexture() -{ - if (!mTexture) { - if (gl() && gl()->MakeCurrent()) { - gl()->fGenTextures(1, &mTexture); - } - } - return mTexture; -} - -GLuint -TextureSharedDataGonkOGL::GetAndResetGLTextureOwnership() -{ - GLuint texture = mTexture; - mTexture = 0; - mBoundEGLImage = EGL_NO_IMAGE; - return texture; -} - -void -TextureSharedDataGonkOGL::DeleteTextureIfPresent() -{ - if (mTexture) { - MOZ_ASSERT(mCompositor); - if (gl() && gl()->MakeCurrent()) { - gl()->fDeleteTextures(1, &mTexture); - } - mTexture = 0; - mBoundEGLImage = EGL_NO_IMAGE; - } -} - -void -TextureSharedDataGonkOGL::BindEGLImage(GLuint aTarget, EGLImage aImage) -{ - if (mBoundEGLImage != aImage) { - MOZ_ASSERT(gl()); - if (gl()) { - gl()->fEGLImageTargetTexture2D(aTarget, aImage); - } - mBoundEGLImage = aImage; - } -} - -void -TextureSharedDataGonkOGL::ClearBoundEGLImage(EGLImage aImage) -{ - if (mBoundEGLImage == aImage) { - DeleteTextureIfPresent(); - mBoundEGLImage = EGL_NO_IMAGE; - } -} - -bool -TextureSharedDataGonkOGL::IsEGLImageBound(EGLImage aImage) -{ - if (mTexture != 0 && - aImage != EGL_NO_IMAGE && - aImage == mBoundEGLImage) { - return true; - } - return false; -} - #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 bool TextureHostOGL::SetReleaseFence(const android::sp& aReleaseFence) @@ -529,27 +351,56 @@ TextureImageTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilt // GLTextureSource GLTextureSource::GLTextureSource(CompositorOGL* aCompositor, - GLuint aTex, - gfx::SurfaceFormat aFormat, + GLuint aTextureHandle, GLenum aTarget, - gfx::IntSize aSize) - : mSize(aSize) - , mCompositor(aCompositor) - , mTex(aTex) - , mFormat(aFormat) + gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, + bool aExternallyOwned) + : mCompositor(aCompositor) + , mTextureHandle(aTextureHandle) , mTextureTarget(aTarget) + , mSize(aSize) + , mFormat(aFormat) + , mExternallyOwned(aExternallyOwned) { + MOZ_COUNT_CTOR(GLTextureSource); +} + +GLTextureSource::~GLTextureSource() +{ + MOZ_COUNT_DTOR(GLTextureSource); + if (!mExternallyOwned) { + DeleteTextureHandle(); + } +} + +void +GLTextureSource::DeallocateDeviceData() +{ + if (!mExternallyOwned) { + DeleteTextureHandle(); + } +} + +void +GLTextureSource::DeleteTextureHandle() +{ + if (mTextureHandle != 0 && gl() && gl()->MakeCurrent()) { + gl()->fDeleteTextures(1, &mTextureHandle); + } + mTextureHandle = 0; } void GLTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) { + MOZ_ASSERT(gl()); + MOZ_ASSERT(mTextureHandle != 0); if (!gl()) { - NS_WARNING("Trying to bind a texture without a GLContext"); return; } gl()->fActiveTexture(aTextureUnit); - gl()->fBindTexture(mTextureTarget, mTex); + gl()->fBindTexture(mTextureTarget, mTextureHandle); ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget); } @@ -562,7 +413,7 @@ GLTextureSource::SetCompositor(Compositor* aCompositor) bool GLTextureSource::IsValid() const { - return !!gl(); + return !!gl() && mTextureHandle != 0; } gl::GLContext* @@ -616,6 +467,10 @@ SurfaceTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) void SurfaceTextureSource::SetCompositor(Compositor* aCompositor) { + if (mCompositor != aCompositor) { + DeallocateDeviceData(); + } + mCompositor = static_cast(aCompositor); } diff --git a/gfx/layers/opengl/TextureHostOGL.h b/gfx/layers/opengl/TextureHostOGL.h index 7dc3eef0b7b0..1b9eb9a24246 100644 --- a/gfx/layers/opengl/TextureHostOGL.h +++ b/gfx/layers/opengl/TextureHostOGL.h @@ -55,99 +55,7 @@ class Compositor; class CompositorOGL; class TextureImageTextureSourceOGL; class TextureSharedDataGonkOGL; - -/** - * CompositableBackendSpecificData implementation for the Gonk OpenGL backend. - * Share a same texture between TextureHosts in the same CompositableHost. - * By shareing the texture among the TextureHosts, number of texture allocations - * can be reduced than texture allocation in every TextureHosts. - * From Bug 912134, use only one texture among all TextureHosts degrade - * the rendering performance. - * CompositableDataGonkOGL chooses in a middile of them. - */ -class CompositableDataGonkOGL : public CompositableBackendSpecificData -{ -protected: - virtual ~CompositableDataGonkOGL(); - -public: - CompositableDataGonkOGL(); - virtual void ClearData() MOZ_OVERRIDE; - virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE; - - TextureSharedDataGonkOGL* GetTextureBackendSpecificData(); -protected: - nsRefPtr mTextureBackendSpecificData; - RefPtr mCompositor; -}; - -/** - * Manage actual shared resources of CompositableDataGonkOGL. - * The resources are split from CompositableDataGonkOGL to handle two use cases. - * Normally TextureHost is used from one CompositableHost at the same time. - * In this case, performance is good if the resources are owned by CompositableDataGonkOGL. - * But TextureHost could be shared among multiple ImageHosts. - * If it happens, performance is good if the resource is owned by TextureHost. - * The resources ownership is carryed over from CompositableDataGonkOGL to TextureHost. - * See Bug 1017351. - */ -class TextureSharedDataGonkOGL -{ -protected: - virtual ~TextureSharedDataGonkOGL(); - -public: - NS_INLINE_DECL_REFCOUNTING(TextureSharedDataGonkOGL) - - TextureSharedDataGonkOGL(); - TextureSharedDataGonkOGL(GLuint aTexture, EGLImage aImage, CompositorOGL* aCompositor); - - void SetCompositor(Compositor* aCompositor); - void ClearData(); - - // Mark TextureSharedDataGonkOGL as owned by TextureHost. - void SetOwnedByTextureHost() - { - mOwnedByCompositableHost = false; - } - - // Check if this is owned by CompositableHost or TextureHost. - bool IsOwnedByCompositableHost() - { - return mOwnedByCompositableHost; - } - - bool IsAllowingSharingTextureHost() - { - return mAllowSharingTextureHost; - } - - void SetAllowSharingTextureHost(bool aAllow) - { - mAllowSharingTextureHost = aAllow; - } - - // Create new TextureSharedDataGonkOGL. - // If aImage is already bound to OpenGL texture, the OpenGL textre is carried over - // to a new object. It could reduce calling fEGLImageTargetTexture2D() - // during resources ownership carry over from CompositableHost to TextureHost. - TemporaryRef GetNewTextureBackendSpecificData(EGLImage aImage); - - GLuint GetTexture(); - void DeleteTextureIfPresent(); - gl::GLContext* gl() const; - void BindEGLImage(GLuint aTarget, EGLImage aImage); - void ClearBoundEGLImage(EGLImage aImage); - bool IsEGLImageBound(EGLImage aImage); -protected: - GLuint GetAndResetGLTextureOwnership(); - - bool mOwnedByCompositableHost; - bool mAllowSharingTextureHost; - RefPtr mCompositor; - GLuint mTexture; - EGLImage mBoundEGLImage; -}; +class GLTextureSource; inline void ApplyFilterToBoundTexture(gl::GLContext* aGL, gfx::Filter aFilter, @@ -202,6 +110,8 @@ public: virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() { return nullptr; } + virtual GLTextureSource* AsGLTextureSource() { return nullptr; } + void SetFilter(gl::GLContext* aGL, gfx::Filter aFilter) { if (mHasCachedFilter && @@ -370,10 +280,15 @@ class GLTextureSource : public TextureSource { public: GLTextureSource(CompositorOGL* aCompositor, - GLuint aTex, - gfx::SurfaceFormat aFormat, + GLuint aTextureHandle, GLenum aTarget, - gfx::IntSize aSize); + gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, + bool aExternallyOwned = false); + + ~GLTextureSource(); + + virtual GLTextureSource* AsGLTextureSource() MOZ_OVERRIDE { return this; } virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; } @@ -389,18 +304,29 @@ public: virtual GLenum GetWrapMode() const MOZ_OVERRIDE { return LOCAL_GL_CLAMP_TO_EDGE; } - virtual void DeallocateDeviceData() MOZ_OVERRIDE {} + virtual void DeallocateDeviceData() MOZ_OVERRIDE; virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE; + void SetSize(gfx::IntSize aSize) { mSize = aSize; } + + void SetFormat(gfx::SurfaceFormat aFormat) { mFormat = aFormat; } + + GLuint GetTextureHandle() const { return mTextureHandle; } + gl::GLContext* gl() const; protected: - const gfx::IntSize mSize; + void DeleteTextureHandle(); + RefPtr mCompositor; - const GLuint mTex; - const gfx::SurfaceFormat mFormat; - const GLenum mTextureTarget; + GLuint mTextureHandle; + GLenum mTextureTarget; + gfx::IntSize mSize; + gfx::SurfaceFormat mFormat; + // If the texture is externally owned, the gl handle will not be deleted + // in the destructor. + bool mExternallyOwned; }; //////////////////////////////////////////////////////////////////////// From b19fa39e01ce68de4180706e60639a5d8d25f845 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Thu, 16 Oct 2014 18:39:30 +0200 Subject: [PATCH 04/81] Bug 1081660 - Remove property iterator from JSAPI. r=Waldo --- js/src/ctypes/CTypes.cpp | 56 ++++++----------- js/src/jsapi.cpp | 131 --------------------------------------- js/src/jsapi.h | 16 ----- 3 files changed, 20 insertions(+), 183 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index bdcdb28e89ea..82c47b7a0b49 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -2625,8 +2625,8 @@ ImplicitConvert(JSContext* cx, if (val.isObject() && !sourceData) { // Enumerate the properties of the object; if they match the struct // specification, convert the fields. - RootedObject iter(cx, JS_NewPropertyIterator(cx, valObj)); - if (!iter) + AutoIdArray props(cx, JS_Enumerate(cx, valObj)); + if (!props) return false; // Convert into an intermediate, in case of failure. @@ -2637,13 +2637,15 @@ ImplicitConvert(JSContext* cx, return false; } + const FieldInfoHash* fields = StructType::GetFieldInfo(targetType); + if (props.length() != fields->count()) { + JS_ReportError(cx, "missing fields"); + return false; + } + RootedId id(cx); - size_t i = 0; - while (1) { - if (!JS_NextProperty(cx, iter, &id)) - return false; - if (JSID_IS_VOID(id)) - break; + for (size_t i = 0; i < props.length(); ++i) { + id = props[i]; if (!JSID_IS_STRING(id)) { JS_ReportError(cx, "property name is not a string"); @@ -2663,14 +2665,6 @@ ImplicitConvert(JSContext* cx, char* fieldData = intermediate.get() + field->mOffset; if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, nullptr)) return false; - - ++i; - } - - const FieldInfoHash* fields = StructType::GetFieldInfo(targetType); - if (i != fields->count()) { - JS_ReportError(cx, "missing fields"); - return false; } memcpy(buffer, intermediate.get(), structSize); @@ -4705,33 +4699,23 @@ ExtractStructField(JSContext* cx, jsval val, MutableHandleObject typeObj) return nullptr; } - RootedObject obj(cx, val.toObjectOrNull()); - RootedObject iter(cx, JS_NewPropertyIterator(cx, obj)); - if (!iter) + RootedObject obj(cx, &val.toObject()); + AutoIdArray props(cx, JS_Enumerate(cx, obj)); + if (!props) return nullptr; - RootedId nameid(cx); - if (!JS_NextProperty(cx, iter, &nameid)) - return nullptr; - if (JSID_IS_VOID(nameid)) { - JS_ReportError(cx, "struct field descriptors require a valid name and type"); - return nullptr; - } - - if (!JSID_IS_STRING(nameid)) { - JS_ReportError(cx, "struct field descriptors require a valid name and type"); - return nullptr; - } - // make sure we have one, and only one, property - RootedId id(cx); - if (!JS_NextProperty(cx, iter, &id)) - return nullptr; - if (!JSID_IS_VOID(id)) { + if (props.length() != 1) { JS_ReportError(cx, "struct field descriptors must contain one property"); return nullptr; } + RootedId nameid(cx, props[0]); + if (!JSID_IS_STRING(nameid)) { + JS_ReportError(cx, "struct field descriptors require a valid name and type"); + return nullptr; + } + RootedValue propVal(cx); if (!JS_GetPropertyById(cx, obj, nameid, &propVal)) return nullptr; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ff7fe5d6b5c7..e482bc962b91 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3620,137 +3620,6 @@ JS_Enumerate(JSContext *cx, HandleObject obj) return ida; } -/* - * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's - * prop_iterator_class somehow... - * + preserve the obj->enumerate API while optimizing the native object case - * + native case here uses a JSShape *, but that iterates in reverse! - * + so we make non-native match, by reverse-iterating after JS_Enumerating - */ -static const uint32_t JSSLOT_ITER_INDEX = 0; - -static void -prop_iter_finalize(FreeOp *fop, JSObject *obj) -{ - void *pdata = obj->as().getPrivate(); - if (!pdata) - return; - - if (obj->as().getSlot(JSSLOT_ITER_INDEX).toInt32() >= 0) { - /* Non-native case: destroy the ida enumerated when obj was created. */ - JSIdArray *ida = (JSIdArray *) pdata; - fop->free_(ida); - } -} - -static void -prop_iter_trace(JSTracer *trc, JSObject *obj) -{ - void *pdata = obj->as().getPrivate(); - if (!pdata) - return; - - if (obj->as().getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) { - /* - * Native case: just mark the next property to visit. We don't need a - * barrier here because the pointer is updated via setPrivate, which - * always takes a barrier. - */ - Shape *tmp = static_cast(pdata); - MarkShapeUnbarriered(trc, &tmp, "prop iter shape"); - obj->as().setPrivateUnbarriered(tmp); - } else { - /* Non-native case: mark each id in the JSIdArray private. */ - JSIdArray *ida = (JSIdArray *) pdata; - MarkIdRange(trc, ida->length, ida->vector, "prop iter"); - } -} - -static const Class prop_iter_class = { - "PropertyIterator", - JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(1), - JS_PropertyStub, /* addProperty */ - JS_DeletePropertyStub, /* delProperty */ - JS_PropertyStub, /* getProperty */ - JS_StrictPropertyStub, /* setProperty */ - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub, - prop_iter_finalize, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - prop_iter_trace -}; - -JS_PUBLIC_API(JSObject *) -JS_NewPropertyIterator(JSContext *cx, HandleObject obj) -{ - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, obj); - - RootedNativeObject iterobj(cx, NewNativeObjectWithClassProto(cx, &prop_iter_class, - nullptr, obj)); - if (!iterobj) - return nullptr; - - int index; - if (obj->isNative()) { - /* Native case: start with the last property in obj. */ - iterobj->setPrivateGCThing(obj->lastProperty()); - index = -1; - } else { - /* Non-native case: enumerate a JSIdArray and keep it via private. */ - JSIdArray *ida = JS_Enumerate(cx, obj); - if (!ida) - return nullptr; - iterobj->setPrivate((void *)ida); - index = ida->length; - } - - /* iterobj cannot escape to other threads here. */ - iterobj->setSlot(JSSLOT_ITER_INDEX, Int32Value(index)); - return iterobj; -} - -JS_PUBLIC_API(bool) -JS_NextProperty(JSContext *cx, HandleObject iterobj, MutableHandleId idp) -{ - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, iterobj); - int32_t i = iterobj->as().getSlot(JSSLOT_ITER_INDEX).toInt32(); - if (i < 0) { - /* Native case: private data is a property tree node pointer. */ - MOZ_ASSERT(iterobj->getParent()->isNative()); - Shape *shape = static_cast(iterobj->as().getPrivate()); - - while (shape->previous() && !shape->enumerable()) - shape = shape->previous(); - - if (!shape->previous()) { - MOZ_ASSERT(shape->isEmptyShape()); - idp.set(JSID_VOID); - } else { - iterobj->as().setPrivateGCThing(const_cast(shape->previous().get())); - idp.set(shape->propid()); - } - } else { - /* Non-native case: use the ida enumerated when iterobj was created. */ - JSIdArray *ida = (JSIdArray *) iterobj->as().getPrivate(); - MOZ_ASSERT(i <= ida->length); - STATIC_ASSUME(i <= ida->length); - if (i == 0) { - idp.set(JSID_VOID); - } else { - idp.set(ida->vector[--i]); - iterobj->as().setSlot(JSSLOT_ITER_INDEX, Int32Value(i)); - } - } - return true; -} - JS_PUBLIC_API(jsval) JS_GetReservedSlot(JSObject *obj, uint32_t index) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 41b65c871fe0..60a7f8328e86 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3305,22 +3305,6 @@ JS_ReleaseMappedArrayBufferContents(void *contents, size_t length); extern JS_PUBLIC_API(JSIdArray *) JS_Enumerate(JSContext *cx, JS::HandleObject obj); -/* - * Create an object to iterate over enumerable properties of obj, in arbitrary - * property definition order. NB: This differs from longstanding for..in loop - * order, which uses order of property definition in obj. - */ -extern JS_PUBLIC_API(JSObject *) -JS_NewPropertyIterator(JSContext *cx, JS::Handle obj); - -/* - * Return true on success with *idp containing the id of the next enumerable - * property to visit using iterobj, or JSID_IS_VOID if there is no such property - * left to visit. Return false on error. - */ -extern JS_PUBLIC_API(bool) -JS_NextProperty(JSContext *cx, JS::HandleObject iterobj, JS::MutableHandleId idp); - extern JS_PUBLIC_API(jsval) JS_GetReservedSlot(JSObject *obj, uint32_t index); From bfd10d65542a4c96fe91a0366a949d937541d696 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Thu, 16 Oct 2014 18:39:38 +0200 Subject: [PATCH 05/81] Bug 1071177 - Support symbol keys and throw exception on unique symbols with CPOWs. r=billm --- content/base/test/chrome/cpows_child.js | 9 +++++---- content/base/test/chrome/cpows_parent.xul | 14 +++++++++++--- js/ipc/JavaScriptBase.h | 8 ++++---- js/ipc/JavaScriptShared.cpp | 3 ++- js/ipc/PJavaScript.ipdl | 2 +- js/ipc/WrapperAnswer.cpp | 8 ++++---- js/ipc/WrapperAnswer.h | 2 +- js/ipc/WrapperOwner.cpp | 14 +++++++------- js/ipc/WrapperOwner.h | 2 +- 9 files changed, 36 insertions(+), 26 deletions(-) diff --git a/content/base/test/chrome/cpows_child.js b/content/base/test/chrome/cpows_child.js index 8dc79bd497a2..1290d9eb556d 100644 --- a/content/base/test/chrome/cpows_child.js +++ b/content/base/test/chrome/cpows_child.js @@ -12,6 +12,9 @@ var is_remote; error_reporting_test(); dom_test(); xray_test(); + if (typeof Symbol === "function") { + symbol_test(); + } compartment_test(); regexp_test(); sync_test(); @@ -119,15 +122,13 @@ function symbol_test() { let iterator = Symbol.iterator; let named = Symbol.for("cpow-test"); - // let unique = Symbol(); let object = { [iterator]: iterator, [named]: named, - // [unique]: unique, - // "unique": unique }; - sendSyncMessage("cpows:symbol_test", {}, object); + let test = ['a']; + sendSyncMessage("cpows:symbol_test", {}, {object: object, test: test}); } // Parent->Child references should go X->parent.privilegedJunkScope->child.privilegedJunkScope->Y diff --git a/content/base/test/chrome/cpows_parent.xul b/content/base/test/chrome/cpows_parent.xul index 74c5437c297b..73876f63f016 100644 --- a/content/base/test/chrome/cpows_parent.xul +++ b/content/base/test/chrome/cpows_parent.xul @@ -206,10 +206,16 @@ } function recvSymbolTest(message) { - let object = message.objects; + let object = message.objects.object; is(object[Symbol.iterator], Symbol.iterator, "Should use Symbol.iterator"); is(Symbol.keyFor(object[Symbol.for("cpow-test")]), "cpow-test", "Symbols aren't registered correctly"); - // is(object.unique, object[object.unique], "Unique symbols as ids and values don't seem to work"); + let symbols = Object.getOwnPropertySymbols(object); + is(symbols.length, 2, "Object should have two symbol keys"); + let test = undefined; + for (let x of message.objects.test) { + test = x; + } + is(test, "a", "for .. of iteration should work"); } let systemGlobal = this; @@ -307,7 +313,9 @@ mm.addMessageListener("cpows:dom_test", recvDomTest); mm.addMessageListener("cpows:dom_test_after_gc", recvDomTestAfterGC); mm.addMessageListener("cpows:xray_test", recvXrayTest); - mm.addMessageListener("cpows:symbol_test", recvSymbolTest); + if (typeof Symbol === "function") { + mm.addMessageListener("cpows:symbol_test", recvSymbolTest); + } mm.addMessageListener("cpows:compartment_test", recvCompartmentTest); mm.addMessageListener("cpows:regexp_test", recvRegExpTest); mm.addMessageListener("cpows:lifetime_test_1", recvLifetimeTest1); diff --git a/js/ipc/JavaScriptBase.h b/js/ipc/JavaScriptBase.h index 4ff69ab984ad..a7cf7ff46ee2 100644 --- a/js/ipc/JavaScriptBase.h +++ b/js/ipc/JavaScriptBase.h @@ -102,8 +102,8 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base } bool RecvGetPropertyKeys(const uint64_t &objId, const uint32_t &flags, - ReturnStatus *rs, nsTArray *names) { - return Answer::RecvGetPropertyKeys(ObjectId::deserialize(objId), flags, rs, names); + ReturnStatus *rs, nsTArray *ids) { + return Answer::RecvGetPropertyKeys(ObjectId::deserialize(objId), flags, rs, ids); } bool RecvInstanceOf(const uint64_t &objId, const JSIID &iid, ReturnStatus *rs, bool *instanceof) { @@ -200,8 +200,8 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base } bool SendGetPropertyKeys(const ObjectId &objId, const uint32_t &flags, - ReturnStatus *rs, nsTArray *names) { - return Base::SendGetPropertyKeys(objId.serialize(), flags, rs, names); + ReturnStatus *rs, nsTArray *ids) { + return Base::SendGetPropertyKeys(objId.serialize(), flags, rs, ids); } bool SendInstanceOf(const ObjectId &objId, const JSIID &iid, ReturnStatus *rs, bool *instanceof) { diff --git a/js/ipc/JavaScriptShared.cpp b/js/ipc/JavaScriptShared.cpp index cff04a719612..38a73cb470e4 100644 --- a/js/ipc/JavaScriptShared.cpp +++ b/js/ipc/JavaScriptShared.cpp @@ -440,7 +440,8 @@ JavaScriptShared::toSymbolVariant(JSContext *cx, JS::Symbol *symArg, SymbolVaria *symVarp = RegisteredSymbol(autoStr); return true; } - MOZ_CRASH("unique symbols not yet implemented"); + + JS_ReportError(cx, "unique symbol can't be used with CPOW"); return false; } diff --git a/js/ipc/PJavaScript.ipdl b/js/ipc/PJavaScript.ipdl index b17a02ad499d..d8d0254e97e6 100644 --- a/js/ipc/PJavaScript.ipdl +++ b/js/ipc/PJavaScript.ipdl @@ -42,7 +42,7 @@ both: prio(high) sync ClassName(uint64_t objId) returns (nsString name); prio(high) sync RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags); - prio(high) sync GetPropertyKeys(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, nsString[] names); + prio(high) sync GetPropertyKeys(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, JSIDVariant[] ids); prio(high) sync InstanceOf(uint64_t objId, JSIID iid) returns (ReturnStatus rs, bool instanceof); prio(high) sync DOMInstanceOf(uint64_t objId, int prototypeID, int depth) returns (ReturnStatus rs, bool instanceof); diff --git a/js/ipc/WrapperAnswer.cpp b/js/ipc/WrapperAnswer.cpp index b63013e70b9c..ce8a7eaf4f58 100644 --- a/js/ipc/WrapperAnswer.cpp +++ b/js/ipc/WrapperAnswer.cpp @@ -580,7 +580,7 @@ WrapperAnswer::RecvRegExpToShared(const ObjectId &objId, ReturnStatus *rs, bool WrapperAnswer::RecvGetPropertyKeys(const ObjectId &objId, const uint32_t &flags, - ReturnStatus *rs, nsTArray *names) + ReturnStatus *rs, nsTArray *ids) { AutoSafeJSContext cx; JSAutoRequest request(cx); @@ -598,11 +598,11 @@ WrapperAnswer::RecvGetPropertyKeys(const ObjectId &objId, const uint32_t &flags, return fail(cx, rs); for (size_t i = 0; i < props.length(); i++) { - nsString name; - if (!convertIdToGeckoString(cx, props[i], &name)) + JSIDVariant id; + if (!toJSIDVariant(cx, props[i], &id)) return fail(cx, rs); - names->AppendElement(name); + ids->AppendElement(id); } return ok(rs); diff --git a/js/ipc/WrapperAnswer.h b/js/ipc/WrapperAnswer.h index fc3b481f9532..08171528de21 100644 --- a/js/ipc/WrapperAnswer.h +++ b/js/ipc/WrapperAnswer.h @@ -55,7 +55,7 @@ class WrapperAnswer : public virtual JavaScriptShared bool RecvRegExpToShared(const ObjectId &objId, ReturnStatus *rs, nsString *source, uint32_t *flags); bool RecvGetPropertyKeys(const ObjectId &objId, const uint32_t &flags, - ReturnStatus *rs, nsTArray *names); + ReturnStatus *rs, nsTArray *ids); bool RecvInstanceOf(const ObjectId &objId, const JSIID &iid, ReturnStatus *rs, bool *instanceof); bool RecvDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth, diff --git a/js/ipc/WrapperOwner.cpp b/js/ipc/WrapperOwner.cpp index 31abb5ce2a3d..adc5df67f67f 100644 --- a/js/ipc/WrapperOwner.cpp +++ b/js/ipc/WrapperOwner.cpp @@ -232,7 +232,7 @@ CPOWProxyHandler::ownPropertyKeys(JSContext *cx, HandleObject proxy, bool WrapperOwner::ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props) { - return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN, props); + return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); } bool @@ -776,8 +776,8 @@ WrapperOwner::getPropertyKeys(JSContext *cx, HandleObject proxy, uint32_t flags, ObjectId objId = idOf(proxy); ReturnStatus status; - InfallibleTArray names; - if (!SendGetPropertyKeys(objId, flags, &status, &names)) + InfallibleTArray ids; + if (!SendGetPropertyKeys(objId, flags, &status, &ids)) return ipcfail(cx); LOG_STACK(); @@ -785,11 +785,11 @@ WrapperOwner::getPropertyKeys(JSContext *cx, HandleObject proxy, uint32_t flags, if (!ok(cx, status)) return false; - for (size_t i = 0; i < names.Length(); i++) { - RootedId name(cx); - if (!convertGeckoStringToId(cx, names[i], &name)) + for (size_t i = 0; i < ids.Length(); i++) { + RootedId id(cx); + if (!fromJSIDVariant(cx, ids[i], &id)) return false; - if (!props.append(name)) + if (!props.append(id)) return false; } diff --git a/js/ipc/WrapperOwner.h b/js/ipc/WrapperOwner.h index 215043dfc072..26f19113c72d 100644 --- a/js/ipc/WrapperOwner.h +++ b/js/ipc/WrapperOwner.h @@ -146,7 +146,7 @@ class WrapperOwner : public virtual JavaScriptShared uint32_t *flags) = 0; virtual bool SendGetPropertyKeys(const ObjectId &objId, const uint32_t &flags, - ReturnStatus *rs, nsTArray *names) = 0; + ReturnStatus *rs, nsTArray *ids) = 0; virtual bool SendInstanceOf(const ObjectId &objId, const JSIID &iid, ReturnStatus *rs, bool *instanceof) = 0; virtual bool SendDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth, From 0e08e7fd641a90edc2a1f0825b1b55262109fe45 Mon Sep 17 00:00:00 2001 From: Georg Fritzsche Date: Thu, 16 Oct 2014 20:11:03 +0200 Subject: [PATCH 06/81] Bug 1064333 - Migrate the FHR client id to the datareporting service. r=gps --- services/common/utils.js | 5 +- .../datareporting/DataReportingService.js | 116 ++++++++++++++++++ services/datareporting/policy.jsm | 3 +- .../tests/xpcshell/test_client_id.js | 86 +++++++++++++ .../datareporting/tests/xpcshell/xpcshell.ini | 1 + services/healthreport/healthreporter.jsm | 47 +++---- .../healthreport/modules-testing/utils.jsm | 14 ++- .../tests/xpcshell/test_healthreporter.js | 47 +++---- .../tests/xpcshell/test_provider_appinfo.js | 15 ++- 9 files changed, 264 insertions(+), 70 deletions(-) create mode 100644 services/datareporting/tests/xpcshell/test_client_id.js diff --git a/services/common/utils.js b/services/common/utils.js index 6d7b30b04e8a..8ad3c90d1d21 100644 --- a/services/common/utils.js +++ b/services/common/utils.js @@ -398,9 +398,8 @@ this.CommonUtils = { * @return a promise, as produced by OS.File.writeAtomic. */ writeJSON: function(contents, path) { - let encoder = new TextEncoder(); - let array = encoder.encode(JSON.stringify(contents)); - return OS.File.writeAtomic(path, array, {tmpPath: path + ".tmp"}); + let data = JSON.stringify(contents); + return OS.File.writeAtomic(path, data, {encoding: "utf-8", tmpPath: path + ".tmp"}); }, diff --git a/services/datareporting/DataReportingService.js b/services/datareporting/DataReportingService.js index 457c4a3bacb2..e7a1aed38a11 100644 --- a/services/datareporting/DataReportingService.js +++ b/services/datareporting/DataReportingService.js @@ -9,6 +9,9 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-common/utils.js"); +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); const ROOT_BRANCH = "datareporting."; @@ -61,6 +64,13 @@ this.DataReportingService = function () { this._os = Cc["@mozilla.org/observer-service;1"] .getService(Ci.nsIObserverService); + + this._clientID = null; + this._loadClientIdTask = null; + this._saveClientIdTask = null; + + this._stateDir = null; + this._stateFilePath = null; } DataReportingService.prototype = Object.freeze({ @@ -126,6 +136,9 @@ DataReportingService.prototype = Object.freeze({ let policyPrefs = new Preferences(POLICY_BRANCH); this.policy = new DataReportingPolicy(policyPrefs, this._prefs, this); + this._stateDir = OS.Path.join(OS.Constants.Path.profileDir, "datareporting"); + this._stateFilePath = OS.Path.join(this._stateDir, "state.json"); + this._os.addObserver(this, "sessionstore-windows-restored", true); } catch (ex) { Cu.reportError("Exception when initializing data reporting service: " + @@ -284,6 +297,109 @@ DataReportingService.prototype = Object.freeze({ this._prefs.set("service.firstRun", true); }.bind(this)); }, + + _loadClientID: Task.async(function* () { + if (this._loadClientIdTask) { + return this._loadClientIdTask; + } + + // Previously we had the stable client ID managed in FHR. + // As we want to start correlating FHR and telemetry data (and moving towards + // unifying the two), we moved the ID management to the datareporting + // service. Consequently, we try to import the FHR ID first, so we can keep + // using it. + + // Try to load the client id from the DRS state file first. + try { + let state = yield CommonUtils.readJSON(this._stateFilePath); + if (state && 'clientID' in state && typeof(state.clientID) == 'string') { + this._clientID = state.clientID; + this._loadClientIdTask = null; + return this._clientID; + } + } catch (e) { + // fall through to next option + } + + // If we dont have DRS state yet, try to import from the FHR state. + try { + let fhrStatePath = OS.Path.join(OS.Constants.Path.profileDir, "healthreport", "state.json"); + let state = yield CommonUtils.readJSON(fhrStatePath); + if (state && 'clientID' in state && typeof(state.clientID) == 'string') { + this._clientID = state.clientID; + this._loadClientIdTask = null; + this._saveClientID(); + return this._clientID; + } + } catch (e) { + // fall through to next option + } + + // We dont have an id from FHR yet, generate a new ID. + this._clientID = CommonUtils.generateUUID(); + this._loadClientIdTask = null; + this._saveClientIdTask = this._saveClientID(); + + // Wait on persisting the id. Otherwise failure to save the ID would result in + // the client creating and subsequently sending multiple IDs to the server. + // This would appear as multiple clients submitting similar data, which would + // result in orphaning. + yield this._saveClientIdTask; + + return this._clientID; + }), + + _saveClientID: Task.async(function* () { + let obj = { clientID: this._clientID }; + yield OS.File.makeDir(this._stateDir); + yield CommonUtils.writeJSON(obj, this._stateFilePath); + this._saveClientIdTask = null; + }), + + /** + * This returns a promise resolving to the the stable client ID we use for + * data reporting (FHR & Telemetry). Previously exising FHR client IDs are + * migrated to this. + * + * @return Promise The stable client ID. + */ + getClientID: function() { + if (this._loadClientIdTask) { + return this._loadClientIdTask; + } + + if (!this._clientID) { + this._loadClientIdTask = this._loadClientID(); + return this._loadClientIdTask; + } + + return Promise.resolve(this._clientID); + }, + + /** + * Reset the stable client id. + * + * @return Promise The new client ID. + */ + resetClientID: Task.async(function* () { + yield this._loadClientIdTask; + yield this._saveClientIdTask; + + this._clientID = CommonUtils.generateUUID(); + this._saveClientIdTask = this._saveClientID(); + yield this._saveClientIdTask; + + return this._clientID; + }), + + /* + * Simulate a restart of the service. This is for testing only. + */ + _reset: Task.async(function* () { + yield this._loadClientIdTask; + yield this._saveClientIdTask; + this._clientID = null; + }), }); this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataReportingService]); diff --git a/services/datareporting/policy.jsm b/services/datareporting/policy.jsm index f68c7885f0a3..b95d12f9c0f6 100644 --- a/services/datareporting/policy.jsm +++ b/services/datareporting/policy.jsm @@ -817,7 +817,8 @@ this.DataReportingPolicy.prototype = Object.freeze({ this._log.info("Requesting data submission. Will expire at " + requestExpiresDate); try { - this._listener[handler](this._inProgressSubmissionRequest); + let promise = this._listener[handler](this._inProgressSubmissionRequest); + chained = chained.then(() => promise, null); } catch (ex) { this._log.warn("Exception when calling " + handler + ": " + CommonUtils.exceptionStr(ex)); diff --git a/services/datareporting/tests/xpcshell/test_client_id.js b/services/datareporting/tests/xpcshell/test_client_id.js new file mode 100644 index 000000000000..d72b66b2c4b2 --- /dev/null +++ b/services/datareporting/tests/xpcshell/test_client_id.js @@ -0,0 +1,86 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://services-common/utils.js"); +Cu.import("resource://gre/modules/osfile.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyGetter(this, "gDatareportingService", + () => Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject); + +function run_test() { + do_get_profile(); + + // Send the needed startup notifications to the datareporting service + // to ensure that it has been initialized. + gDatareportingService.observe(null, "app-startup", null); + gDatareportingService.observe(null, "profile-after-change", null); + + run_next_test(); +} + +add_task(function* () { + const drsPath = gDatareportingService._stateFilePath; + const fhrDir = OS.Path.join(OS.Constants.Path.profileDir, "healthreport"); + const fhrPath = OS.Path.join(fhrDir, "state.json"); + const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + + yield OS.File.makeDir(fhrDir); + + // Check that we are importing the FHR client ID. + let clientID = CommonUtils.generateUUID(); + yield CommonUtils.writeJSON({clientID: clientID}, fhrPath); + Assert.equal(clientID, yield gDatareportingService.getClientID()); + + // We should persist the ID in DRS now and not pick up a differing ID from FHR. + yield gDatareportingService._reset(); + yield CommonUtils.writeJSON({clientID: CommonUtils.generateUUID()}, fhrPath); + Assert.equal(clientID, yield gDatareportingService.getClientID()); + + // We should be guarded against broken FHR data. + yield gDatareportingService._reset(); + yield OS.File.remove(drsPath); + yield CommonUtils.writeJSON({clientID: -1}, fhrPath); + clientID = yield gDatareportingService.getClientID(); + Assert.equal(typeof(clientID), 'string'); + Assert.ok(uuidRegex.test(clientID)); + + // We should be guarded against invalid FHR json. + yield gDatareportingService._reset(); + yield OS.File.remove(drsPath); + yield OS.File.writeAtomic(fhrPath, "abcd", {encoding: "utf-8", tmpPath: fhrPath + ".tmp"}); + clientID = yield gDatareportingService.getClientID(); + Assert.equal(typeof(clientID), 'string'); + Assert.ok(uuidRegex.test(clientID)); + + // We should be guarded against broken DRS data too and fall back to loading + // the FHR ID. + yield gDatareportingService._reset(); + clientID = CommonUtils.generateUUID(); + yield CommonUtils.writeJSON({clientID: clientID}, fhrPath); + yield CommonUtils.writeJSON({clientID: -1}, drsPath); + Assert.equal(clientID, yield gDatareportingService.getClientID()); + + // We should be guarded against invalid DRS json too. + yield gDatareportingService._reset(); + yield OS.File.remove(fhrPath); + yield OS.File.writeAtomic(drsPath, "abcd", {encoding: "utf-8", tmpPath: drsPath + ".tmp"}); + clientID = yield gDatareportingService.getClientID(); + Assert.equal(typeof(clientID), 'string'); + Assert.ok(uuidRegex.test(clientID)); + + // If both the FHR and DSR data are broken, we should end up with a new client ID. + yield gDatareportingService._reset(); + yield CommonUtils.writeJSON({clientID: -1}, fhrPath); + yield CommonUtils.writeJSON({clientID: -1}, drsPath); + clientID = yield gDatareportingService.getClientID(); + Assert.equal(typeof(clientID), 'string'); + Assert.ok(uuidRegex.test(clientID)); +}); diff --git a/services/datareporting/tests/xpcshell/xpcshell.ini b/services/datareporting/tests/xpcshell/xpcshell.ini index 934227e192db..fbd88acf2b53 100644 --- a/services/datareporting/tests/xpcshell/xpcshell.ini +++ b/services/datareporting/tests/xpcshell/xpcshell.ini @@ -5,3 +5,4 @@ skip-if = toolkit == 'android' || toolkit == 'gonk' [test_policy.js] [test_session_recorder.js] +[test_client_id.js] diff --git a/services/healthreport/healthreporter.jsm b/services/healthreport/healthreporter.jsm index ef0a4c287436..2c79bfaaf0c3 100644 --- a/services/healthreport/healthreporter.jsm +++ b/services/healthreport/healthreporter.jsm @@ -130,13 +130,18 @@ HealthReporterState.prototype = Object.freeze({ return Task.spawn(function* init() { yield OS.File.makeDir(this._stateDir); + let drs = Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject; + let drsClientID = yield drs.getClientID(); + let resetObjectState = function () { this._s = { // The payload version. This is bumped whenever there is a // backwards-incompatible change. v: 1, // The persistent client identifier. - clientID: CommonUtils.generateUUID(), + clientID: drsClientID, // Denotes the mechanism used to generate the client identifier. // 1: Random UUID. clientIDVersion: 1, @@ -176,22 +181,7 @@ HealthReporterState.prototype = Object.freeze({ // comes along and fixes us. } - let regen = false; - if (!this._s.clientID) { - this._log.warn("No client ID stored. Generating random ID."); - regen = true; - } - - if (typeof(this._s.clientID) != "string") { - this._log.warn("Client ID is not a string. Regenerating."); - regen = true; - } - - if (regen) { - this._s.clientID = CommonUtils.generateUUID(); - this._s.clientIDVersion = 1; - yield this.save(); - } + this._s.clientID = drsClientID; // Always look for preferences. This ensures that downgrades followed // by reupgrades don't result in excessive data loss. @@ -255,21 +245,18 @@ HealthReporterState.prototype = Object.freeze({ /** * Reset the client ID to something else. - * - * This fails if remote IDs are stored because changing the client ID - * while there is remote data will create orphaned records. + * Returns a promise that is resolved when completed. */ - resetClientID: function () { - if (this.remoteIDs.length) { - throw new Error("Cannot reset client ID while remote IDs are stored."); - } + resetClientID: Task.async(function* () { + let drs = Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject; + yield drs.resetClientID(); + this._s.clientID = yield drs.getClientID(); + this._log.info("Reset client id to " + this._s.clientID + "."); - this._log.warn("Resetting client ID."); - this._s.clientID = CommonUtils.generateUUID(); - this._s.clientIDVersion = 1; - - return this.save(); - }, + yield this.save(); + }), _migratePrefs: function () { let prefs = this._reporter._prefs; diff --git a/services/healthreport/modules-testing/utils.jsm b/services/healthreport/modules-testing/utils.jsm index a08ce954c63c..bad2f6c8cca4 100644 --- a/services/healthreport/modules-testing/utils.jsm +++ b/services/healthreport/modules-testing/utils.jsm @@ -182,6 +182,14 @@ InspectedHealthReporter.prototype = { const DUMMY_URI="http://localhost:62013/"; this.getHealthReporter = function (name, uri=DUMMY_URI, inspected=false) { + // The healthreporters use the client id from the datareporting service, + // so we need to ensure it is initialized. + let drs = Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject; + drs.observe(null, "app-startup", null); + drs.observe(null, "profile-after-change", null); + let branch = "healthreport.testing." + name + "."; let prefs = new Preferences(branch + "healthreport."); @@ -193,12 +201,14 @@ this.getHealthReporter = function (name, uri=DUMMY_URI, inspected=false) { let policyPrefs = new Preferences(branch + "policy."); let listener = new MockPolicyListener(); listener.onRequestDataUpload = function (request) { - reporter.requestDataUpload(request); + let promise = reporter.requestDataUpload(request); MockPolicyListener.prototype.onRequestDataUpload.call(this, request); + return promise; } listener.onRequestRemoteDelete = function (request) { - reporter.deleteRemoteData(request); + let promise = reporter.deleteRemoteData(request); MockPolicyListener.prototype.onRequestRemoteDelete.call(this, request); + return promise; } let policy = new DataReportingPolicy(policyPrefs, prefs, listener); let type = inspected ? InspectedHealthReporter : HealthReporter; diff --git a/services/healthreport/tests/xpcshell/test_healthreporter.js b/services/healthreport/tests/xpcshell/test_healthreporter.js index 1eecc8c4ad4e..9bb2b3ed2276 100644 --- a/services/healthreport/tests/xpcshell/test_healthreporter.js +++ b/services/healthreport/tests/xpcshell/test_healthreporter.js @@ -21,6 +21,12 @@ Cu.import("resource://testing-common/services/common/bagheeraserver.js"); Cu.import("resource://testing-common/services/metrics/mocks.jsm"); Cu.import("resource://testing-common/services/healthreport/utils.jsm"); Cu.import("resource://testing-common/AppData.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyGetter(this, "gDatareportingService", + () => Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject); const DUMMY_URI = "http://localhost:62013/"; @@ -108,6 +114,13 @@ function ensureUserNotified (reporter) { } function run_test() { + do_get_profile(); + + // Send the needed startup notifications to the datareporting service + // to ensure that it has been initialized. + gDatareportingService.observe(null, "app-startup", null); + gDatareportingService.observe(null, "profile-after-change", null); + run_next_test(); } @@ -745,7 +758,7 @@ add_task(function test_request_remote_data_deletion() { do_check_false(reporter.haveRemoteData()); do_check_false(server.hasDocument(reporter.serverNamespace, id)); - // Client ID should be updated. + // Client ID should be updated. do_check_neq(reporter._state.clientID, null); do_check_neq(reporter._state.clientID, clientID); do_check_eq(reporter._state.clientIDVersion, 1); @@ -1171,38 +1184,6 @@ add_task(function test_state_downgrade_upgrade() { } }); -// Missing client ID in state should be created on state load. -add_task(function* test_state_create_client_id() { - let reporter = getHealthReporter("state_create_client_id"); - - yield CommonUtils.writeJSON({ - v: 1, - remoteIDs: ["id1", "id2"], - lastPingTime: Date.now(), - removeOutdatedLastPayload: true, - }, reporter._state._filename); - - try { - yield reporter.init(); - - do_check_eq(reporter.lastSubmitID, "id1"); - do_check_neq(reporter._state.clientID, null); - do_check_eq(reporter._state.clientID.length, 36); - do_check_eq(reporter._state.clientIDVersion, 1); - - let clientID = reporter._state.clientID; - - // The client ID should be persisted as soon as it is created. - reporter._shutdown(); - - reporter = getHealthReporter("state_create_client_id"); - yield reporter.init(); - do_check_eq(reporter._state.clientID, clientID); - } finally { - reporter._shutdown(); - } -}); - // Invalid stored client ID is reset automatically. add_task(function* test_empty_client_id() { let reporter = getHealthReporter("state_empty_client_id"); diff --git a/services/healthreport/tests/xpcshell/test_provider_appinfo.js b/services/healthreport/tests/xpcshell/test_provider_appinfo.js index 97b93ef96a54..6f5115110478 100644 --- a/services/healthreport/tests/xpcshell/test_provider_appinfo.js +++ b/services/healthreport/tests/xpcshell/test_provider_appinfo.js @@ -3,16 +3,29 @@ "use strict"; -const {interfaces: Ci, results: Cr, utils: Cu} = Components; +const {interfaces: Ci, results: Cr, utils: Cu, classes: Cc} = Components; Cu.import("resource://gre/modules/Metrics.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); Cu.import("resource://testing-common/services/healthreport/utils.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyGetter(this, "gDatareportingService", + () => Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject); function run_test() { + do_get_profile(); + + // Send the needed startup notifications to the datareporting service + // to ensure that it has been initialized. + gDatareportingService.observe(null, "app-startup", null); + gDatareportingService.observe(null, "profile-after-change", null); + run_next_test(); } From 5d3e093b48aa9bee5e7ee635722370e28da4ec39 Mon Sep 17 00:00:00 2001 From: Georg Fritzsche Date: Thu, 16 Oct 2014 20:11:17 +0200 Subject: [PATCH 07/81] Bug 1064333 - Add the stable client id to the telemetry ping. r=froydnj --- .../components/telemetry/TelemetryPing.jsm | 9 ++++++- .../tests/unit/test_TelemetryPing.js | 26 +++++++++++++++++++ .../tests/unit/test_TelemetryPingBuildID.js | 14 +++++++++- .../tests/unit/test_TelemetrySendOldPings.js | 12 +++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryPing.jsm b/toolkit/components/telemetry/TelemetryPing.jsm index d9c9f6132ee2..68d2db28da68 100644 --- a/toolkit/components/telemetry/TelemetryPing.jsm +++ b/toolkit/components/telemetry/TelemetryPing.jsm @@ -247,6 +247,7 @@ let Impl = { // The previous build ID, if this is the first run with a new build. // Undefined if this is not the first run, or the previous build ID is unknown. _previousBuildID: undefined, + _clientID: null, /** * Gets a series of simple measurements (counters). At the moment, this @@ -701,7 +702,8 @@ let Impl = { addonDetails: AddonManagerPrivate.getTelemetryDetails(), UIMeasurements: UITelemetry.getUIMeasurements(), log: TelemetryLog.entries(), - info: info + info: info, + clientID: this._clientID, }; if (Object.keys(this._slowSQLStartup).length != 0 && @@ -957,6 +959,11 @@ let Impl = { this.attachObservers(); this.gatherMemory(); + let drs = Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject; + this._clientID = yield drs.getClientID(); + Telemetry.asyncFetchTelemetryData(function () {}); delete this._timer; deferred.resolve(); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 7cc90e0d872e..30036a82beb9 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -41,11 +41,20 @@ const RW_OWNER = 0600; const NUMBER_OF_THREADS_TO_LAUNCH = 30; let gNumberOfThreadsLaunched = 0; +const PREF_BRANCH = "toolkit.telemetry."; +const PREF_ENABLED = PREF_BRANCH + "enabled"; + const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); let gHttpServer = new HttpServer(); let gServerStarted = false; let gRequestIterator = null; +let gDataReportingClientID = null; + +XPCOMUtils.defineLazyGetter(this, "gDatareportingService", + () => Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject); function sendPing () { TelemetryPing.gatherStartup(); @@ -162,6 +171,10 @@ function checkPayloadInfo(payload, reason) { do_check_true(payload.info.revision.startsWith("http")); } + do_check_true("clientID" in payload); + do_check_neq(payload.clientID, null); + do_check_eq(payload.clientID, gDataReportingClientID); + try { // If we've not got nsIGfxInfoDebug, then this will throw and stop us doing // this test. @@ -379,6 +392,13 @@ function run_test() { do_get_profile(); createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + Services.prefs.setBoolPref(PREF_ENABLED, true); + + // Send the needed startup notifications to the datareporting service + // to ensure that it has been initialized. + gDatareportingService.observe(null, "app-startup", null); + gDatareportingService.observe(null, "profile-after-change", null); + // Make it look like we've previously failed to lock a profile a couple times. write_fake_failedprofilelocks_file(); @@ -425,6 +445,12 @@ function actualTest() { run_next_test(); } +add_task(function* asyncSetup() { + yield TelemetryPing.setup(); + + gDataReportingClientID = yield gDatareportingService.getClientID(); +}); + // Ensure that not overwriting an existing file fails silently add_task(function* test_overwritePing() { let ping = {slug: "foo"} diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js index 9fca40c07390..f418f050c674 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js @@ -15,10 +15,16 @@ "use strict" -const Cu = Components.utils; +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/TelemetryPing.jsm", this); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyGetter(this, "gDatareportingService", + () => Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject); // Force the Telemetry enabled preference so that TelemetryPing.reset() doesn't exit early. Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true); @@ -65,5 +71,11 @@ add_task(function* test_newBuild() { function run_test() { // Make sure we have a profile directory. do_get_profile(); + + // Send the needed startup notifications to the datareporting service + // to ensure that it has been initialized. + gDatareportingService.observe(null, "app-startup", null); + gDatareportingService.observe(null, "profile-after-change", null); + run_next_test(); } diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js index ec3c4a1bc619..135bf4812766 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js @@ -23,8 +23,14 @@ Cu.import("resource://gre/modules/Promise.jsm", this); Cu.import("resource://gre/modules/TelemetryFile.jsm", this); Cu.import("resource://gre/modules/TelemetryPing.jsm", this); Cu.import("resource://gre/modules/Task.jsm", this); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); let {OS: {File, Path, Constants}} = Cu.import("resource://gre/modules/osfile.jsm", {}); +XPCOMUtils.defineLazyGetter(this, "gDatareportingService", + () => Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject); + // We increment TelemetryFile's MAX_PING_FILE_AGE and // OVERDUE_PING_FILE_AGE by 1 minute so that our test pings exceed // those points in time, even taking into account file system imprecision. @@ -190,6 +196,12 @@ function run_test() { gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler); gHttpServer.start(-1); do_get_profile(); + + // Send the needed startup notifications to the datareporting service + // to ensure that it has been initialized. + gDatareportingService.observe(null, "app-startup", null); + gDatareportingService.observe(null, "profile-after-change", null); + Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true); Services.prefs.setCharPref(TelemetryPing.Constants.PREF_SERVER, "http://localhost:" + gHttpServer.identity.primaryPort); From 9d4ef5423881aaa380ac67200d8731c19dbc248f Mon Sep 17 00:00:00 2001 From: Georg Fritzsche Date: Thu, 16 Oct 2014 20:11:34 +0200 Subject: [PATCH 08/81] Bug 1064333 - Only add the stable user id to the ping when FHR upload is enabled. r=froydnj --- .../components/telemetry/TelemetryPing.jsm | 23 +++++++++++++++---- .../tests/unit/test_TelemetryPing.js | 17 ++++++++++---- .../tests/unit/test_TelemetryPingBuildID.js | 6 +++-- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryPing.jsm b/toolkit/components/telemetry/TelemetryPing.jsm index 68d2db28da68..6a13db165ac3 100644 --- a/toolkit/components/telemetry/TelemetryPing.jsm +++ b/toolkit/components/telemetry/TelemetryPing.jsm @@ -32,6 +32,7 @@ const PREF_BRANCH = "toolkit.telemetry."; const PREF_SERVER = PREF_BRANCH + "server"; const PREF_ENABLED = PREF_BRANCH + "enabled"; const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID"; +const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; // Do not gather data more than once a minute const TELEMETRY_INTERVAL = 60000; @@ -703,7 +704,6 @@ let Impl = { UIMeasurements: UITelemetry.getUIMeasurements(), log: TelemetryLog.entries(), info: info, - clientID: this._clientID, }; if (Object.keys(this._slowSQLStartup).length != 0 && @@ -712,6 +712,17 @@ let Impl = { payloadObj.slowSQLStartup = this._slowSQLStartup; } + let fhrUploadEnabled = false; + try { + fhrUploadEnabled = Services.prefs.getBoolPref(PREF_FHR_UPLOAD_ENABLED); + } catch (e) { + // Pref not set. + } + + if (this._clientID && fhrUploadEnabled) { + payloadObj.clientID = this._clientID; + } + return payloadObj; }, @@ -959,10 +970,12 @@ let Impl = { this.attachObservers(); this.gatherMemory(); - let drs = Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject; - this._clientID = yield drs.getClientID(); + if ("@mozilla.org/datareporting/service;1" in Cc) { + let drs = Cc["@mozilla.org/datareporting/service;1"] + .getService(Ci.nsISupports) + .wrappedJSObject; + this._clientID = yield drs.getClientID(); + } Telemetry.asyncFetchTelemetryData(function () {}); delete this._timer; diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 30036a82beb9..59ed7accd5a4 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -43,6 +43,7 @@ let gNumberOfThreadsLaunched = 0; const PREF_BRANCH = "toolkit.telemetry."; const PREF_ENABLED = PREF_BRANCH + "enabled"; +const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); @@ -171,9 +172,12 @@ function checkPayloadInfo(payload, reason) { do_check_true(payload.info.revision.startsWith("http")); } - do_check_true("clientID" in payload); - do_check_neq(payload.clientID, null); - do_check_eq(payload.clientID, gDataReportingClientID); + if ("@mozilla.org/datareporting/service;1" in Cc && + Services.prefs.getBoolPref(PREF_FHR_UPLOAD_ENABLED)) { + do_check_true("clientID" in payload); + do_check_neq(payload.clientID, null); + do_check_eq(payload.clientID, gDataReportingClientID); + } try { // If we've not got nsIGfxInfoDebug, then this will throw and stop us doing @@ -393,11 +397,14 @@ function run_test() { createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); Services.prefs.setBoolPref(PREF_ENABLED, true); + Services.prefs.setBoolPref(PREF_FHR_UPLOAD_ENABLED, true); // Send the needed startup notifications to the datareporting service // to ensure that it has been initialized. - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); + if ("@mozilla.org/datareporting/service;1" in Cc) { + gDatareportingService.observe(null, "app-startup", null); + gDatareportingService.observe(null, "profile-after-change", null); + } // Make it look like we've previously failed to lock a profile a couple times. write_fake_failedprofilelocks_file(); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js index f418f050c674..4e234d417042 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js @@ -74,8 +74,10 @@ function run_test() { // Send the needed startup notifications to the datareporting service // to ensure that it has been initialized. - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); + if ("@mozilla.org/datareporting/service;1" in Cc) { + gDatareportingService.observe(null, "app-startup", null); + gDatareportingService.observe(null, "profile-after-change", null); + } run_next_test(); } From 727ad7822b1944d8e352b838757189cd22894ce7 Mon Sep 17 00:00:00 2001 From: Georg Fritzsche Date: Thu, 16 Oct 2014 20:31:22 +0200 Subject: [PATCH 09/81] Bug 1064333 - Fix test oversight. r=test-only --- toolkit/components/telemetry/tests/unit/test_TelemetryPing.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 59ed7accd5a4..23a608545f78 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -455,7 +455,9 @@ function actualTest() { add_task(function* asyncSetup() { yield TelemetryPing.setup(); - gDataReportingClientID = yield gDatareportingService.getClientID(); + if ("@mozilla.org/datareporting/service;1" in Cc) { + gDataReportingClientID = yield gDatareportingService.getClientID(); + } }); // Ensure that not overwriting an existing file fails silently From 95c05d4b6819378867eaad1af45853ef7869d03e Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 16 Oct 2014 14:38:29 -0400 Subject: [PATCH 10/81] Backed out changeset d16adf321576 (bug 1077301) for B2G bustage. CLOSED TREE --- gfx/layers/Compositor.h | 15 + gfx/layers/composite/CompositableHost.cpp | 36 +- gfx/layers/composite/CompositableHost.h | 50 +++ gfx/layers/composite/ContentHost.cpp | 271 +++------------ gfx/layers/composite/ContentHost.h | 46 +-- gfx/layers/composite/ImageHost.cpp | 59 ++-- gfx/layers/composite/ImageHost.h | 5 +- gfx/layers/composite/TextureHost.cpp | 31 +- gfx/layers/composite/TextureHost.h | 84 +---- gfx/layers/opengl/GrallocTextureHost.cpp | 400 +++++++++++----------- gfx/layers/opengl/GrallocTextureHost.h | 41 +-- gfx/layers/opengl/TextureHostOGL.cpp | 231 ++++++++++--- gfx/layers/opengl/TextureHostOGL.h | 128 +++++-- 13 files changed, 717 insertions(+), 680 deletions(-) diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h index 868d67e695af..035bae513080 100644 --- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -135,6 +135,17 @@ enum SurfaceInitMode INIT_MODE_CLEAR }; +/** + * A base class for a platform-dependent helper for use by TextureHost. + */ +class CompositorBackendSpecificData +{ + NS_INLINE_DECL_REFCOUNTING(CompositorBackendSpecificData) + +protected: + virtual ~CompositorBackendSpecificData() {} +}; + /** * Common interface for compositor backends. * @@ -470,6 +481,10 @@ public: return fillRatio; } + virtual CompositorBackendSpecificData* GetCompositorBackendSpecificData() { + return nullptr; + } + ScreenRotation GetScreenRotation() const { return mScreenRotation; } diff --git a/gfx/layers/composite/CompositableHost.cpp b/gfx/layers/composite/CompositableHost.cpp index 645e0f813cb6..2bbbd78d82ad 100644 --- a/gfx/layers/composite/CompositableHost.cpp +++ b/gfx/layers/composite/CompositableHost.cpp @@ -24,6 +24,14 @@ namespace layers { class Compositor; +CompositableBackendSpecificData::CompositableBackendSpecificData() + : mAllowSharingTextureHost(false) +{ + static uint64_t sNextID = 1; + ++sNextID; + mId = sNextID; +} + /** * IPDL actor used by CompositableHost to match with its corresponding * CompositableClient on the content side. @@ -79,6 +87,9 @@ CompositableHost::CompositableHost(const TextureInfo& aTextureInfo) CompositableHost::~CompositableHost() { MOZ_COUNT_DTOR(CompositableHost); + if (mBackendData) { + mBackendData->ClearData(); + } } PCompositableParent* @@ -110,6 +121,7 @@ CompositableHost::UseTextureHost(TextureHost* aTexture) return; } aTexture->SetCompositor(GetCompositor()); + aTexture->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); } void @@ -118,12 +130,17 @@ CompositableHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack, { MOZ_ASSERT(aTextureOnBlack && aTextureOnWhite); aTextureOnBlack->SetCompositor(GetCompositor()); + aTextureOnBlack->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); aTextureOnWhite->SetCompositor(GetCompositor()); + aTextureOnWhite->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); } void CompositableHost::RemoveTextureHost(TextureHost* aTexture) -{} +{ + // Clear strong refrence to CompositableBackendSpecificData + aTexture->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); +} void CompositableHost::SetCompositor(Compositor* aCompositor) @@ -136,7 +153,7 @@ CompositableHost::AddMaskEffect(EffectChain& aEffects, const gfx::Matrix4x4& aTransform, bool aIs3D) { - CompositableTextureSourceRef source; + RefPtr source; RefPtr host = GetAsTextureHost(); if (!host) { @@ -149,12 +166,14 @@ CompositableHost::AddMaskEffect(EffectChain& aEffects, return false; } - if (!host->BindTextureSource(source)) { + source = host->GetTextureSources(); + MOZ_ASSERT(source); + + if (!source) { NS_WARNING("The TextureHost was successfully locked but can't provide a TextureSource"); host->Unlock(); return false; } - MOZ_ASSERT(source); RefPtr effect = new EffectMask(source, source->GetSize(), @@ -173,6 +192,9 @@ CompositableHost::RemoveMaskEffect() } } +// implemented in TextureHostOGL.cpp +TemporaryRef CreateCompositableBackendSpecificDataOGL(); + /* static */ TemporaryRef CompositableHost::Create(const TextureInfo& aTextureInfo) { @@ -205,6 +227,12 @@ CompositableHost::Create(const TextureInfo& aTextureInfo) default: NS_ERROR("Unknown CompositableType"); } + // We know that Tiled buffers don't use the compositable backend-specific + // data, so don't bother creating it. + if (result && aTextureInfo.mCompositableType != CompositableType::BUFFER_TILED) { + RefPtr data = CreateCompositableBackendSpecificDataOGL(); + result->SetCompositableBackendSpecificData(data); + } return result; } diff --git a/gfx/layers/composite/CompositableHost.h b/gfx/layers/composite/CompositableHost.h index 0c6543ad51b1..4a2862813ef2 100644 --- a/gfx/layers/composite/CompositableHost.h +++ b/gfx/layers/composite/CompositableHost.h @@ -49,6 +49,42 @@ class CompositableParentManager; class PCompositableParent; struct EffectChain; +/** + * A base class for doing CompositableHost and platform dependent task on TextureHost. + */ +class CompositableBackendSpecificData +{ +protected: + virtual ~CompositableBackendSpecificData() {} + +public: + NS_INLINE_DECL_REFCOUNTING(CompositableBackendSpecificData) + + CompositableBackendSpecificData(); + + virtual void ClearData() {} + virtual void SetCompositor(Compositor* aCompositor) {} + + bool IsAllowingSharingTextureHost() + { + return mAllowSharingTextureHost; + } + + void SetAllowSharingTextureHost(bool aAllow) + { + mAllowSharingTextureHost = aAllow; + } + + uint64_t GetId() + { + return mId; + } + +public: + bool mAllowSharingTextureHost; + uint64_t mId; +}; + /** * The compositor-side counterpart to CompositableClient. Responsible for * updating textures and data about textures from IPC and how textures are @@ -76,6 +112,16 @@ public: virtual CompositableType GetType() = 0; + virtual CompositableBackendSpecificData* GetCompositableBackendSpecificData() + { + return mBackendData; + } + + virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) + { + mBackendData = aBackendData; + } + // If base class overrides, it should still call the parent implementation virtual void SetCompositor(Compositor* aCompositor); @@ -206,6 +252,9 @@ public: SetLayer(nullptr); mAttached = false; mKeepAttached = false; + if (mBackendData) { + mBackendData->ClearData(); + } } } bool IsAttached() { return mAttached; } @@ -265,6 +314,7 @@ protected: uint64_t mCompositorID; RefPtr mCompositor; Layer* mLayer; + RefPtr mBackendData; uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true. bool mAttached; bool mKeepAttached; diff --git a/gfx/layers/composite/ContentHost.cpp b/gfx/layers/composite/ContentHost.cpp index d537bd15beb7..09b0a0550b8a 100644 --- a/gfx/layers/composite/ContentHost.cpp +++ b/gfx/layers/composite/ContentHost.cpp @@ -35,12 +35,12 @@ ContentHostBase::~ContentHostBase() } void -ContentHostTexture::Composite(EffectChain& aEffectChain, - float aOpacity, - const gfx::Matrix4x4& aTransform, - const Filter& aFilter, - const Rect& aClipRect, - const nsIntRegion* aVisibleRegion) +ContentHostBase::Composite(EffectChain& aEffectChain, + float aOpacity, + const gfx::Matrix4x4& aTransform, + const Filter& aFilter, + const Rect& aClipRect, + const nsIntRegion* aVisibleRegion) { NS_ASSERTION(aVisibleRegion, "Requires a visible region"); @@ -49,18 +49,14 @@ ContentHostTexture::Composite(EffectChain& aEffectChain, return; } - if (!mTextureHost->BindTextureSource(mTextureSource)) { - return; - } - MOZ_ASSERT(mTextureSource.get()); + RefPtr source = GetTextureSource(); + RefPtr sourceOnWhite = GetTextureSourceOnWhite(); - if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) { + if (!source) { return; } - RefPtr effect = CreateTexturedEffect(mTextureSource.get(), - mTextureSourceOnWhite.get(), - aFilter, true); + RefPtr effect = GenEffect(aFilter); if (!effect) { return; } @@ -85,7 +81,7 @@ ContentHostTexture::Composite(EffectChain& aEffectChain, region.MoveBy(-origin); // Figure out the intersecting draw region - gfx::IntSize texSize = mTextureSource->GetSize(); + gfx::IntSize texSize = source->GetSize(); nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height); textureRect.MoveBy(region.GetBounds().TopLeft()); nsIntRegion subregion; @@ -109,14 +105,14 @@ ContentHostTexture::Composite(EffectChain& aEffectChain, regionRects.Or(regionRects, regionRect); } - BigImageIterator* bigImgIter = mTextureSource->AsBigImageIterator(); + BigImageIterator* bigImgIter = source->AsBigImageIterator(); BigImageIterator* iterOnWhite = nullptr; if (bigImgIter) { bigImgIter->BeginBigImageIteration(); } - if (mTextureSourceOnWhite) { - iterOnWhite = mTextureSourceOnWhite->AsBigImageIterator(); + if (sourceOnWhite) { + iterOnWhite = sourceOnWhite->AsBigImageIterator(); MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(), "Tile count mismatch on component alpha texture"); if (iterOnWhite) { @@ -209,39 +205,32 @@ ContentHostTexture::Composite(EffectChain& aEffectChain, aTransform, mFlashCounter); } +TemporaryRef +ContentHostBase::GenEffect(const gfx::Filter& aFilter) +{ + RefPtr source = GetTextureSource(); + RefPtr sourceOnWhite = GetTextureSourceOnWhite(); + if (!source) { + return nullptr; + } + return CreateTexturedEffect(source, sourceOnWhite, aFilter, true); +} + void ContentHostTexture::UseTextureHost(TextureHost* aTexture) { - if (mTextureHost && mTextureHost != aTexture) { - mTextureHost->UnbindTextureSource(); - } ContentHostBase::UseTextureHost(aTexture); mTextureHost = aTexture; mTextureHostOnWhite = nullptr; - if (mTextureHost) { - mTextureHost->PrepareTextureSource(mTextureSource); - } } void ContentHostTexture::UseComponentAlphaTextures(TextureHost* aTextureOnBlack, TextureHost* aTextureOnWhite) { - if (mTextureHost && mTextureHost != aTextureOnBlack) { - mTextureHost->UnbindTextureSource(); - } - if (mTextureHostOnWhite && mTextureHostOnWhite != aTextureOnWhite) { - mTextureHostOnWhite->UnbindTextureSource(); - } ContentHostBase::UseComponentAlphaTextures(aTextureOnBlack, aTextureOnWhite); mTextureHost = aTextureOnBlack; mTextureHostOnWhite = aTextureOnWhite; - if (mTextureHost) { - mTextureHost->PrepareTextureSource(mTextureSource); - } - if (mTextureHostOnWhite) { - mTextureHost->PrepareTextureSource(mTextureSourceOnWhite); - } } void @@ -428,176 +417,6 @@ ContentHostIncremental::UpdateIncremental(TextureIdentifier aTextureId, FlushUpdateQueue(); } -void -ContentHostIncremental::Composite(EffectChain& aEffectChain, - float aOpacity, - const gfx::Matrix4x4& aTransform, - const Filter& aFilter, - const Rect& aClipRect, - const nsIntRegion* aVisibleRegion) -{ - NS_ASSERTION(aVisibleRegion, "Requires a visible region"); - - AutoLockCompositableHost lock(this); - if (lock.Failed()) { - return; - } - - if (!mSource) { - return; - } - - RefPtr effect = CreateTexturedEffect(mSource.get(), - mSourceOnWhite.get(), - aFilter, true); - if (!effect) { - return; - } - - aEffectChain.mPrimaryEffect = effect; - - nsIntRegion tmpRegion; - const nsIntRegion* renderRegion; - if (PaintWillResample()) { - // If we're resampling, then the texture image will contain exactly the - // entire visible region's bounds, and we should draw it all in one quad - // to avoid unexpected aliasing. - tmpRegion = aVisibleRegion->GetBounds(); - renderRegion = &tmpRegion; - } else { - renderRegion = aVisibleRegion; - } - - nsIntRegion region(*renderRegion); - nsIntPoint origin = GetOriginOffset(); - // translate into TexImage space, buffer origin might not be at texture (0,0) - region.MoveBy(-origin); - - // Figure out the intersecting draw region - gfx::IntSize texSize = mSource->GetSize(); - nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height); - textureRect.MoveBy(region.GetBounds().TopLeft()); - nsIntRegion subregion; - subregion.And(region, textureRect); - if (subregion.IsEmpty()) { - // Region is empty, nothing to draw - return; - } - - nsIntRegion screenRects; - nsIntRegion regionRects; - - // Collect texture/screen coordinates for drawing - nsIntRegionRectIterator iter(subregion); - while (const nsIntRect* iterRect = iter.Next()) { - nsIntRect regionRect = *iterRect; - nsIntRect screenRect = regionRect; - screenRect.MoveBy(origin); - - screenRects.Or(screenRects, screenRect); - regionRects.Or(regionRects, regionRect); - } - - BigImageIterator* bigImgIter = mSource->AsBigImageIterator(); - BigImageIterator* iterOnWhite = nullptr; - if (bigImgIter) { - bigImgIter->BeginBigImageIteration(); - } - - if (mSourceOnWhite) { - iterOnWhite = mSourceOnWhite->AsBigImageIterator(); - MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(), - "Tile count mismatch on component alpha texture"); - if (iterOnWhite) { - iterOnWhite->BeginBigImageIteration(); - } - } - - bool usingTiles = (bigImgIter && bigImgIter->GetTileCount() > 1); - do { - if (iterOnWhite) { - MOZ_ASSERT(iterOnWhite->GetTileRect() == bigImgIter->GetTileRect(), - "component alpha textures should be the same size."); - } - - nsIntRect texRect = bigImgIter ? bigImgIter->GetTileRect() - : nsIntRect(0, 0, - texSize.width, - texSize.height); - - // Draw texture. If we're using tiles, we do repeating manually, as texture - // repeat would cause each individual tile to repeat instead of the - // compound texture as a whole. This involves drawing at most 4 sections, - // 2 for each axis that has texture repeat. - for (int y = 0; y < (usingTiles ? 2 : 1); y++) { - for (int x = 0; x < (usingTiles ? 2 : 1); x++) { - nsIntRect currentTileRect(texRect); - currentTileRect.MoveBy(x * texSize.width, y * texSize.height); - - nsIntRegionRectIterator screenIter(screenRects); - nsIntRegionRectIterator regionIter(regionRects); - - const nsIntRect* screenRect; - const nsIntRect* regionRect; - while ((screenRect = screenIter.Next()) && - (regionRect = regionIter.Next())) { - nsIntRect tileScreenRect(*screenRect); - nsIntRect tileRegionRect(*regionRect); - - // When we're using tiles, find the intersection between the tile - // rect and this region rect. Tiling is then handled by the - // outer for-loops and modifying the tile rect. - if (usingTiles) { - tileScreenRect.MoveBy(-origin); - tileScreenRect = tileScreenRect.Intersect(currentTileRect); - tileScreenRect.MoveBy(origin); - - if (tileScreenRect.IsEmpty()) - continue; - - tileRegionRect = regionRect->Intersect(currentTileRect); - tileRegionRect.MoveBy(-currentTileRect.TopLeft()); - } - gfx::Rect rect(tileScreenRect.x, tileScreenRect.y, - tileScreenRect.width, tileScreenRect.height); - - effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width, - Float(tileRegionRect.y) / texRect.height, - Float(tileRegionRect.width) / texRect.width, - Float(tileRegionRect.height) / texRect.height); - GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform); - if (usingTiles) { - DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE; - if (iterOnWhite) { - diagnostics |= DiagnosticFlags::COMPONENT_ALPHA; - } - GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect, - aTransform, mFlashCounter); - } - } - } - } - - if (iterOnWhite) { - iterOnWhite->NextTile(); - } - } while (usingTiles && bigImgIter->NextTile()); - - if (bigImgIter) { - bigImgIter->EndBigImageIteration(); - } - if (iterOnWhite) { - iterOnWhite->EndBigImageIteration(); - } - - DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT; - if (iterOnWhite) { - diagnostics |= DiagnosticFlags::COMPONENT_ALPHA; - } - GetCompositor()->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect, - aTransform, mFlashCounter); -} - void ContentHostIncremental::FlushUpdateQueue() { @@ -620,6 +439,20 @@ ContentHostIncremental::ProcessTextureUpdates() mUpdateList.Clear(); } +TextureSource* +ContentHostIncremental::GetTextureSource() +{ + MOZ_ASSERT(mLocked); + return mSource; +} + +TextureSource* +ContentHostIncremental::GetTextureSourceOnWhite() +{ + MOZ_ASSERT(mLocked); + return mSourceOnWhite; +} + void ContentHostIncremental::TextureCreationRequest::Execute(ContentHostIncremental* aHost) { @@ -850,32 +683,6 @@ ContentHostTexture::GetRenderState() return result; } -TemporaryRef -ContentHostTexture::GenEffect(const gfx::Filter& aFilter) -{ - if (!mTextureHost) { - return nullptr; - } - if (!mTextureHost->BindTextureSource(mTextureSource)) { - return nullptr; - } - if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) { - return nullptr; - } - return CreateTexturedEffect(mTextureSource.get(), - mTextureSourceOnWhite.get(), - aFilter, true); -} - -TemporaryRef -ContentHostIncremental::GenEffect(const gfx::Filter& aFilter) -{ - if (!mSource) { - return nullptr; - } - return CreateTexturedEffect(mSource, mSourceOnWhite, aFilter, true); -} - #ifdef MOZ_DUMP_PAINTING TemporaryRef ContentHostTexture::GetAsSurface() diff --git a/gfx/layers/composite/ContentHost.h b/gfx/layers/composite/ContentHost.h index e7676d798c39..b445287ef20c 100644 --- a/gfx/layers/composite/ContentHost.h +++ b/gfx/layers/composite/ContentHost.h @@ -96,6 +96,18 @@ public: explicit ContentHostBase(const TextureInfo& aTextureInfo); virtual ~ContentHostBase(); + virtual void Composite(EffectChain& aEffectChain, + float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Filter& aFilter, + const gfx::Rect& aClipRect, + const nsIntRegion* aVisibleRegion = nullptr); + + virtual TextureSource* GetTextureSource() = 0; + virtual TextureSource* GetTextureSourceOnWhite() = 0; + + virtual TemporaryRef GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE; + protected: virtual nsIntPoint GetOriginOffset() { @@ -120,13 +132,6 @@ public: , mLocked(false) { } - virtual void Composite(EffectChain& aEffectChain, - float aOpacity, - const gfx::Matrix4x4& aTransform, - const gfx::Filter& aFilter, - const gfx::Rect& aClipRect, - const nsIntRegion* aVisibleRegion = nullptr); - virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE; #ifdef MOZ_DUMP_PAINTING @@ -168,15 +173,23 @@ public: mLocked = false; } - LayerRenderState GetRenderState(); + virtual TextureSource* GetTextureSource() MOZ_OVERRIDE { + MOZ_ASSERT(mLocked); + return mTextureHost->GetTextureSources(); + } + virtual TextureSource* GetTextureSourceOnWhite() MOZ_OVERRIDE { + MOZ_ASSERT(mLocked); + if (mTextureHostOnWhite) { + return mTextureHostOnWhite->GetTextureSources(); + } + return nullptr; + } - virtual TemporaryRef GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE; + LayerRenderState GetRenderState(); protected: RefPtr mTextureHost; RefPtr mTextureHostOnWhite; - CompositableTextureSourceRef mTextureSource; - CompositableTextureSourceRef mTextureSourceOnWhite; bool mLocked; }; @@ -264,13 +277,6 @@ public: return false; } - virtual void Composite(EffectChain& aEffectChain, - float aOpacity, - const gfx::Matrix4x4& aTransform, - const gfx::Filter& aFilter, - const gfx::Rect& aClipRect, - const nsIntRegion* aVisibleRegion = nullptr); - virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) MOZ_OVERRIDE; virtual bool Lock() MOZ_OVERRIDE { @@ -285,8 +291,8 @@ public: mLocked = false; } - virtual TemporaryRef - GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE; + virtual TextureSource* GetTextureSource() MOZ_OVERRIDE; + virtual TextureSource* GetTextureSourceOnWhite() MOZ_OVERRIDE; private: diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index 3a07fcf677db..6fd06a4d8a0b 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -37,21 +37,28 @@ ImageHost::ImageHost(const TextureInfo& aTextureInfo) ImageHost::~ImageHost() { if (mFrontBuffer) { - mFrontBuffer->UnbindTextureSource(); + mFrontBuffer->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); + } +} + +void +ImageHost::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) +{ + CompositableHost::SetCompositableBackendSpecificData(aBackendData); + // ImageHost allows TextureHost sharing among ImageHosts. + if (aBackendData) { + aBackendData->SetAllowSharingTextureHost(true); } } void ImageHost::UseTextureHost(TextureHost* aTexture) { - if (mFrontBuffer && mFrontBuffer != aTexture) { - mFrontBuffer->UnbindTextureSource(); - } CompositableHost::UseTextureHost(aTexture); - mFrontBuffer = aTexture; if (mFrontBuffer) { - mFrontBuffer->PrepareTextureSource(mTextureSource); + mFrontBuffer->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); } + mFrontBuffer = aTexture; } void @@ -59,8 +66,7 @@ ImageHost::RemoveTextureHost(TextureHost* aTexture) { CompositableHost::RemoveTextureHost(aTexture); if (aTexture && mFrontBuffer == aTexture) { - mFrontBuffer->UnbindTextureSource(); - mTextureSource = nullptr; + aTexture->SetCompositableBackendSpecificData(nullptr); mFrontBuffer = nullptr; } } @@ -91,34 +97,25 @@ ImageHost::Composite(EffectChain& aEffectChain, // Make sure the front buffer has a compositor mFrontBuffer->SetCompositor(GetCompositor()); + mFrontBuffer->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData()); AutoLockCompositableHost autoLock(this); if (autoLock.Failed()) { NS_WARNING("failed to lock front buffer"); return; } - - if (!mFrontBuffer->BindTextureSource(mTextureSource)) { + RefPtr source = GetTextureSource(); + if (!source) { return; } - if (!mTextureSource) { - // BindTextureSource above should have returned false! - MOZ_ASSERT(false); - return; - } - - bool isAlphaPremultiplied = !(mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED); - RefPtr effect = CreateTexturedEffect(mFrontBuffer->GetFormat(), - mTextureSource.get(), - aFilter, - isAlphaPremultiplied); + RefPtr effect = GenEffect(aFilter); if (!effect) { return; } aEffectChain.mPrimaryEffect = effect; - IntSize textureSize = mTextureSource->GetSize(); + IntSize textureSize = source->GetSize(); gfx::Rect gfxPictureRect = mHasPictureRect ? gfx::Rect(0, 0, mPictureRect.width, mPictureRect.height) : gfx::Rect(0, 0, textureSize.width, textureSize.height); @@ -126,7 +123,7 @@ ImageHost::Composite(EffectChain& aEffectChain, gfx::Rect pictureRect(0, 0, mPictureRect.width, mPictureRect.height); - BigImageIterator* it = mTextureSource->AsBigImageIterator(); + BigImageIterator* it = source->AsBigImageIterator(); if (it) { // This iteration does not work if we have multiple texture sources here @@ -142,7 +139,7 @@ ImageHost::Composite(EffectChain& aEffectChain, // the corresponding source tiles from all planes, with appropriate // per-plane per-tile texture coords. // DrawQuad currently assumes that all planes use the same texture coords. - MOZ_ASSERT(it->GetTileCount() == 1 || !mTextureSource->GetNextSibling(), + MOZ_ASSERT(it->GetTileCount() == 1 || !source->GetNextSibling(), "Can't handle multi-plane BigImages"); it->BeginBigImageIteration(); @@ -173,7 +170,7 @@ ImageHost::Composite(EffectChain& aEffectChain, gfxPictureRect, aClipRect, aTransform, mFlashCounter); } else { - IntSize textureSize = mTextureSource->GetSize(); + IntSize textureSize = source->GetSize(); gfx::Rect rect; if (mHasPictureRect) { effect->mTextureCoords = Rect(Float(mPictureRect.x) / textureSize.width, @@ -276,10 +273,18 @@ ImageHost::Unlock() mLocked = false; } +TemporaryRef +ImageHost::GetTextureSource() +{ + MOZ_ASSERT(mLocked); + return mFrontBuffer->GetTextureSources(); +} + TemporaryRef ImageHost::GenEffect(const gfx::Filter& aFilter) { - if (!mFrontBuffer->BindTextureSource(mTextureSource)) { + RefPtr source = GetTextureSource(); + if (!source) { return nullptr; } bool isAlphaPremultiplied = true; @@ -287,7 +292,7 @@ ImageHost::GenEffect(const gfx::Filter& aFilter) isAlphaPremultiplied = false; return CreateTexturedEffect(mFrontBuffer->GetFormat(), - mTextureSource, + source, aFilter, isAlphaPremultiplied); } diff --git a/gfx/layers/composite/ImageHost.h b/gfx/layers/composite/ImageHost.h index e7524ebf7d20..ad12d229f049 100644 --- a/gfx/layers/composite/ImageHost.h +++ b/gfx/layers/composite/ImageHost.h @@ -45,6 +45,8 @@ public: virtual CompositableType GetType() { return mTextureInfo.mCompositableType; } + virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE; + virtual void Composite(EffectChain& aEffectChain, float aOpacity, const gfx::Matrix4x4& aTransform, @@ -82,12 +84,13 @@ public: virtual void Unlock() MOZ_OVERRIDE; + virtual TemporaryRef GetTextureSource(); + virtual TemporaryRef GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE; protected: RefPtr mFrontBuffer; - CompositableTextureSourceRef mTextureSource; nsIntRect mPictureRect; bool mHasPictureRect; bool mLocked; diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index e22dfd8aceb5..1397c69f6c4e 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -144,13 +144,6 @@ TextureHost::GetIPDLActor() return mActor; } -bool -TextureHost::BindTextureSource(CompositableTextureSourceRef& texture) -{ - texture = GetTextureSources(); - return !!texture; -} - FenceHandle TextureHost::GetAndResetReleaseFenceHandle() { @@ -284,6 +277,18 @@ TextureHost::CompositorRecycle() static_cast(mActor)->CompositorRecycle(); } +void +TextureHost::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) +{ + mCompositableBackendData = aBackendData; +} + +void +TextureHost::UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) +{ + mCompositableBackendData = nullptr; +} + TextureHost::TextureHost(TextureFlags aFlags) : mActor(nullptr) , mFlags(aFlags) @@ -317,11 +322,9 @@ TextureHost::PrintInfo(std::stringstream& aStream, const char* aPrefix) } TextureSource::TextureSource() -: mCompositableCount(0) { MOZ_COUNT_CTOR(TextureSource); } - TextureSource::~TextureSource() { MOZ_COUNT_DTOR(TextureSource); @@ -840,9 +843,8 @@ SharedSurfaceToTexSource(gl::SharedSurface* abstractSurf, Compositor* compositor GLenum target = surf->ConsTextureTarget(); GLuint tex = surf->ConsTexture(gl); - texSource = new GLTextureSource(compositorOGL, tex, target, - surf->mSize, format, - true/*externally owned*/); + texSource = new GLTextureSource(compositorOGL, tex, format, target, + surf->mSize); break; } case gl::SharedSurfaceType::EGLImageShare: { @@ -857,9 +859,8 @@ SharedSurfaceToTexSource(gl::SharedSurface* abstractSurf, Compositor* compositor GLuint tex = 0; surf->AcquireConsumerTexture(gl, &tex, &target); - texSource = new GLTextureSource(compositorOGL, tex, target, - surf->mSize, format, - true/*externally owned*/); + texSource = new GLTextureSource(compositorOGL, tex, format, target, + surf->mSize); break; } #ifdef XP_MACOSX diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h index f44fd1c467fa..b6e00bf4c861 100644 --- a/gfx/layers/composite/TextureHost.h +++ b/gfx/layers/composite/TextureHost.h @@ -46,6 +46,7 @@ namespace layers { class Compositor; class CompositableHost; +class CompositableBackendSpecificData; class CompositableParentManager; class SurfaceDescriptor; class SharedSurfaceDescriptor; @@ -149,69 +150,10 @@ public: return nullptr; } - void AddCompositableRef() { ++mCompositableCount; } - - void ReleaseCompositableRef() { - --mCompositableCount; - MOZ_ASSERT(mCompositableCount >= 0); - } - - int NumCompositableRefs() const { return mCompositableCount; } - protected: virtual ~TextureSource(); RefPtr mNextSibling; - int mCompositableCount; -}; - -/** - * equivalent of a RefPtr, that calls AddCompositableRef and - * ReleaseCompositableRef in addition to the usual AddRef and Release. - */ -class CompositableTextureSourceRef { -public: - CompositableTextureSourceRef() {} - - ~CompositableTextureSourceRef() - { - if (mRef) { - mRef->ReleaseCompositableRef(); - } - } - - CompositableTextureSourceRef& operator=(const TemporaryRef& aOther) - { - RefPtr temp = aOther; - if (temp) { - temp->AddCompositableRef(); - } - if (mRef) { - mRef->ReleaseCompositableRef(); - } - mRef = temp; - return *this; - } - - CompositableTextureSourceRef& operator=(TextureSource* aOther) - { - if (aOther) { - aOther->AddCompositableRef(); - } - if (mRef) { - mRef->ReleaseCompositableRef(); - } - mRef = aOther; - return *this; - } - - TextureSource* get() const { return mRef; } - operator TextureSource*() const { return mRef; } - TextureSource* operator->() const { return mRef; } - TextureSource& operator*() const { return *mRef; } - -private: - RefPtr mRef; }; /** @@ -360,25 +302,6 @@ public: */ virtual TextureSource* GetTextureSources() = 0; - /** - * Called during the transaction. The TextureSource may or may not be composited. - * - * Note that this is called outside of lock/unlock. - */ - virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {} - - /** - * Called at composition time, just before compositing the TextureSource composited. - * - * Note that this is called only withing lock/unlock. - */ - virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture); - - /** - * Called when another TextureHost will take over. - */ - virtual void UnbindTextureSource() {} - /** * Is called before compositing if the shared data has changed since last * composition. @@ -483,6 +406,10 @@ public: return LayerRenderState(); } + virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData); + + virtual void UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData); + // If a texture host holds a reference to shmem, it should override this method // to forget about the shmem _without_ releasing it. virtual void OnShutdown() {} @@ -508,6 +435,7 @@ public: protected: PTextureParent* mActor; TextureFlags mFlags; + RefPtr mCompositableBackendData; friend class TextureParent; }; diff --git a/gfx/layers/opengl/GrallocTextureHost.cpp b/gfx/layers/opengl/GrallocTextureHost.cpp index 3e6871bc5886..a4b87ca385fe 100644 --- a/gfx/layers/opengl/GrallocTextureHost.cpp +++ b/gfx/layers/opengl/GrallocTextureHost.cpp @@ -130,6 +130,17 @@ GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) gl()->fActiveTexture(aTextureUnit); gl()->fBindTexture(textureTarget, tex); + if (mTextureBackendSpecificData) { + // There are two paths for locking/unlocking - if mTextureBackendSpecificData is + // set, we use the texture on there, otherwise we use + // CompositorBackendSpecificData from the compositor and bind the EGLImage + // only in Lock(). + if (!mEGLImage) { + mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); + } + BindEGLImage(); + } + ApplyFilterToBoundTexture(gl(), aFilter, textureTarget); #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 @@ -142,6 +153,10 @@ GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) bool GrallocTextureSourceOGL::Lock() { + if (mTextureBackendSpecificData) { + return true; + } + MOZ_ASSERT(IsValid()); if (!IsValid()) { return false; @@ -167,7 +182,7 @@ bool GrallocTextureSourceOGL::Lock() bool GrallocTextureSourceOGL::IsValid() const { - return !!gl() && !!mGraphicBuffer.get() && !!mCompositor; + return !!gl() && !!mGraphicBuffer.get() && (!!mCompositor || !!mTextureBackendSpecificData); } gl::GLContext* @@ -209,6 +224,62 @@ GrallocTextureSourceOGL::GetTextureTarget() const return TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat()); } +void +GrallocTextureSourceOGL::SetTextureBackendSpecificData(TextureSharedDataGonkOGL* aBackendData) +{ + if (!aBackendData) { + DeallocateDeviceData(); + // Update mTextureBackendSpecificData after calling DeallocateDeviceData(). + mTextureBackendSpecificData = nullptr; + return; + } + + if (mTextureBackendSpecificData != aBackendData) { + mNeedsReset = true; + } + + if (!gl() || !gl()->MakeCurrent()) { + NS_WARNING("Failed to make the context current"); + return; + } + + if (!mNeedsReset) { + // Update binding to the EGLImage + GLuint tex = GetGLTexture(); + GLuint textureTarget = GetTextureTarget(); + gl()->fActiveTexture(LOCAL_GL_TEXTURE0); + gl()->fBindTexture(textureTarget, tex); + BindEGLImage(); + return; + } + + if (!mCompositor) { + mTextureBackendSpecificData = aBackendData; + return; + } + + // delete old EGLImage + DeallocateDeviceData(); + + // Update mTextureBackendSpecificData after calling DeallocateDeviceData(). + mTextureBackendSpecificData = aBackendData; + + GLuint tex = GetGLTexture(); + GLuint textureTarget = GetTextureTarget(); + + gl()->fActiveTexture(LOCAL_GL_TEXTURE0); + gl()->fBindTexture(textureTarget, tex); + + // Setup texure parameters at the first binding. + gl()->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, GetWrapMode()); + gl()->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, GetWrapMode()); + + // create new EGLImage + mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); + BindEGLImage(); + mNeedsReset = false; +} + gfx::IntSize GrallocTextureSourceOGL::GetSize() const { @@ -227,6 +298,9 @@ GrallocTextureSourceOGL::DeallocateDeviceData() if (!gl() || !gl()->MakeCurrent()) { return; } + if (mTextureBackendSpecificData) { + mTextureBackendSpecificData->ClearBoundEGLImage(mEGLImage); + } EGLImageDestroy(gl(), mEGLImage); mEGLImage = EGL_NO_IMAGE; } @@ -235,48 +309,49 @@ GrallocTextureSourceOGL::DeallocateDeviceData() GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags, const NewSurfaceDescriptorGralloc& aDescriptor) : TextureHost(aFlags) - , mGrallocHandle(aDescriptor) - , mSize(0, 0) - , mDescriptorSize(aDescriptor.size()) - , mFormat(gfx::SurfaceFormat::UNKNOWN) - , mEGLImage(EGL_NO_IMAGE) { + gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN; + mGrallocHandle = aDescriptor; + android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); MOZ_ASSERT(graphicBuffer); + mSize = aDescriptor.size(); if (graphicBuffer) { - mFormat = + format = SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(), aFlags & TextureFlags::RB_SWAPPED); - mSize = gfx::IntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight()); + mTextureSource = new GrallocTextureSourceOGL(nullptr, + this, + graphicBuffer, + format); } else { printf_stderr("gralloc buffer is nullptr"); } } GrallocTextureHostOGL::~GrallocTextureHostOGL() -{} +{ + MOZ_ASSERT(!mTextureSource || (mFlags & TextureFlags::DEALLOCATE_CLIENT), + "Leaking our buffer"); +} void GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor) { - mCompositor = static_cast(aCompositor); - if (mTilingTextureSource) { - mTilingTextureSource->SetCompositor(mCompositor); - } - if (mGLTextureSource) { - mGLTextureSource->SetCompositor(mCompositor); - } - - if (mCompositor && aCompositor != mCompositor) { - DestroyEGLImage(); + if (mTextureSource) { + mTextureSource->SetCompositor(static_cast(aCompositor)); } } bool GrallocTextureHostOGL::Lock() { - return IsValid(); + if (IsValid()) { + mTextureSource->Lock(); + return true; + } + return false; } void @@ -288,29 +363,28 @@ GrallocTextureHostOGL::Unlock() bool GrallocTextureHostOGL::IsValid() const { - android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); - return graphicBuffer != nullptr; + if (!mTextureSource) { + return false; + } + return mTextureSource->IsValid(); } gfx::SurfaceFormat GrallocTextureHostOGL::GetFormat() const { - return mFormat; + if (!mTextureSource) { + return gfx::SurfaceFormat::UNKNOWN; + } + return mTextureSource->GetFormat(); } void GrallocTextureHostOGL::DeallocateSharedData() { - if (mTilingTextureSource) { - mTilingTextureSource->ForgetBuffer(); - mTilingTextureSource = nullptr; + if (mTextureSource) { + mTextureSource->ForgetBuffer(); + mTextureSource = nullptr; } - if (mGLTextureSource) { - mGLTextureSource = nullptr; - } - - DestroyEGLImage(); - if (mGrallocHandle.buffer().type() != SurfaceDescriptor::Tnull_t) { MaybeMagicGrallocBufferHandle handle = mGrallocHandle.buffer(); base::ProcessId owner; @@ -328,33 +402,24 @@ GrallocTextureHostOGL::DeallocateSharedData() void GrallocTextureHostOGL::ForgetSharedData() { - if (mTilingTextureSource) { - mTilingTextureSource->ForgetBuffer(); - mTilingTextureSource = nullptr; - } - if (mGLTextureSource) { - mGLTextureSource = nullptr; + if (mTextureSource) { + mTextureSource->ForgetBuffer(); + mTextureSource = nullptr; } } void GrallocTextureHostOGL::DeallocateDeviceData() { - if (mTilingTextureSource) { - mTilingTextureSource->DeallocateDeviceData(); + if (mTextureSource) { + mTextureSource->DeallocateDeviceData(); } - if (mGLTextureSource) { - mGLTextureSource = nullptr; - } - DestroyEGLImage(); } LayerRenderState GrallocTextureHostOGL::GetRenderState() { - android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); - - if (graphicBuffer) { + if (IsValid()) { LayerRenderStateFlags flags = LayerRenderStateFlags::LAYER_RENDER_STATE_DEFAULT; if (mFlags & TextureFlags::NEEDS_Y_FLIP) { flags |= LayerRenderStateFlags::Y_FLIPPED; @@ -362,8 +427,8 @@ GrallocTextureHostOGL::GetRenderState() if (mFlags & TextureFlags::RB_SWAPPED) { flags |= LayerRenderStateFlags::FORMAT_RB_SWAP; } - return LayerRenderState(graphicBuffer, - gfx::ThebesIntSize(mDescriptorSize), + return LayerRenderState(mTextureSource->mGraphicBuffer.get(), + gfx::ThebesIntSize(mSize), flags, this); } @@ -373,8 +438,8 @@ GrallocTextureHostOGL::GetRenderState() TemporaryRef GrallocTextureHostOGL::GetAsSurface() { - return mTilingTextureSource ? mTilingTextureSource->GetAsSurface() - : nullptr; + return mTextureSource ? mTextureSource->GetAsSurface() + : nullptr; } TemporaryRef @@ -402,186 +467,103 @@ GrallocTextureSourceOGL::GetAsSurface() { GLuint GrallocTextureSourceOGL::GetGLTexture() { + if (mTextureBackendSpecificData) { + mTextureBackendSpecificData->SetCompositor(mCompositor); + return mTextureBackendSpecificData->GetTexture(); + } + return mTexture; } void GrallocTextureSourceOGL::BindEGLImage() { - gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage); + if (mTextureBackendSpecificData) { + mTextureBackendSpecificData->BindEGLImage(GetTextureTarget(), mEGLImage); + } else { + gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage); + } } -TextureSource* -GrallocTextureHostOGL::GetTextureSources() +void +GrallocTextureHostOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) { - // This is now only used with tiled layers, and will eventually be removed. - // Other layer types use BindTextureSource instead. - MOZ_ASSERT(!mGLTextureSource); - if (!mTilingTextureSource) { - android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); - MOZ_ASSERT(graphicBuffer); - if (!graphicBuffer) { - return nullptr; + if(!aBackendData) { + return; + } + + // Update mTextureBackendSpecificData if it is not set yet. + if (!mTextureBackendSpecificData) { + MOZ_ASSERT(!mCompositableBackendData); + mCompositableBackendData = aBackendData; + CompositableDataGonkOGL* backend = static_cast(mCompositableBackendData.get()); + mTextureBackendSpecificData = backend->GetTextureBackendSpecificData(); + } + + // If TextureHost sharing by multiple CompositableHosts are detected, + // enable mBackendDatas usage. + if (!mBackendDatas && + mCompositableBackendData && + mCompositableBackendData != aBackendData && + mTextureBackendSpecificData->IsAllowingSharingTextureHost()) + { + mBackendDatas = MakeUnique > >(); + (*mBackendDatas)[mCompositableBackendData->GetId()] = mCompositableBackendData; + mCompositableBackendData = nullptr; + + // Get new mTextureBackendSpecificData + mTextureBackendSpecificData = + mTextureBackendSpecificData->GetNewTextureBackendSpecificData(mTextureSource->GetEGLImage()); + mTextureBackendSpecificData->SetOwnedByTextureHost(); + } + + // Update mCompositableBackendData. + if (mBackendDatas) + { + // Handle a case that TextureHost has ownership of TextureSharedDataGonkOGL. + MOZ_ASSERT(aBackendData->IsAllowingSharingTextureHost()); + (*mBackendDatas)[aBackendData->GetId()] = aBackendData; + if (mBackendDatas->size() > 200) { + NS_WARNING("Too many CompositableBackends"); } - mTilingTextureSource = new GrallocTextureSourceOGL(mCompositor, this, - graphicBuffer, mFormat); - } - mTilingTextureSource->Lock(); - return mTilingTextureSource; -} - -void -GrallocTextureHostOGL::UnbindTextureSource() -{ - // Clear the reference to the TextureSource (if any), because we know that - // another TextureHost is being bound to the TextureSource. This means that - // we will have to re-do gl->fEGLImageTargetTexture2D next time we go through - // BindTextureSource (otherwise we would have skipped it). - // Note that this doesn't "unlock" the gralloc buffer or force it to be - // detached, Although decreasing the refcount of the TextureSource may lead - // to the gl handle being destroyed, which would unlock the gralloc buffer. - // That said, this method is called before another TextureHost attaches to the - // TextureSource, which has the effect of unlocking the gralloc buffer. So when - // this is called we know we are going to be unlocked soon. - mGLTextureSource = nullptr; -} - -GLenum GetTextureTarget(gl::GLContext* aGL, android::PixelFormat aFormat) { - MOZ_ASSERT(aGL); - if (aGL->Renderer() == gl::GLRenderer::SGX530 || - aGL->Renderer() == gl::GLRenderer::SGX540) { - // SGX has a quirk that only TEXTURE_EXTERNAL works and any other value will - // result in black pixels when trying to draw from bound textures. - // Unfortunately, using TEXTURE_EXTERNAL on Adreno has a terrible effect on - // performance. - // See Bug 950050. - return LOCAL_GL_TEXTURE_EXTERNAL; } else { - return TextureTargetForAndroidPixelFormat(aFormat); + // Handle a case that CompositableHost has ownership of TextureSharedDataGonkOGL. + mCompositableBackendData = aBackendData; + CompositableDataGonkOGL* backend = static_cast(mCompositableBackendData.get()); + mTextureBackendSpecificData = backend->GetTextureBackendSpecificData(); } + + if (mTextureSource) { + mTextureSource->SetTextureBackendSpecificData(mTextureBackendSpecificData); + } + } void -GrallocTextureHostOGL::DestroyEGLImage() +GrallocTextureHostOGL::UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) { - // Only called when we want to get rid of the gralloc buffer, usually - // around the end of life of the TextureHost. - if (mEGLImage != EGL_NO_IMAGE && GetGLContext()) { - EGLImageDestroy(GetGLContext(), mEGLImage); - mEGLImage = EGL_NO_IMAGE; - } -} - -void -GrallocTextureHostOGL::PrepareTextureSource(CompositableTextureSourceRef& aTextureSource) -{ - // This happens during the layers transaction. - // All of the gralloc magic goes here. The only thing that happens externally - // and that is good to keep in mind is that when the TextureSource is deleted, - // it destroys its gl texture handle which is important for genlock. - - // If this TextureHost's mGLTextureSource member is non-null, it means we are - // still bound to the TextureSource, in which case we can skip the driver - // overhead of binding the texture again (fEGLImageTargetTexture2D) - // As a result, if the TextureHost is used with several CompositableHosts, - // it will be bound to only one TextureSource, and we'll do the driver work - // only once, which is great. This means that all of the compositables that - // use this TextureHost will keep a reference to this TextureSource at least - // for the duration of this frame. - - // If the compositable already has a TextureSource (the aTextureSource parameter), - // that is compatible and is not in use by several compositable, we try to - // attach to it. This has the effect of unlocking the previous TextureHost that - // we attached to the TextureSource (the previous frame) - - // If the TextureSource used by the compositable is also used by other - // compositables (see NumCompositableRefs), we have to create a new TextureSource, - // because otherwise we would be modifying the content of every layer that uses - // the TextureSource in question, even thoug they don't use this TextureHost. - - MOZ_ASSERT(!mTilingTextureSource); - - android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); - - MOZ_ASSERT(graphicBuffer); - if (!graphicBuffer) { - mGLTextureSource = nullptr; + if(!aBackendData || + !mTextureBackendSpecificData) { return; } - if (mGLTextureSource && !mGLTextureSource->IsValid()) { - mGLTextureSource = nullptr; - } - - if (mGLTextureSource) { - // We are already attached to a TextureSource, nothing to do except tell - // the compositable to use it. - aTextureSource = mGLTextureSource.get(); - return; - } - - gl::GLContext* gl = GetGLContext(); - if (!gl || !gl->MakeCurrent()) { - mGLTextureSource = nullptr; - return; - } - - if (mEGLImage == EGL_NO_IMAGE) { - // Should only happen the first time. - mEGLImage = EGLImageCreateFromNativeBuffer(gl, graphicBuffer->getNativeBuffer()); - } - - GLenum textureTarget = GetTextureTarget(gl, graphicBuffer->getPixelFormat()); - - GLTextureSource* glSource = aTextureSource.get() ? - aTextureSource->AsSourceOGL()->AsGLTextureSource() : nullptr; - - bool shouldCreateTextureSource = !glSource || !glSource->IsValid() - || glSource->NumCompositableRefs() > 1 - || glSource->GetTextureTarget() != textureTarget; - - if (shouldCreateTextureSource) { - GLuint textureHandle; - gl->fGenTextures(1, &textureHandle); - gl->fBindTexture(textureTarget, textureHandle); - gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); - gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); - gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage); - - mGLTextureSource = new GLTextureSource(mCompositor, textureHandle, textureTarget, - mSize, mFormat); - aTextureSource = mGLTextureSource.get(); + if (mBackendDatas) + { + // Handle a case that TextureHost has ownership of TextureSharedDataGonkOGL. + mBackendDatas->erase(aBackendData->GetId()); + if (mBackendDatas->size() == 0) { + mCompositableBackendData = nullptr; + mTextureBackendSpecificData = nullptr; + } } else { - gl->fBindTexture(textureTarget, glSource->GetTextureHandle()); - - gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage); - glSource->SetSize(mSize); - glSource->SetFormat(mFormat); - mGLTextureSource = glSource; - } -} - -bool -GrallocTextureHostOGL::BindTextureSource(CompositableTextureSourceRef& aTextureSource) -{ - // This happens at composition time. - - // If mGLTextureSource is null it means PrepareTextureSource failed. - if (!mGLTextureSource) { - return false; + // Handle a case that CompositableHost has ownership of TextureSharedDataGonkOGL. + mCompositableBackendData = nullptr; + mTextureBackendSpecificData = nullptr; } - // If Prepare didn't fail, we expect our TextureSource to be the same as aTextureSource, - // otherwise it means something has fiddled with the TextureSource between Prepare and - // now. - MOZ_ASSERT(mGLTextureSource == aTextureSource); - aTextureSource = mGLTextureSource; - -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 - // Wait until it's ready. - WaitAcquireFenceSyncComplete(); -#endif - return true; + if (mTextureSource) { + mTextureSource->SetTextureBackendSpecificData(mTextureBackendSpecificData); + } } } // namepsace layers diff --git a/gfx/layers/opengl/GrallocTextureHost.h b/gfx/layers/opengl/GrallocTextureHost.h index aad2799b74e0..55500c6ec06a 100644 --- a/gfx/layers/opengl/GrallocTextureHost.h +++ b/gfx/layers/opengl/GrallocTextureHost.h @@ -17,7 +17,6 @@ namespace layers { class GrallocTextureHostOGL; -// Progressively getting replaced by GLTextureSource class GrallocTextureSourceOGL : public TextureSource , public TextureSourceOGL { @@ -48,6 +47,8 @@ public: return LOCAL_GL_CLAMP_TO_EDGE; } + virtual void SetTextureBackendSpecificData(TextureSharedDataGonkOGL* aBackendData); + void DeallocateDeviceData(); gl::GLContext* gl() const; @@ -74,6 +75,7 @@ public: bool Lock(); protected: + RefPtr mTextureBackendSpecificData; RefPtr mCompositor; GrallocTextureHostOGL* mTextureHost; android::sp mGraphicBuffer; @@ -111,17 +113,14 @@ public: virtual gfx::SurfaceFormat GetFormat() const; - virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mDescriptorSize; } + virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; } virtual LayerRenderState GetRenderState() MOZ_OVERRIDE; - virtual void PrepareTextureSource(CompositableTextureSourceRef& aTextureSource) MOZ_OVERRIDE; - - virtual bool BindTextureSource(CompositableTextureSourceRef& aTextureSource) MOZ_OVERRIDE; - - virtual void UnbindTextureSource() MOZ_OVERRIDE; - - virtual TextureSource* GetTextureSources() MOZ_OVERRIDE; + virtual TextureSource* GetTextureSources() MOZ_OVERRIDE + { + return mTextureSource; + } #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 virtual TextureHostOGL* AsHostOGL() MOZ_OVERRIDE @@ -132,27 +131,21 @@ public: virtual TemporaryRef GetAsSurface() MOZ_OVERRIDE; + virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE; + + virtual void UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE; + bool IsValid() const; virtual const char* Name() MOZ_OVERRIDE { return "GrallocTextureHostOGL"; } - gl::GLContext* GetGLContext() const { return mCompositor ? mCompositor->gl() : nullptr; } - private: - void DestroyEGLImage(); - NewSurfaceDescriptorGralloc mGrallocHandle; - RefPtr mGLTextureSource; - RefPtr mCompositor; - // only used for tiling, will be removed. - RefPtr mTilingTextureSource; - // Size reported by the GraphicBuffer - gfx::IntSize mSize; - // Size reported by TextureClient, can be different in some cases (video?), - // used by LayerRenderState. - gfx::IntSize mDescriptorSize; - gfx::SurfaceFormat mFormat; - EGLImage mEGLImage; + RefPtr mTextureSource; + gfx::IntSize mSize; // See comment in textureClientOGL.h + + RefPtr mTextureBackendSpecificData; + UniquePtr > > mBackendDatas; }; } // namespace layers diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp index 4fa926f1891f..0094d3f0107a 100644 --- a/gfx/layers/opengl/TextureHostOGL.cpp +++ b/gfx/layers/opengl/TextureHostOGL.cpp @@ -40,6 +40,16 @@ namespace layers { class Compositor; +TemporaryRef +CreateCompositableBackendSpecificDataOGL() +{ +#ifdef MOZ_WIDGET_GONK + return new CompositableDataGonkOGL(); +#else + return nullptr; +#endif +} + TemporaryRef CreateTextureHostOGL(const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator, @@ -109,6 +119,174 @@ FlagsToGLFlags(TextureFlags aFlags) return static_cast(result); } +CompositableDataGonkOGL::CompositableDataGonkOGL() +{ +} + +CompositableDataGonkOGL::~CompositableDataGonkOGL() +{ + ClearData(); +} + +void +CompositableDataGonkOGL::ClearData() +{ + CompositableBackendSpecificData::ClearData(); + mTextureBackendSpecificData = nullptr; + mCompositor = nullptr; +} + +void +CompositableDataGonkOGL::SetCompositor(Compositor* aCompositor) +{ + mCompositor = static_cast(aCompositor); + if (mTextureBackendSpecificData) { + mTextureBackendSpecificData->SetCompositor(aCompositor); + } +} + +TextureSharedDataGonkOGL* +CompositableDataGonkOGL::GetTextureBackendSpecificData() +{ + if (!mTextureBackendSpecificData) { + mTextureBackendSpecificData = new TextureSharedDataGonkOGL(); + mTextureBackendSpecificData->SetCompositor(mCompositor); + mTextureBackendSpecificData->SetAllowSharingTextureHost(IsAllowingSharingTextureHost()); + } + return mTextureBackendSpecificData; +} + +TextureSharedDataGonkOGL::TextureSharedDataGonkOGL() + : mOwnedByCompositableHost(true) + , mAllowSharingTextureHost(false) + , mTexture(0) + , mBoundEGLImage(EGL_NO_IMAGE) +{ +} + +TextureSharedDataGonkOGL::TextureSharedDataGonkOGL(GLuint aTexture, EGLImage aImage, CompositorOGL* aCompositor) + : mOwnedByCompositableHost(true) + , mAllowSharingTextureHost(false) + , mCompositor(aCompositor) + , mTexture(aTexture) + , mBoundEGLImage(aImage) +{ +} + +TextureSharedDataGonkOGL::~TextureSharedDataGonkOGL() +{ + DeleteTextureIfPresent(); +} + +gl::GLContext* +TextureSharedDataGonkOGL::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +void +TextureSharedDataGonkOGL::SetCompositor(Compositor* aCompositor) +{ + if (gl() && mCompositor != aCompositor) { + DeleteTextureIfPresent(); + } + mCompositor = static_cast(aCompositor); +} + +void +TextureSharedDataGonkOGL::ClearData() +{ + DeleteTextureIfPresent(); +} + +TemporaryRef +TextureSharedDataGonkOGL::GetNewTextureBackendSpecificData(EGLImage aImage) +{ + MOZ_ASSERT(IsAllowingSharingTextureHost()); + + if (IsEGLImageBound(aImage)) + { + // If EGLImage is already bound to OpenGL Texture, + // handover the OpenGL Texture to caller + GLuint textureId = GetAndResetGLTextureOwnership(); + RefPtr data = new TextureSharedDataGonkOGL(textureId, aImage, mCompositor); + data->SetCompositor(mCompositor); + data->SetAllowSharingTextureHost(true); + return data; + } + + // Create brand new TextureSharedDataGonkOGL + RefPtr data = new TextureSharedDataGonkOGL(); + data->SetCompositor(mCompositor); + data->SetAllowSharingTextureHost(true); + return data; +} + +GLuint +TextureSharedDataGonkOGL::GetTexture() +{ + if (!mTexture) { + if (gl() && gl()->MakeCurrent()) { + gl()->fGenTextures(1, &mTexture); + } + } + return mTexture; +} + +GLuint +TextureSharedDataGonkOGL::GetAndResetGLTextureOwnership() +{ + GLuint texture = mTexture; + mTexture = 0; + mBoundEGLImage = EGL_NO_IMAGE; + return texture; +} + +void +TextureSharedDataGonkOGL::DeleteTextureIfPresent() +{ + if (mTexture) { + MOZ_ASSERT(mCompositor); + if (gl() && gl()->MakeCurrent()) { + gl()->fDeleteTextures(1, &mTexture); + } + mTexture = 0; + mBoundEGLImage = EGL_NO_IMAGE; + } +} + +void +TextureSharedDataGonkOGL::BindEGLImage(GLuint aTarget, EGLImage aImage) +{ + if (mBoundEGLImage != aImage) { + MOZ_ASSERT(gl()); + if (gl()) { + gl()->fEGLImageTargetTexture2D(aTarget, aImage); + } + mBoundEGLImage = aImage; + } +} + +void +TextureSharedDataGonkOGL::ClearBoundEGLImage(EGLImage aImage) +{ + if (mBoundEGLImage == aImage) { + DeleteTextureIfPresent(); + mBoundEGLImage = EGL_NO_IMAGE; + } +} + +bool +TextureSharedDataGonkOGL::IsEGLImageBound(EGLImage aImage) +{ + if (mTexture != 0 && + aImage != EGL_NO_IMAGE && + aImage == mBoundEGLImage) { + return true; + } + return false; +} + #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 bool TextureHostOGL::SetReleaseFence(const android::sp& aReleaseFence) @@ -351,56 +529,27 @@ TextureImageTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilt // GLTextureSource GLTextureSource::GLTextureSource(CompositorOGL* aCompositor, - GLuint aTextureHandle, - GLenum aTarget, - gfx::IntSize aSize, + GLuint aTex, gfx::SurfaceFormat aFormat, - bool aExternallyOwned) - : mCompositor(aCompositor) - , mTextureHandle(aTextureHandle) - , mTextureTarget(aTarget) - , mSize(aSize) + GLenum aTarget, + gfx::IntSize aSize) + : mSize(aSize) + , mCompositor(aCompositor) + , mTex(aTex) , mFormat(aFormat) - , mExternallyOwned(aExternallyOwned) + , mTextureTarget(aTarget) { - MOZ_COUNT_CTOR(GLTextureSource); -} - -GLTextureSource::~GLTextureSource() -{ - MOZ_COUNT_DTOR(GLTextureSource); - if (!mExternallyOwned) { - DeleteTextureHandle(); - } -} - -void -GLTextureSource::DeallocateDeviceData() -{ - if (!mExternallyOwned) { - DeleteTextureHandle(); - } -} - -void -GLTextureSource::DeleteTextureHandle() -{ - if (mTextureHandle != 0 && gl() && gl()->MakeCurrent()) { - gl()->fDeleteTextures(1, &mTextureHandle); - } - mTextureHandle = 0; } void GLTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) { - MOZ_ASSERT(gl()); - MOZ_ASSERT(mTextureHandle != 0); if (!gl()) { + NS_WARNING("Trying to bind a texture without a GLContext"); return; } gl()->fActiveTexture(aTextureUnit); - gl()->fBindTexture(mTextureTarget, mTextureHandle); + gl()->fBindTexture(mTextureTarget, mTex); ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget); } @@ -413,7 +562,7 @@ GLTextureSource::SetCompositor(Compositor* aCompositor) bool GLTextureSource::IsValid() const { - return !!gl() && mTextureHandle != 0; + return !!gl(); } gl::GLContext* @@ -467,10 +616,6 @@ SurfaceTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) void SurfaceTextureSource::SetCompositor(Compositor* aCompositor) { - if (mCompositor != aCompositor) { - DeallocateDeviceData(); - } - mCompositor = static_cast(aCompositor); } diff --git a/gfx/layers/opengl/TextureHostOGL.h b/gfx/layers/opengl/TextureHostOGL.h index 1b9eb9a24246..7dc3eef0b7b0 100644 --- a/gfx/layers/opengl/TextureHostOGL.h +++ b/gfx/layers/opengl/TextureHostOGL.h @@ -55,7 +55,99 @@ class Compositor; class CompositorOGL; class TextureImageTextureSourceOGL; class TextureSharedDataGonkOGL; -class GLTextureSource; + +/** + * CompositableBackendSpecificData implementation for the Gonk OpenGL backend. + * Share a same texture between TextureHosts in the same CompositableHost. + * By shareing the texture among the TextureHosts, number of texture allocations + * can be reduced than texture allocation in every TextureHosts. + * From Bug 912134, use only one texture among all TextureHosts degrade + * the rendering performance. + * CompositableDataGonkOGL chooses in a middile of them. + */ +class CompositableDataGonkOGL : public CompositableBackendSpecificData +{ +protected: + virtual ~CompositableDataGonkOGL(); + +public: + CompositableDataGonkOGL(); + virtual void ClearData() MOZ_OVERRIDE; + virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE; + + TextureSharedDataGonkOGL* GetTextureBackendSpecificData(); +protected: + nsRefPtr mTextureBackendSpecificData; + RefPtr mCompositor; +}; + +/** + * Manage actual shared resources of CompositableDataGonkOGL. + * The resources are split from CompositableDataGonkOGL to handle two use cases. + * Normally TextureHost is used from one CompositableHost at the same time. + * In this case, performance is good if the resources are owned by CompositableDataGonkOGL. + * But TextureHost could be shared among multiple ImageHosts. + * If it happens, performance is good if the resource is owned by TextureHost. + * The resources ownership is carryed over from CompositableDataGonkOGL to TextureHost. + * See Bug 1017351. + */ +class TextureSharedDataGonkOGL +{ +protected: + virtual ~TextureSharedDataGonkOGL(); + +public: + NS_INLINE_DECL_REFCOUNTING(TextureSharedDataGonkOGL) + + TextureSharedDataGonkOGL(); + TextureSharedDataGonkOGL(GLuint aTexture, EGLImage aImage, CompositorOGL* aCompositor); + + void SetCompositor(Compositor* aCompositor); + void ClearData(); + + // Mark TextureSharedDataGonkOGL as owned by TextureHost. + void SetOwnedByTextureHost() + { + mOwnedByCompositableHost = false; + } + + // Check if this is owned by CompositableHost or TextureHost. + bool IsOwnedByCompositableHost() + { + return mOwnedByCompositableHost; + } + + bool IsAllowingSharingTextureHost() + { + return mAllowSharingTextureHost; + } + + void SetAllowSharingTextureHost(bool aAllow) + { + mAllowSharingTextureHost = aAllow; + } + + // Create new TextureSharedDataGonkOGL. + // If aImage is already bound to OpenGL texture, the OpenGL textre is carried over + // to a new object. It could reduce calling fEGLImageTargetTexture2D() + // during resources ownership carry over from CompositableHost to TextureHost. + TemporaryRef GetNewTextureBackendSpecificData(EGLImage aImage); + + GLuint GetTexture(); + void DeleteTextureIfPresent(); + gl::GLContext* gl() const; + void BindEGLImage(GLuint aTarget, EGLImage aImage); + void ClearBoundEGLImage(EGLImage aImage); + bool IsEGLImageBound(EGLImage aImage); +protected: + GLuint GetAndResetGLTextureOwnership(); + + bool mOwnedByCompositableHost; + bool mAllowSharingTextureHost; + RefPtr mCompositor; + GLuint mTexture; + EGLImage mBoundEGLImage; +}; inline void ApplyFilterToBoundTexture(gl::GLContext* aGL, gfx::Filter aFilter, @@ -110,8 +202,6 @@ public: virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() { return nullptr; } - virtual GLTextureSource* AsGLTextureSource() { return nullptr; } - void SetFilter(gl::GLContext* aGL, gfx::Filter aFilter) { if (mHasCachedFilter && @@ -280,15 +370,10 @@ class GLTextureSource : public TextureSource { public: GLTextureSource(CompositorOGL* aCompositor, - GLuint aTextureHandle, - GLenum aTarget, - gfx::IntSize aSize, + GLuint aTex, gfx::SurfaceFormat aFormat, - bool aExternallyOwned = false); - - ~GLTextureSource(); - - virtual GLTextureSource* AsGLTextureSource() MOZ_OVERRIDE { return this; } + GLenum aTarget, + gfx::IntSize aSize); virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; } @@ -304,29 +389,18 @@ public: virtual GLenum GetWrapMode() const MOZ_OVERRIDE { return LOCAL_GL_CLAMP_TO_EDGE; } - virtual void DeallocateDeviceData() MOZ_OVERRIDE; + virtual void DeallocateDeviceData() MOZ_OVERRIDE {} virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE; - void SetSize(gfx::IntSize aSize) { mSize = aSize; } - - void SetFormat(gfx::SurfaceFormat aFormat) { mFormat = aFormat; } - - GLuint GetTextureHandle() const { return mTextureHandle; } - gl::GLContext* gl() const; protected: - void DeleteTextureHandle(); - + const gfx::IntSize mSize; RefPtr mCompositor; - GLuint mTextureHandle; - GLenum mTextureTarget; - gfx::IntSize mSize; - gfx::SurfaceFormat mFormat; - // If the texture is externally owned, the gl handle will not be deleted - // in the destructor. - bool mExternallyOwned; + const GLuint mTex; + const gfx::SurfaceFormat mFormat; + const GLenum mTextureTarget; }; //////////////////////////////////////////////////////////////////////// From 78df988ba7adef4901c1d1738379d0194f618c3e Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:14:05 -0700 Subject: [PATCH 11/81] Bug 1063197 - Storing LoadInfo on all channels created through NS_NewInputStreamChannel - API changes (r=mcmanus) --- netwerk/base/public/nsNetUtil.h | 204 +++++++++++++++++++------------- 1 file changed, 124 insertions(+), 80 deletions(-) diff --git a/netwerk/base/public/nsNetUtil.h b/netwerk/base/public/nsNetUtil.h index b23f7ce361cc..d2f91af90df6 100644 --- a/netwerk/base/public/nsNetUtil.h +++ b/netwerk/base/public/nsNetUtil.h @@ -597,101 +597,145 @@ NS_GetRealPort(nsIURI* aURI) } inline nsresult -NS_NewInputStreamChannel(nsIChannel **result, - nsIURI *uri, - nsIInputStream *stream, - const nsACString &contentType, - const nsACString *contentCharset) +NS_NewInputStreamChannelInternal(nsIChannel** outChannel, + nsIURI* aUri, + nsIInputStream* aStream, + const nsACString& aContentType, + const nsACString& aContentCharset, + nsINode* aRequestingNode, + nsIPrincipal* aRequestingPrincipal, + nsSecurityFlags aSecurityFlags, + nsContentPolicyType aContentPolicyType) { - nsresult rv; - nsCOMPtr isc = - do_CreateInstance(NS_INPUTSTREAMCHANNEL_CONTRACTID, &rv); - if (NS_FAILED(rv)) - return rv; - rv = isc->SetURI(uri); - nsresult tmp = isc->SetContentStream(stream); - if (NS_FAILED(tmp)) { - rv = tmp; - } - if (NS_FAILED(rv)) - return rv; - nsCOMPtr chan = do_QueryInterface(isc, &rv); - if (NS_FAILED(rv)) - return rv; - if (!contentType.IsEmpty()) - rv = chan->SetContentType(contentType); - if (contentCharset && !contentCharset->IsEmpty()) { - tmp = chan->SetContentCharset(*contentCharset); - if (NS_FAILED(tmp)) { - rv = tmp; - } - } - if (NS_SUCCEEDED(rv)) { - *result = nullptr; - chan.swap(*result); - } - return rv; -} + nsresult rv; + nsCOMPtr isc = + do_CreateInstance(NS_INPUTSTREAMCHANNEL_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = isc->SetURI(aUri); + NS_ENSURE_SUCCESS(rv, rv); + rv = isc->SetContentStream(aStream); + NS_ENSURE_SUCCESS(rv, rv); -inline nsresult -NS_NewInputStreamChannel(nsIChannel **result, - nsIURI *uri, - nsIInputStream *stream, - const nsACString &contentType = EmptyCString()) -{ - return NS_NewInputStreamChannel(result, uri, stream, contentType, nullptr); -} + nsCOMPtr channel = do_QueryInterface(isc, &rv); + NS_ENSURE_SUCCESS(rv, rv); -inline nsresult -NS_NewInputStreamChannel(nsIChannel **result, - nsIURI *uri, - nsIInputStream *stream, - const nsACString &contentType, - const nsACString &contentCharset) -{ - return NS_NewInputStreamChannel(result, uri, stream, contentType, - &contentCharset); -} - -inline nsresult -NS_NewInputStreamChannel(nsIChannel **result, - nsIURI *uri, - const nsAString &data, - const nsACString &contentType, - bool isSrcdocChannel = false) -{ - - nsresult rv; - - nsCOMPtr stream; - stream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv); + if (!aContentType.IsEmpty()) { + rv = channel->SetContentType(aContentType); NS_ENSURE_SUCCESS(rv, rv); + } + + if (!aContentCharset.IsEmpty()) { + rv = channel->SetContentCharset(aContentCharset); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr loadInfo = + new mozilla::LoadInfo(aRequestingPrincipal, + aRequestingNode, + aSecurityFlags, + aContentPolicyType); + if (!loadInfo) { + return NS_ERROR_UNEXPECTED; + } + channel->SetLoadInfo(loadInfo); + + // If we're sandboxed, make sure to clear any owner the channel + // might already have. + if (loadInfo->GetLoadingSandboxed()) { + channel->SetOwner(nullptr); + } + + channel.forget(outChannel); + return NS_OK; +} + +inline nsresult /* NS_NewInputStreamChannelPrincipal */ +NS_NewInputStreamChannel(nsIChannel** outChannel, + nsIURI* aUri, + nsIInputStream* aStream, + nsIPrincipal* aRequestingPrincipal, + nsSecurityFlags aSecurityFlags, + nsContentPolicyType aContentPolicyType, + const nsACString& aContentType = EmptyCString(), + const nsACString& aContentCharset = EmptyCString()) +{ + return NS_NewInputStreamChannelInternal(outChannel, + aUri, + aStream, + aContentType, + aContentCharset, + nullptr, // aRequestingNode + aRequestingPrincipal, + aSecurityFlags, + aContentPolicyType); +} + +inline nsresult +NS_NewInputStreamChannelInternal(nsIChannel** outChannel, + nsIURI* aUri, + const nsAString& aData, + const nsACString& aContentType, + nsINode* aRequestingNode, + nsIPrincipal* aRequestingPrincipal, + nsSecurityFlags aSecurityFlags, + nsContentPolicyType aContentPolicyType, + bool aIsSrcdocChannel = false) +{ + nsresult rv; + nsCOMPtr stream; + stream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); #ifdef MOZILLA_INTERNAL_API uint32_t len; - char* utf8Bytes = ToNewUTF8String(data, &len); + char* utf8Bytes = ToNewUTF8String(aData, &len); rv = stream->AdoptData(utf8Bytes, len); #else - char* utf8Bytes = ToNewUTF8String(data); + char* utf8Bytes = ToNewUTF8String(aData); rv = stream->AdoptData(utf8Bytes, strlen(utf8Bytes)); #endif - nsCOMPtr chan; + nsCOMPtr channel; + rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), + aUri, + stream, + aContentType, + NS_LITERAL_CSTRING("UTF-8"), + aRequestingNode, + aRequestingPrincipal, + aSecurityFlags, + aContentPolicyType); - rv = NS_NewInputStreamChannel(getter_AddRefs(chan), uri, stream, - contentType, NS_LITERAL_CSTRING("UTF-8")); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(rv, rv); - if (isSrcdocChannel) { - nsCOMPtr inStrmChan = do_QueryInterface(chan); - NS_ENSURE_TRUE(inStrmChan, NS_ERROR_FAILURE); - inStrmChan->SetSrcdocData(data); - } + if (aIsSrcdocChannel) { + nsCOMPtr inStrmChan = do_QueryInterface(channel); + NS_ENSURE_TRUE(inStrmChan, NS_ERROR_FAILURE); + inStrmChan->SetSrcdocData(aData); + } + channel.forget(outChannel); + return NS_OK; +} - *result = nullptr; - chan.swap(*result); - - return NS_OK; +inline nsresult +NS_NewInputStreamChannel(nsIChannel** outChannel, + nsIURI* aUri, + const nsAString& aData, + const nsACString& aContentType, + nsIPrincipal* aRequestingPrincipal, + nsSecurityFlags aSecurityFlags, + nsContentPolicyType aContentPolicyType, + bool aIsSrcdocChannel = false) +{ + return NS_NewInputStreamChannelInternal(outChannel, + aUri, + aData, + aContentType, + nullptr, // aRequestingNode + aRequestingPrincipal, + aSecurityFlags, + aContentPolicyType, + aIsSrcdocChannel); } inline nsresult From 3636869e7d2a3fbc01e7dbe0623a034c229b1672 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:14:35 -0700 Subject: [PATCH 12/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /netwerk/ (r=mcmanus) --- netwerk/protocol/about/nsAboutBlank.cpp | 8 +++++++- netwerk/protocol/about/nsAboutBloat.cpp | 8 +++++++- netwerk/protocol/about/nsAboutCache.cpp | 8 +++++++- netwerk/protocol/about/nsAboutCacheEntry.cpp | 8 +++++++- netwerk/protocol/viewsource/nsViewSourceChannel.cpp | 10 ++++++++-- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/netwerk/protocol/about/nsAboutBlank.cpp b/netwerk/protocol/about/nsAboutBlank.cpp index 95cb51862d81..930585067609 100644 --- a/netwerk/protocol/about/nsAboutBlank.cpp +++ b/netwerk/protocol/about/nsAboutBlank.cpp @@ -7,6 +7,7 @@ #include "nsStringStream.h" #include "nsDOMString.h" #include "nsNetUtil.h" +#include "nsContentUtils.h" NS_IMPL_ISUPPORTS(nsAboutBlank, nsIAboutModule) @@ -20,7 +21,12 @@ nsAboutBlank::NewChannel(nsIURI *aURI, nsIChannel **result) if (NS_FAILED(rv)) return rv; nsCOMPtr channel; - rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, in, + rv = NS_NewInputStreamChannel(getter_AddRefs(channel), + aURI, + in, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, NS_LITERAL_CSTRING("text/html"), NS_LITERAL_CSTRING("utf-8")); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/protocol/about/nsAboutBloat.cpp b/netwerk/protocol/about/nsAboutBloat.cpp index ba1a95d53b1b..65beac5b93c7 100644 --- a/netwerk/protocol/about/nsAboutBloat.cpp +++ b/netwerk/protocol/about/nsAboutBloat.cpp @@ -9,6 +9,7 @@ #ifdef NS_BUILD_REFCNT_LOGGING #include "nsAboutBloat.h" +#include "nsContentUtils.h" #include "nsStringStream.h" #include "nsDOMString.h" #include "nsIURI.h" @@ -109,7 +110,12 @@ nsAboutBloat::NewChannel(nsIURI *aURI, nsIChannel **result) } nsIChannel* channel = nullptr; - rv = NS_NewInputStreamChannel(&channel, aURI, inStr, + rv = NS_NewInputStreamChannel(&channel, + aURI, + inStr, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, NS_LITERAL_CSTRING("text/plain"), NS_LITERAL_CSTRING("utf-8")); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/protocol/about/nsAboutCache.cpp b/netwerk/protocol/about/nsAboutCache.cpp index 75b6bbf9efbc..f2face1f32e4 100644 --- a/netwerk/protocol/about/nsAboutCache.cpp +++ b/netwerk/protocol/about/nsAboutCache.cpp @@ -9,6 +9,7 @@ #include "nsIURI.h" #include "nsCOMPtr.h" #include "nsNetUtil.h" +#include "nsContentUtils.h" #include "nsEscape.h" #include "nsAboutProtocolUtils.h" #include "nsPrintfCString.h" @@ -61,7 +62,12 @@ nsAboutCache::NewChannel(nsIURI *aURI, nsIChannel **result) mEntriesHeaderAdded = false; nsCOMPtr channel; - rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, inputStream, + rv = NS_NewInputStreamChannel(getter_AddRefs(channel), + aURI, + inputStream, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, NS_LITERAL_CSTRING("text/html"), NS_LITERAL_CSTRING("utf-8")); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/protocol/about/nsAboutCacheEntry.cpp b/netwerk/protocol/about/nsAboutCacheEntry.cpp index 74ae0f291579..5f4f2a19d0fd 100644 --- a/netwerk/protocol/about/nsAboutCacheEntry.cpp +++ b/netwerk/protocol/about/nsAboutCacheEntry.cpp @@ -14,6 +14,7 @@ #include "nsIAsyncInputStream.h" #include "nsIAsyncOutputStream.h" #include "nsAboutProtocolUtils.h" +#include "nsContentUtils.h" #include "nsInputStreamPump.h" #include "CacheFileUtils.h" #include @@ -97,7 +98,12 @@ nsAboutCacheEntry::NewChannel(nsIURI *uri, nsIChannel **result) rv = GetContentStream(uri, getter_AddRefs(stream)); if (NS_FAILED(rv)) return rv; - return NS_NewInputStreamChannel(result, uri, stream, + return NS_NewInputStreamChannel(result, + uri, + stream, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, NS_LITERAL_CSTRING("text/html"), NS_LITERAL_CSTRING("utf-8")); } diff --git a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp index ff7166eeecb0..99d6c6c4efb3 100644 --- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp +++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp @@ -8,6 +8,7 @@ #include "nsIIOService.h" #include "nsMimeTypes.h" #include "nsNetUtil.h" +#include "nsContentUtils.h" #include "nsIHttpHeaderVisitor.h" NS_IMPL_ADDREF(nsViewSourceChannel) @@ -85,8 +86,13 @@ nsViewSourceChannel::InitSrcdoc(nsIURI* aURI, const nsAString &aSrcdoc, NS_LITERAL_STRING("about:srcdoc")); NS_ENSURE_SUCCESS(rv, rv); - rv = NS_NewInputStreamChannel(getter_AddRefs(mChannel), inStreamURI, - aSrcdoc, NS_LITERAL_CSTRING("text/html"), + rv = NS_NewInputStreamChannel(getter_AddRefs(mChannel), + inStreamURI, + aSrcdoc, + NS_LITERAL_CSTRING("text/html"), + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, true); NS_ENSURE_SUCCESS(rv, rv); From fdc7306ea1b85f238679a27f1f7c00ff7a6c81b5 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:14:42 -0700 Subject: [PATCH 13/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /content/base (r=jst) --- content/base/src/DOMParser.cpp | 17 +++++++---------- .../base/src/nsHostObjectProtocolHandler.cpp | 12 +++++------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/content/base/src/DOMParser.cpp b/content/base/src/DOMParser.cpp index 04d169424ce4..b9bec3e69b6e 100644 --- a/content/base/src/DOMParser.cpp +++ b/content/base/src/DOMParser.cpp @@ -238,18 +238,15 @@ DOMParser::ParseFromStream(nsIInputStream *stream, // Create a fake channel nsCOMPtr parserChannel; - NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mDocumentURI, nullptr, - nsDependentCString(contentType), nullptr); + NS_NewInputStreamChannel(getter_AddRefs(parserChannel), + mDocumentURI, + nullptr, // aStream + mOriginalPrincipal, + nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, + nsIContentPolicy::TYPE_OTHER, + nsDependentCString(contentType)); NS_ENSURE_STATE(parserChannel); - // More principal-faking here - nsCOMPtr loadInfo = - new LoadInfo(mOriginalPrincipal, - nullptr, - nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, - nsIContentPolicy::TYPE_OTHER); - parserChannel->SetLoadInfo(loadInfo); - if (charset) { parserChannel->SetContentCharset(nsDependentCString(charset)); } diff --git a/content/base/src/nsHostObjectProtocolHandler.cpp b/content/base/src/nsHostObjectProtocolHandler.cpp index bf04e2d533ff..0a7ed3a4661e 100644 --- a/content/base/src/nsHostObjectProtocolHandler.cpp +++ b/content/base/src/nsHostObjectProtocolHandler.cpp @@ -519,7 +519,11 @@ nsHostObjectProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) nsCOMPtr channel; rv = NS_NewInputStreamChannel(getter_AddRefs(channel), uri, - stream); + stream, + info->mPrincipal, + nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, + nsIContentPolicy::TYPE_OTHER); + NS_ENSURE_SUCCESS(rv, rv); nsString type; @@ -537,12 +541,6 @@ nsHostObjectProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) return error.ErrorCode(); } - nsCOMPtr loadInfo = - new mozilla::LoadInfo(info->mPrincipal, - nullptr, - nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, - nsIContentPolicy::TYPE_OTHER); - channel->SetLoadInfo(loadInfo); channel->SetOriginalURI(uri); channel->SetContentType(NS_ConvertUTF16toUTF8(type)); channel->SetContentLength(size); From d92e478a0f461d7f043425b1f69affd51b65f887 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:14:52 -0700 Subject: [PATCH 14/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /dom/ (r=bz) --- dom/json/nsJSON.cpp | 17 ++++++++++++++--- dom/jsurl/nsJSProtocolHandler.cpp | 12 +++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/dom/json/nsJSON.cpp b/dom/json/nsJSON.cpp index b54814688427..ae3999333073 100644 --- a/dom/json/nsJSON.cpp +++ b/dom/json/nsJSON.cpp @@ -22,6 +22,7 @@ #include "nsCRTGlue.h" #include "nsAutoPtr.h" #include "nsIScriptSecurityManager.h" +#include "nsNullPrincipal.h" #include "mozilla/Maybe.h" #include @@ -409,9 +410,19 @@ nsJSON::DecodeInternal(JSContext* cx, return NS_ERROR_OUT_OF_MEMORY; } - nsresult rv = - NS_NewInputStreamChannel(getter_AddRefs(jsonChannel), mURI, aStream, - NS_LITERAL_CSTRING("application/json")); + nsresult rv; + nsCOMPtr nullPrincipal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = NS_NewInputStreamChannel(getter_AddRefs(jsonChannel), + mURI, + aStream, + nullPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, + NS_LITERAL_CSTRING("application/json")); + if (!jsonChannel || NS_FAILED(rv)) return NS_ERROR_FAILURE; diff --git a/dom/jsurl/nsJSProtocolHandler.cpp b/dom/jsurl/nsJSProtocolHandler.cpp index 06ad830f7a63..2d3b56c65688 100644 --- a/dom/jsurl/nsJSProtocolHandler.cpp +++ b/dom/jsurl/nsJSProtocolHandler.cpp @@ -36,6 +36,7 @@ #include "nsIContentViewer.h" #include "nsIXPConnect.h" #include "nsContentUtils.h" +#include "nsNullPrincipal.h" #include "nsJSUtils.h" #include "nsThreadUtils.h" #include "nsIScriptChannel.h" @@ -427,9 +428,18 @@ nsresult nsJSChannel::Init(nsIURI *aURI) // and the underlying Input Stream will not be created... nsCOMPtr channel; + nsCOMPtr nullPrincipal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + // If the resultant script evaluation actually does return a value, we // treat it as html. - rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, mIOThunk, + rv = NS_NewInputStreamChannel(getter_AddRefs(channel), + aURI, + mIOThunk, + nullPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, NS_LITERAL_CSTRING("text/html")); if (NS_FAILED(rv)) return rv; From a2d26000ad11791ff6c3e69b4b8c318a93054bd1 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:15:21 -0700 Subject: [PATCH 15/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /extensions/ (r=karlt) --- extensions/gio/nsGIOProtocolHandler.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/gio/nsGIOProtocolHandler.cpp b/extensions/gio/nsGIOProtocolHandler.cpp index 6b483ffa57b5..105039ec61ce 100644 --- a/extensions/gio/nsGIOProtocolHandler.cpp +++ b/extensions/gio/nsGIOProtocolHandler.cpp @@ -17,6 +17,7 @@ #include "nsIStandardURL.h" #include "nsMimeTypes.h" #include "nsNetUtil.h" +#include "nsNullPrincipal.h" #include "mozilla/Monitor.h" #include #include @@ -1062,11 +1063,18 @@ nsGIOProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **aResult) } else { + nsCOMPtr nullPrincipal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + // start out assuming an unknown content-type. we'll set the content-type // to something better once we open the URI. rv = NS_NewInputStreamChannel(aResult, aURI, stream, + nullPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE)); if (NS_SUCCEEDED(rv)) stream->SetChannel(*aResult); From bfc8e917fb3c9a0679365f9ae43451f0d93591eb Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:15:40 -0700 Subject: [PATCH 16/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /gfx/ (r=roc) --- gfx/thebes/gfxSVGGlyphs.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/gfx/thebes/gfxSVGGlyphs.cpp b/gfx/thebes/gfxSVGGlyphs.cpp index 47f264b28bef..19de263fbc7b 100644 --- a/gfx/thebes/gfxSVGGlyphs.cpp +++ b/gfx/thebes/gfxSVGGlyphs.cpp @@ -369,17 +369,16 @@ gfxSVGGlyphsDocument::ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen) } nsCOMPtr channel; - rv = NS_NewInputStreamChannel(getter_AddRefs(channel), uri, nullptr /* stream */, - SVG_CONTENT_TYPE, UTF8_CHARSET); + rv = NS_NewInputStreamChannel(getter_AddRefs(channel), + uri, + nullptr, //aStream + principal, + nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, + nsIContentPolicy::TYPE_OTHER, + SVG_CONTENT_TYPE, + UTF8_CHARSET); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr loadInfo = - new LoadInfo(principal, - nullptr, - nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, - nsIContentPolicy::TYPE_OTHER); - channel->SetLoadInfo(loadInfo); - // Set this early because various decisions during page-load depend on it. document->SetIsBeingUsedAsImage(); document->SetReadyStateInternal(nsIDocument::READYSTATE_UNINITIALIZED); From 9764bce61f05d1e5159c7987e3ca43d745c90a9a Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:15:47 -0700 Subject: [PATCH 17/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /image/ (r=seth) --- image/decoders/icon/android/nsIconChannel.cpp | 12 +++++++++++- image/decoders/icon/gtk/nsIconChannel.cpp | 15 ++++++++++++--- image/decoders/icon/qt/nsIconChannel.cpp | 12 +++++++++++- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/image/decoders/icon/android/nsIconChannel.cpp b/image/decoders/icon/android/nsIconChannel.cpp index 074450366646..6030a66e0e07 100644 --- a/image/decoders/icon/android/nsIconChannel.cpp +++ b/image/decoders/icon/android/nsIconChannel.cpp @@ -12,6 +12,7 @@ #include "nsIconChannel.h" #include "nsIStringStream.h" #include "nsNetUtil.h" +#include "nsNullPrincipal.h" NS_IMPL_ISUPPORTS(nsIconChannel, nsIRequest, @@ -102,7 +103,16 @@ moz_icon_to_channel(nsIURI *aURI, const nsACString& aFileExt, uint32_t aIconSize rv = stream->AdoptData((char*)buf, buf_size); NS_ENSURE_SUCCESS(rv, rv); - return NS_NewInputStreamChannel(aChannel, aURI, stream, + nsCOMPtr nullPrincipal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_NewInputStreamChannel(aChannel, + aURI, + stream, + nullPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, NS_LITERAL_CSTRING(IMAGE_ICON_MS)); } diff --git a/image/decoders/icon/gtk/nsIconChannel.cpp b/image/decoders/icon/gtk/nsIconChannel.cpp index 03e830880922..b7b96e74a64c 100644 --- a/image/decoders/icon/gtk/nsIconChannel.cpp +++ b/image/decoders/icon/gtk/nsIconChannel.cpp @@ -33,6 +33,7 @@ extern "C" { #include "nsIStringBundle.h" #include "nsNetUtil.h" +#include "nsNullPrincipal.h" #include "nsIURL.h" #include "prlink.h" @@ -137,9 +138,17 @@ moz_gdk_pixbuf_to_channel(GdkPixbuf* aPixbuf, nsIURI *aURI, MOZ_ASSERT(NS_SUCCEEDED(rv)); NS_ENSURE_SUCCESS(rv, rv); - rv = NS_NewInputStreamChannel(aChannel, aURI, stream, - NS_LITERAL_CSTRING(IMAGE_ICON_MS)); - return rv; + nsCOMPtr nullPrincipal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_NewInputStreamChannel(aChannel, + aURI, + stream, + nullPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, + NS_LITERAL_CSTRING(IMAGE_ICON_MS)); } static GtkWidget *gProtoWindow = nullptr; diff --git a/image/decoders/icon/qt/nsIconChannel.cpp b/image/decoders/icon/qt/nsIconChannel.cpp index 46375a8b53e3..84f7bb95f5eb 100644 --- a/image/decoders/icon/qt/nsIconChannel.cpp +++ b/image/decoders/icon/qt/nsIconChannel.cpp @@ -16,6 +16,7 @@ #include "nsIStringBundle.h" #include "nsNetUtil.h" +#include "nsNullPrincipal.h" #include "nsIURL.h" #include "nsIconChannel.h" @@ -83,7 +84,16 @@ moz_qicon_to_channel(QImage *image, nsIURI *aURI, rv = stream->AdoptData((char*)buf, buf_size); NS_ENSURE_SUCCESS(rv, rv); - return NS_NewInputStreamChannel(aChannel, aURI, stream, + nsCOMPtr nullPrincipal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_NewInputStreamChannel(aChannel, + aURI, + stream, + nullPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, NS_LITERAL_CSTRING(IMAGE_ICON_MS)); } From 3ff98fc51a0677821dd8e997c4ab127d6f9cccb1 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:17:07 -0700 Subject: [PATCH 18/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /parser/ (r=bz) --- parser/xml/nsSAXXMLReader.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/parser/xml/nsSAXXMLReader.cpp b/parser/xml/nsSAXXMLReader.cpp index 049cb6c12601..2a119aed1cfe 100644 --- a/parser/xml/nsSAXXMLReader.cpp +++ b/parser/xml/nsSAXXMLReader.cpp @@ -6,6 +6,7 @@ #include "nsIInputStream.h" #include "nsNetCID.h" #include "nsNetUtil.h" +#include "nsNullPrincipal.h" #include "nsIParser.h" #include "nsParserCIID.h" #include "nsStreamUtils.h" @@ -495,9 +496,18 @@ nsSAXXMLReader::ParseFromStream(nsIInputStream *aStream, rv = EnsureBaseURI(); NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr nullPrincipal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr parserChannel; - rv = NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mBaseURI, - aStream, nsDependentCString(aContentType)); + rv = NS_NewInputStreamChannel(getter_AddRefs(parserChannel), + mBaseURI, + aStream, + nullPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, + nsDependentCString(aContentType)); if (!parserChannel || NS_FAILED(rv)) return NS_ERROR_FAILURE; From cd2a7562b59b55d17be15fd8ee157faf84fe1ac3 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:17:14 -0700 Subject: [PATCH 19/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /rdf/ (r=bsmedberg) --- rdf/base/nsRDFXMLParser.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rdf/base/nsRDFXMLParser.cpp b/rdf/base/nsRDFXMLParser.cpp index 03210658f305..fc466f3a90e2 100644 --- a/rdf/base/nsRDFXMLParser.cpp +++ b/rdf/base/nsRDFXMLParser.cpp @@ -13,6 +13,7 @@ #include "nsParserCIID.h" #include "nsStringStream.h" #include "nsNetUtil.h" +#include "nsNullPrincipal.h" static NS_DEFINE_CID(kParserCID, NS_PARSER_CID); @@ -114,8 +115,17 @@ nsRDFXMLParser::ParseString(nsIRDFDataSource* aSink, nsIURI* aBaseURI, const nsA rv = NS_NewCStringInputStream(getter_AddRefs(stream), aString); if (NS_FAILED(rv)) return rv; + nsCOMPtr nullPrincipal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr channel; - rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aBaseURI, stream, + rv = NS_NewInputStreamChannel(getter_AddRefs(channel), + aBaseURI, + stream, + nullPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, NS_LITERAL_CSTRING("text/xml")); if (NS_FAILED(rv)) return rv; From 1fdd35f09d4f67129edbb6a1dc38a86c29839925 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:17:22 -0700 Subject: [PATCH 20/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /toolit/ (r=mak77) --- toolkit/components/places/nsAnnoProtocolHandler.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/toolkit/components/places/nsAnnoProtocolHandler.cpp b/toolkit/components/places/nsAnnoProtocolHandler.cpp index 0ab98e175ba9..115eed6c5151 100644 --- a/toolkit/components/places/nsAnnoProtocolHandler.cpp +++ b/toolkit/components/places/nsAnnoProtocolHandler.cpp @@ -335,8 +335,12 @@ nsAnnoProtocolHandler::NewFaviconChannel(nsIURI *aURI, nsIURI *aAnnotationURI, // Create our channel. We'll call SetContentType with the right type when // we know what it actually is. nsCOMPtr channel; - rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, inputStream, - EmptyCString()); + rv = NS_NewInputStreamChannel(getter_AddRefs(channel), + aURI, + inputStream, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER); NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel)); // Now we go ahead and get our data asynchronously for the favicon. From 9090067df3ce0cbc6d80c8cbfb4d0f1668d55f13 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 16 Oct 2014 11:17:29 -0700 Subject: [PATCH 21/81] Bug 1063197 - Callsites of NS_NewInputStreamChannel in /docshell/ (r=bz) --- docshell/base/nsDocShell.cpp | 48 ++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index d218f4810825..a6b1136dd1f3 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -1655,12 +1655,25 @@ nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI, mLoadType = loadType; + nsCOMPtr owner; + aLoadInfo->GetOwner(getter_AddRefs(owner)); + nsCOMPtr requestingPrincipal = do_QueryInterface(owner); + if (!requestingPrincipal) { + requestingPrincipal = nsContentUtils::GetSystemPrincipal(); + } + // build up a channel for this stream. nsCOMPtr channel; - NS_ENSURE_SUCCESS(NS_NewInputStreamChannel - (getter_AddRefs(channel), uri, aStream, - aContentType, aContentCharset), - NS_ERROR_FAILURE); + nsresult rv = + NS_NewInputStreamChannel(getter_AddRefs(channel), + uri, + aStream, + requestingPrincipal, + nsILoadInfo::SEC_NORMAL, + nsIContentPolicy::TYPE_OTHER, + aContentType, + aContentCharset); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); nsCOMPtr uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID)); @@ -10266,25 +10279,28 @@ nsDocShell::DoURILoad(nsIURI * aURI, rv = vsh->NewSrcdocChannel(aURI, aSrcdoc, aBaseURI, getter_AddRefs(channel)); NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr loadInfo = + new LoadInfo(requestingPrincipal, + requestingNode, + securityFlags, + aContentPolicyType); + channel->SetLoadInfo(loadInfo); } else { - rv = NS_NewInputStreamChannel(getter_AddRefs(channel),aURI, - aSrcdoc, - NS_LITERAL_CSTRING("text/html"), - true); + rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), + aURI, + aSrcdoc, + NS_LITERAL_CSTRING("text/html"), + requestingNode, + requestingPrincipal, + securityFlags, + aContentPolicyType, + true); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr isc = do_QueryInterface(channel); MOZ_ASSERT(isc); isc->SetBaseURI(aBaseURI); } - // NS_NewInputStreamChannel does not yet attach the loadInfo in nsNetutil.h, - // hence we have to manually attach the loadInfo for that channel. - nsCOMPtr loadInfo = - new LoadInfo(requestingPrincipal, - requestingNode, - securityFlags, - aContentPolicyType); - channel->SetLoadInfo(loadInfo); } nsCOMPtr appCacheChannel = From 25c6db8f3d5107ee93477303909819760561bb0f Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Thu, 16 Oct 2014 14:28:50 -0500 Subject: [PATCH 22/81] Bug 1083376 - Fix for typeo error in browserPlacesViews.js, regression from bug 1068671. r=mak --- browser/components/places/content/browserPlacesViews.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/places/content/browserPlacesViews.js b/browser/components/places/content/browserPlacesViews.js index e93d60e5d2cc..6b057ea5b201 100644 --- a/browser/components/places/content/browserPlacesViews.js +++ b/browser/components/places/content/browserPlacesViews.js @@ -1376,7 +1376,7 @@ PlacesToolbar.prototype = { elt.localName != "menupopup") { let eltRect = elt.getBoundingClientRect(); let eltIndex = Array.indexOf(this._rootElt.childNodes, elt); - if (PlacesUIUtils.nodeIsFolder(elt._placesNode) && + if (PlacesUtils.nodeIsFolder(elt._placesNode) && !PlacesUIUtils.isContentsReadOnly(elt._placesNode)) { // This is a folder. // If we are in the middle of it, drop inside it. From 45e8946fb1279f887463a2bfd4c0baf548508b85 Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Thu, 16 Oct 2014 15:36:47 -0400 Subject: [PATCH 23/81] Bug 1064835 - Fix SPS crash. r=jandem --- js/src/jit/Bailouts.cpp | 18 +++++++++++++----- js/src/jit/BaselineBailouts.cpp | 18 +++++++++++++++--- js/src/jit/BaselineJIT.h | 3 ++- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/js/src/jit/Bailouts.cpp b/js/src/jit/Bailouts.cpp index 13bc359a858d..00d35450c9a1 100644 --- a/js/src/jit/Bailouts.cpp +++ b/js/src/jit/Bailouts.cpp @@ -49,7 +49,9 @@ jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) MOZ_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; - uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo); + bool poppedLastSPSFrame = false; + uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo, + /* excInfo = */ nullptr, &poppedLastSPSFrame); MOZ_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); @@ -68,7 +70,8 @@ jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) // pseudostack frame would not have been pushed in the first // place, so don't pop anything in that case. bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() && - (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck); + (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) && + !poppedLastSPSFrame; JSScript *script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); @@ -105,7 +108,9 @@ jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, MOZ_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; - uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo); + bool poppedLastSPSFrame = false; + uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo, + /* excInfo = */ nullptr, &poppedLastSPSFrame); MOZ_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); @@ -124,7 +129,8 @@ jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, // pseudostack frame would not have been pushed in the first // place, so don't pop anything in that case. bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() && - (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck); + (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) && + !poppedLastSPSFrame; JSScript *script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); @@ -180,7 +186,9 @@ jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, JitFrameIterator iter(jitActivations); BaselineBailoutInfo *bailoutInfo = nullptr; - uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, &bailoutInfo, &excInfo); + bool poppedLastSPSFrame = false; + uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, + &bailoutInfo, &excInfo, &poppedLastSPSFrame); if (retval == BAILOUT_RETURN_OK) { MOZ_ASSERT(bailoutInfo); diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index 24fdad6a012d..c0ee98dbc2c6 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -515,9 +515,12 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, HandleFunction fun, HandleScript script, IonScript *ionScript, SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder, AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee, - jsbytecode **callPC, const ExceptionBailoutInfo *excInfo) + jsbytecode **callPC, const ExceptionBailoutInfo *excInfo, + bool *poppedLastSPSFrameOut) { MOZ_ASSERT(script->hasBaselineScript()); + MOZ_ASSERT(poppedLastSPSFrameOut); + MOZ_ASSERT(!*poppedLastSPSFrameOut); // Are we catching an exception? bool catchingException = excInfo && excInfo->catchingException(); @@ -1035,6 +1038,11 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, JitSpew(JitSpew_BaselineBailouts, " Popping SPS entry for outermost frame"); cx->runtime()->spsProfiler.exit(script, fun); + + // Notify caller that the last SPS frame was popped, so not + // to do it again. + if (poppedLastSPSFrameOut) + *poppedLastSPSFrameOut = true; } } } else { @@ -1291,7 +1299,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, uint32_t jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIterator &iter, bool invalidate, BaselineBailoutInfo **bailoutInfo, - const ExceptionBailoutInfo *excInfo) + const ExceptionBailoutInfo *excInfo, bool *poppedLastSPSFrameOut) { // The Baseline frames we will reconstruct on the heap are not rooted, so GC // must be suppressed here. @@ -1300,6 +1308,9 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIter MOZ_ASSERT(bailoutInfo != nullptr); MOZ_ASSERT(*bailoutInfo == nullptr); + MOZ_ASSERT(poppedLastSPSFrameOut); + MOZ_ASSERT(!*poppedLastSPSFrameOut); + TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); TraceLogStopEvent(logger, TraceLogger::IonMonkey); TraceLogStartEvent(logger, TraceLogger::Baseline); @@ -1429,7 +1440,8 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIter RootedFunction nextCallee(cx, nullptr); if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(), snapIter, invalidate, builder, startFrameFormals, - &nextCallee, &callPC, passExcInfo ? excInfo : nullptr)) + &nextCallee, &callPC, passExcInfo ? excInfo : nullptr, + poppedLastSPSFrameOut)) { return BAILOUT_RETURN_FATAL_ERROR; } diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 6800fd891166..89243877a66b 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -439,7 +439,8 @@ struct BaselineBailoutInfo uint32_t BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIterator &iter, bool invalidate, BaselineBailoutInfo **bailoutInfo, - const ExceptionBailoutInfo *exceptionInfo = nullptr); + const ExceptionBailoutInfo *exceptionInfo, + bool *poppedLastSPSFrame); // Mark baseline scripts on the stack as active, so that they are not discarded // during GC. From 87d538f3093ff306f040b8f8739bd34c9a9bb439 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 8 Oct 2014 12:09:08 -0500 Subject: [PATCH 24/81] Bug 1081255 - Rewrite comments in jsproxy.h; reclassify the methods a bit. No change in behavior. r=efaust, r=bz, r=jwalden. --HG-- extra : rebase_source : b3440548322d65b2bd01862fb863e6197eb263c6 --- dom/base/nsGlobalWindow.cpp | 59 ++--- dom/bindings/DOMJSProxyHandler.h | 40 +-- js/ipc/WrapperOwner.cpp | 15 +- js/ipc/WrapperOwner.h | 24 +- js/src/jsproxy.h | 263 +++++++++++++------- js/src/jswrapper.h | 62 ++--- js/src/proxy/DeadObjectProxy.h | 16 +- js/src/proxy/Proxy.h | 32 ++- js/src/proxy/ScriptedDirectProxyHandler.h | 28 +-- js/src/proxy/ScriptedIndirectProxyHandler.h | 18 +- js/src/vm/ProxyObject.h | 2 +- js/xpconnect/wrappers/AddonWrapper.h | 13 +- js/xpconnect/wrappers/ChromeObjectWrapper.h | 21 +- js/xpconnect/wrappers/FilteringWrapper.h | 28 +-- js/xpconnect/wrappers/WaiveXrayWrapper.h | 17 +- js/xpconnect/wrappers/XrayWrapper.h | 51 ++-- 16 files changed, 380 insertions(+), 309 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 44067714919b..afce81803be1 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -599,20 +599,7 @@ public: return false; } - virtual const char *className(JSContext *cx, - JS::Handle wrapper) const MOZ_OVERRIDE; - virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE; - - // Fundamental traps - virtual bool isExtensible(JSContext *cx, JS::Handle proxy, bool *extensible) - const MOZ_OVERRIDE; - virtual bool preventExtensions(JSContext *cx, - JS::Handle proxy) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext* cx, - JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc) - const MOZ_OVERRIDE; + // Standard internal methods virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle proxy, JS::Handle id, @@ -631,23 +618,12 @@ public: bool *bp) const MOZ_OVERRIDE; virtual bool enumerate(JSContext *cx, JS::Handle proxy, JS::AutoIdVector &props) const MOZ_OVERRIDE; - - virtual bool watch(JSContext *cx, JS::Handle proxy, - JS::Handle id, JS::Handle callable) const MOZ_OVERRIDE; - virtual bool unwatch(JSContext *cx, JS::Handle proxy, - JS::Handle id) const MOZ_OVERRIDE; - virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE { - return false; - } - virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE { - return false; - } - - // Derived traps + virtual bool isExtensible(JSContext *cx, JS::Handle proxy, bool *extensible) + const MOZ_OVERRIDE; + virtual bool preventExtensions(JSContext *cx, + JS::Handle proxy) const MOZ_OVERRIDE; virtual bool has(JSContext *cx, JS::Handle proxy, JS::Handle id, bool *bp) const MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, JS::Handle proxy, - JS::Handle id, bool *bp) const MOZ_OVERRIDE; virtual bool get(JSContext *cx, JS::Handle proxy, JS::Handle receiver, JS::Handle id, @@ -657,11 +633,36 @@ public: JS::Handle id, bool strict, JS::MutableHandle vp) const MOZ_OVERRIDE; + + // SpiderMonkey extensions + virtual bool getPropertyDescriptor(JSContext* cx, + JS::Handle proxy, + JS::Handle id, + JS::MutableHandle desc) + const MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, JS::Handle proxy, + JS::Handle id, bool *bp) const MOZ_OVERRIDE; virtual bool keys(JSContext *cx, JS::Handle proxy, JS::AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, JS::Handle proxy, unsigned flags, JS::MutableHandle vp) const MOZ_OVERRIDE; + virtual const char *className(JSContext *cx, + JS::Handle wrapper) const MOZ_OVERRIDE; + + virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE; + + virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE { + return false; + } + virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE { + return false; + } + + virtual bool watch(JSContext *cx, JS::Handle proxy, + JS::Handle id, JS::Handle callable) const MOZ_OVERRIDE; + virtual bool unwatch(JSContext *cx, JS::Handle proxy, + JS::Handle id) const MOZ_OVERRIDE; static void ObjectMoved(JSObject *obj, const JSObject *old); diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index 0ecdedf74931..ade0df8962f9 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -50,23 +50,20 @@ public: : js::BaseProxyHandler(aProxyFamily, aHasPrototype) {} - // Implementations of traps that can be implemented in terms of - // fundamental traps. - bool enumerate(JSContext* cx, JS::Handle proxy, - JS::AutoIdVector& props) const MOZ_OVERRIDE; - bool getPropertyDescriptor(JSContext* cx, JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc) const MOZ_OVERRIDE; + // Implementations of methods that can be implemented in terms of + // other lower-level methods. bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle proxy, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; - - bool watch(JSContext* cx, JS::Handle proxy, JS::Handle id, - JS::Handle callable) const MOZ_OVERRIDE; - bool unwatch(JSContext* cx, JS::Handle proxy, - JS::Handle id) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext* cx, JS::Handle proxy, JS::AutoIdVector &props) const MOZ_OVERRIDE; + bool enumerate(JSContext* cx, JS::Handle proxy, + JS::AutoIdVector& props) const MOZ_OVERRIDE; + + bool getPropertyDescriptor(JSContext* cx, JS::Handle proxy, + JS::Handle id, + JS::MutableHandle desc) const MOZ_OVERRIDE; + // We override keys() and implement it directly instead of using the // default implementation, which would getOwnPropertyNames and then // filter out the non-enumerable ones. This avoids doing @@ -74,6 +71,11 @@ public: virtual bool keys(JSContext* cx, JS::Handle proxy, JS::AutoIdVector &props) const MOZ_OVERRIDE; + bool watch(JSContext* cx, JS::Handle proxy, JS::Handle id, + JS::Handle callable) const MOZ_OVERRIDE; + bool unwatch(JSContext* cx, JS::Handle proxy, + JS::Handle id) const MOZ_OVERRIDE; + protected: // Hook for subclasses to implement shared ownPropertyKeys()/keys() // functionality. The "flags" argument is either JSITER_OWNONLY (for keys()) @@ -100,7 +102,6 @@ public: : BaseDOMProxyHandler(&family) {} - bool preventExtensions(JSContext *cx, JS::Handle proxy) const MOZ_OVERRIDE; bool defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE { @@ -110,15 +111,16 @@ public: virtual bool defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, JS::MutableHandle desc, bool* defined) const; + bool delete_(JSContext* cx, JS::Handle proxy, + JS::Handle id, bool* bp) const MOZ_OVERRIDE; + bool isExtensible(JSContext *cx, JS::Handle proxy, bool *extensible) + const MOZ_OVERRIDE; + bool preventExtensions(JSContext *cx, JS::Handle proxy) const MOZ_OVERRIDE; + bool has(JSContext* cx, JS::Handle proxy, JS::Handle id, + bool* bp) const MOZ_OVERRIDE; bool set(JSContext *cx, JS::Handle proxy, JS::Handle receiver, JS::Handle id, bool strict, JS::MutableHandle vp) const MOZ_OVERRIDE; - bool delete_(JSContext* cx, JS::Handle proxy, - JS::Handle id, bool* bp) const MOZ_OVERRIDE; - bool has(JSContext* cx, JS::Handle proxy, JS::Handle id, - bool* bp) const MOZ_OVERRIDE; - bool isExtensible(JSContext *cx, JS::Handle proxy, bool *extensible) - const MOZ_OVERRIDE; /* * If assigning to proxy[id] hits a named setter with OverrideBuiltins or diff --git a/js/ipc/WrapperOwner.cpp b/js/ipc/WrapperOwner.cpp index adc5df67f67f..2a27ca9ccac4 100644 --- a/js/ipc/WrapperOwner.cpp +++ b/js/ipc/WrapperOwner.cpp @@ -63,9 +63,6 @@ class CPOWProxyHandler : public BaseProxyHandler return false; } - virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const MOZ_OVERRIDE; virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, @@ -74,18 +71,20 @@ class CPOWProxyHandler : public BaseProxyHandler AutoIdVector &props) const MOZ_OVERRIDE; virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; - + virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE; virtual bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver, JS::HandleId id, bool strict, JS::MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; - - virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; + virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) const MOZ_OVERRIDE; virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue, diff --git a/js/ipc/WrapperOwner.h b/js/ipc/WrapperOwner.h index 26f19113c72d..08bfd8f78a6f 100644 --- a/js/ipc/WrapperOwner.h +++ b/js/ipc/WrapperOwner.h @@ -31,11 +31,8 @@ class WrapperOwner : public virtual JavaScriptShared explicit WrapperOwner(JSRuntime *rt); bool init(); - // Fundamental proxy traps. These are required. + // Standard internal methods. // (The traps should be in the same order like js/src/jsproxy.h) - bool preventExtensions(JSContext *cx, JS::HandleObject proxy); - bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, - JS::MutableHandle desc); bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::MutableHandle desc); bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, @@ -43,25 +40,26 @@ class WrapperOwner : public virtual JavaScriptShared bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props); bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp); bool enumerate(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props); - - // Derived proxy traps. Implementing these is useful for perfomance. + bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible); + bool preventExtensions(JSContext *cx, JS::HandleObject proxy); bool has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp); - bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp); bool get(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver, JS::HandleId id, JS::MutableHandleValue vp); bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver, JS::HandleId id, bool strict, JS::MutableHandleValue vp); - bool keys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props); - // We use "iterate" provided by the base class here. - - // SpiderMonkey Extensions. - bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible); - bool regexp_toShared(JSContext *cx, JS::HandleObject proxy, js::RegExpGuard *g); bool callOrConstruct(JSContext *cx, JS::HandleObject proxy, const JS::CallArgs &args, bool construct); + + // SpiderMonkey extensions. + bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + JS::MutableHandle desc); + bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp); + bool keys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props); + // We use "iterate" provided by the base class here. bool hasInstance(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool *bp); bool objectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue); const char* className(JSContext *cx, JS::HandleObject proxy); + bool regexp_toShared(JSContext *cx, JS::HandleObject proxy, js::RegExpGuard *g); bool isCallable(JSObject *obj); bool isConstructor(JSObject *obj); diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index aa19392a910a..940ded54ba92 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -33,57 +33,120 @@ class RegExpGuard; class JS_FRIEND_API(Wrapper); /* - * A proxy is a JSObject that implements generic behavior by providing custom - * implementations for each object trap. The implementation for each trap is - * provided by a C++ object stored on the proxy, known as its handler. + * A proxy is a JSObject with highly customizable behavior. ES6 specifies a + * single kind of proxy, but the customization mechanisms we use to implement + * ES6 Proxy objects are also useful wherever an object with weird behavior is + * wanted. Proxies are used to implement: * - * A major use case for proxies is to forward each trap to another object, - * known as its target. The target can be an arbitrary C++ object. Not every - * proxy has the notion of a target, however. + * - the scope objects used by the Debugger's frame.eval() method + * (see js::GetDebugScopeForFunction) * - * Proxy traps are grouped into fundamental and derived traps. Every proxy has - * to at least provide implementations for the fundamental traps, but the - * derived traps can be implemented in terms of the fundamental ones - * BaseProxyHandler provides implementations of the derived traps in terms of - * the (pure virtual) fundamental traps. + * - the khuey hack, whereby a whole compartment can be blown away + * even if other compartments hold references to objects in it + * (see js::NukeCrossCompartmentWrappers) * - * In addition to the normal traps, there are two models for proxy prototype - * chains. First, proxies may opt to use the standard prototype mechanism used - * throughout the engine. To do so, simply pass a prototype to NewProxyObject() - * at creation time. All prototype accesses will then "just work" to treat the - * proxy as a "normal" object. Alternatively, if instead the proxy wishes to - * implement more complicated prototype semantics (if, for example, it wants to - * delegate the prototype lookup to a wrapped object), it may pass Proxy::LazyProto - * as the prototype at create time and opt in to the trapped prototype system, - * which guarantees that their trap will be called on any and every prototype - * chain access of the object. + * - XPConnect security wrappers, which protect chrome from malicious content + * (js/xpconnect/wrappers) * - * This system is implemented with two traps: {get,set}PrototypeOf. The default - * implementation of setPrototypeOf throws a TypeError. Since it is not possible - * to create an object without a sense of prototype chain, handler implementors - * must provide a getPrototypeOf trap if opting in to the dynamic prototype system. + * - DOM objects with special property behavior, like named getters + * (dom/bindings/Codegen.py generates these proxies from WebIDL) + * + * - semi-transparent use of objects that live in other processes + * (CPOWs, implemented in js/ipc) + * + * ### Proxies and internal methods + * + * ES6 draft rev 27 (24 August 2014) specifies 14 internal methods. The runtime + * semantics of just about everything a script can do to an object is specified + * in terms of these internal methods. For example: + * + * JS code ES6 internal method that gets called + * --------------------------- -------------------------------- + * obj.prop obj.[[Get]](obj, "prop") + * "prop" in obj obj.[[HasProperty]]("prop") + * new obj() obj.[[Construct]]() + * for (k in obj) {} obj.[[Enumerate]]() + * + * With regard to the implementation of these internal methods, there are three + * very different kinds of object in SpiderMonkey. + * + * 1. Native objects' internal methods are implemented in js::baseops in + * vm/NativeObject.cpp, with duplicate (but functionally identical) + * implementations scattered through the ICs and JITs. + * + * 2. Certain non-native objects have internal methods that are implemented as + * magical js::ObjectOps hooks. We're trying to get rid of these. + * + * 3. All other objects are proxies. A proxy's internal methods are + * implemented in C++, as the virtual methods of a C++ object stored on the + * proxy, known as its handler. + * + * This means that just about anything you do to a proxy will end up going + * through a C++ virtual method call. Possibly several. There's no reason the + * JITs and ICs can't specialize for particular proxies, based on the handler; + * but currently we don't do much of this, so the virtual method overhead + * typically is actually incurred. + * + * ### The proxy handler hierarchy + * + * A major use case for proxies is to forward each internal method call to + * another object, known as its target. The target can be an arbitrary JS + * object. Not every proxy has the notion of a target, however. * * To minimize code duplication, a set of abstract proxy handler classes is - * provided, from which other handlers may inherit. These abstract classes - * are organized in the following hierarchy: + * provided, from which other handlers may inherit. These abstract classes are + * organized in the following hierarchy: * - * BaseProxyHandler - * | - * DirectProxyHandler - * | - * Wrapper + * BaseProxyHandler + * | + * DirectProxyHandler // has a target + * | + * Wrapper // can be unwrapped, revealing target + * | // (see js::CheckedUnwrap) + * | + * CrossCompartmentWrapper // target is in another compartment; + * // implements membrane between compartments + * + * Example: Some DOM objects (including all the arraylike DOM objects) are + * implemented as proxies. Since these objects don't need to forward operations + * to any underlying JS object, DOMJSProxyHandler directly subclasses + * BaseProxyHandler. + * + * Gecko's security wrappers are examples of cross-compartment wrappers. + * + * ### Proxy prototype chains + * + * In addition to the normal methods, there are two models for proxy prototype + * chains. + * + * 1. Proxies can use the standard prototype mechanism used throughout the + * engine. To do so, simply pass a prototype to NewProxyObject() at + * creation time. All prototype accesses will then "just work" to treat the + * proxy as a "normal" object. + * + * 2. A proxy can implement more complicated prototype semantics (if, for + * example, it wants to delegate the prototype lookup to a wrapped object) + * by passing Proxy::LazyProto as the prototype at create time. This + * guarantees that the getPrototypeOf() handler method will be called every + * time the object's prototype chain is accessed. + * + * This system is implemented with two methods: {get,set}PrototypeOf. The + * default implementation of setPrototypeOf throws a TypeError. Since it is + * not possible to create an object without a sense of prototype chain, + * handlers must implement getPrototypeOf if opting in to the dynamic + * prototype system. */ /* * BaseProxyHandler is the most generic kind of proxy handler. It does not make * any assumptions about the target. Consequently, it does not provide any - * default implementation for the fundamental traps. It does, however, implement - * the derived traps in terms of the fundamental ones. This allows consumers of - * this class to define any custom behavior they want. + * default implementation for most methods. As a convenience, a few high-level + * methods, like get() and set(), are given default implementations that work by + * calling the low-level methods, like getOwnPropertyDescriptor(). * - * Important: If you add a trap here, you should probably also add a Proxy::foo - * entry point with an AutoEnterPolicy. If you don't, you need an explicit - * override for the trap in SecurityWrapper. See bug 945826 comment 0. + * Important: If you add a method here, you should probably also add a + * Proxy::foo entry point with an AutoEnterPolicy. If you don't, you need an + * explicit override for the method in SecurityWrapper. See bug 945826 comment 0. */ class JS_FRIEND_API(BaseProxyHandler) { @@ -103,7 +166,7 @@ class JS_FRIEND_API(BaseProxyHandler) * * - When mHasPrototype is true, the engine never calls these methods: * getPropertyDescriptor, has, set, enumerate, iterate. Instead, for - * these operations, it calls the "own" traps like + * these operations, it calls the "own" methods like * getOwnPropertyDescriptor, hasOwn, defineProperty, keys, etc., and * consults the prototype chain if needed. * @@ -155,14 +218,14 @@ class JS_FRIEND_API(BaseProxyHandler) return true; } - /* Policy enforcement traps. + /* Policy enforcement methods. * * enter() allows the policy to specify whether the caller may perform |act| * on the proxy's |id| property. In the case when |act| is CALL, |id| is * generally JSID_VOID. * * The |act| parameter to enter() specifies the action being performed. - * If |bp| is false, the trap suggests that the caller throw (though it + * If |bp| is false, the method suggests that the caller throw (though it * may still decide to squelch the error). * * We make these OR-able so that assertEnteredPolicy can pass a union of them. @@ -183,34 +246,62 @@ class JS_FRIEND_API(BaseProxyHandler) virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act, bool *bp) const; - /* ES5 Harmony fundamental proxy traps. */ - virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const = 0; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const = 0; - virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, - HandleId id, MutableHandle desc) const = 0; + /* Standard internal methods. */ + virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const = 0; virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const = 0; virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const = 0; virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const = 0; virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const = 0; + virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const = 0; + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const = 0; - /* ES5 Harmony derived proxy traps. */ + /* + * These methods are standard, but the engine does not normally call them. + * They're opt-in. See "Proxy prototype chains" above. + * + * getPrototypeOf() crashes if called. setPrototypeOf() throws a TypeError. + */ + virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const; + virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const; + + /* + * These standard internal methods are implemented, as a convenience, so + * that ProxyHandler subclasses don't have to provide every single method. + * + * The base-class implementations work by calling getPropertyDescriptor(). + * They do not follow any standard. When in doubt, override them. + */ virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const; - virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const; virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const; virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, MutableHandleValue vp) const; + + /* + * [[Call]] and [[Construct]] are standard internal methods but according + * to the spec, they are not present on every object. + * + * SpiderMonkey never calls a proxy's call()/construct() internal method + * unless isCallable()/isConstructor() returns true for that proxy. + * + * BaseProxyHandler::isCallable()/isConstructor() always return false, and + * BaseProxyHandler::call()/construct() crash if called. So if you're + * creating a kind of that is never callable, you don't have to override + * anything, but otherwise you probably want to override all four. + */ + virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const; + virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const; + + /* SpiderMonkey extensions. */ + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const = 0; + virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const; virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const; virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp) const; - - /* Spidermonkey extensions. */ - virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const = 0; - virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const; - virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) const; virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) const; virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const; @@ -221,8 +312,6 @@ class JS_FRIEND_API(BaseProxyHandler) virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) const; virtual void finalize(JSFreeOp *fop, JSObject *proxy) const; virtual void objectMoved(JSObject *proxy, const JSObject *old) const; - virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const; - virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const; // Allow proxies, wrappers in particular, to specify callability at runtime. // Note: These do not take const JSObject *, but they do in spirit. @@ -246,13 +335,13 @@ class JS_FRIEND_API(BaseProxyHandler) }; /* - * DirectProxyHandler includes a notion of a target object. All traps are + * DirectProxyHandler includes a notion of a target object. All methods are * reimplemented such that they forward their behavior to the target. This * allows consumers of this class to forward to another object as transparently * and efficiently as possible. * - * Important: If you add a trap implementation here, you probably also need to - * add an override in CrossCompartmentWrapper. If you don't, you risk + * Important: If you add a method implementation here, you probably also need + * to add an override in CrossCompartmentWrapper. If you don't, you risk * compartment mismatches. See bug 945826 comment 0. */ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler @@ -263,10 +352,7 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler : BaseProxyHandler(aFamily, aHasPrototype, aHasSecurityPolicy) { } - /* ES5 Harmony fundamental proxy traps. */ - virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; + /* Standard internal methods. */ virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const MOZ_OVERRIDE; virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, @@ -277,33 +363,34 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler bool *bp) const MOZ_OVERRIDE; virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; - - /* ES5 Harmony derived proxy traps. */ - virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, - bool *bp) const MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, - bool *bp) const MOZ_OVERRIDE; - virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, HandleObject proxy, - AutoIdVector &props) const MOZ_OVERRIDE; - virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, - MutableHandleValue vp) const MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; - virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) const MOZ_OVERRIDE; - virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, - bool *bp) const MOZ_OVERRIDE; + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const MOZ_OVERRIDE; virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const MOZ_OVERRIDE; + virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, + bool *bp) const MOZ_OVERRIDE; + virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, bool strict, MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + + /* SpiderMonkey extensions. */ + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, + bool *bp) const MOZ_OVERRIDE; + virtual bool keys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, + MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const MOZ_OVERRIDE; + virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, + bool *bp) const MOZ_OVERRIDE; virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const MOZ_OVERRIDE; virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; @@ -312,8 +399,8 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE; virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const; - virtual JSObject *weakmapKeyDelegate(JSObject *proxy) const MOZ_OVERRIDE; virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE; + virtual JSObject *weakmapKeyDelegate(JSObject *proxy) const MOZ_OVERRIDE; }; extern JS_FRIEND_DATA(const js::Class* const) ProxyClassPtr; @@ -469,8 +556,8 @@ class JS_FRIEND_API(AutoEnterPolicy) Action enteredAction; // NB: We explicitly don't track the entered action here, because sometimes - // SET traps do an implicit GET during their implementation, leading to - // spurious assertions. + // set() methods do an implicit get() during their implementation, leading + // to spurious assertions. AutoEnterPolicy *prev; void recordEnter(JSContext *cx, HandleObject proxy, HandleId id, Action act); void recordLeave(); diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 82021e7b8708..dd7216752b99 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -46,12 +46,13 @@ class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions { /* * A wrapper is a proxy with a target object to which it generally forwards - * operations, but may restrict access to certain operations or instrument - * the trap operations in various ways. A wrapper is distinct from a Direct Proxy - * Handler in the sense that it can be "unwrapped" in C++, exposing the underlying + * operations, but may restrict access to certain operations or instrument the + * methods in various ways. A wrapper is distinct from a Direct Proxy Handler + * in the sense that it can be "unwrapped" in C++, exposing the underlying * object (Direct Proxy Handlers have an underlying target object, but don't * expect to expose this object via any kind of unwrapping operation). Callers - * should be careful to avoid unwrapping security wrappers in the wrong context. + * should be careful to avoid unwrapping security wrappers in the wrong + * context. */ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler { @@ -112,10 +113,7 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper : Wrapper(CROSS_COMPARTMENT | aFlags, aHasPrototype, aHasSecurityPolicy) { } - /* ES5 Harmony fundamental wrapper traps. */ - virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; + /* Standard internal methods. */ virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandle desc) const MOZ_OVERRIDE; virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, @@ -124,22 +122,27 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper AutoIdVector &props) const MOZ_OVERRIDE; virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE; - - /* ES5 Harmony derived wrapper traps. */ + virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const MOZ_OVERRIDE; + virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) const MOZ_OVERRIDE; + virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, + MutableHandleObject protop) const MOZ_OVERRIDE; + virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, + bool *bp) const MOZ_OVERRIDE; virtual bool has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool get(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE; virtual bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, bool strict, MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const MOZ_OVERRIDE; + virtual bool construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const MOZ_OVERRIDE; + + /* SpiderMonkey extensions. */ + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, HandleObject wrapper, unsigned flags, MutableHandleValue vp) const MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const MOZ_OVERRIDE; - virtual bool call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) const MOZ_OVERRIDE; virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, @@ -151,10 +154,6 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, - MutableHandleObject protop) const MOZ_OVERRIDE; - virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, - bool *bp) const MOZ_OVERRIDE; static const CrossCompartmentWrapper singleton; static const CrossCompartmentWrapper singletonWithPrototype; @@ -177,31 +176,32 @@ class JS_FRIEND_API(SecurityWrapper) : public Base : Base(flags, hasPrototype, /* hasSecurityPolicy = */ true) { } - virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const MOZ_OVERRIDE; - virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) const MOZ_OVERRIDE; virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Wrapper::Action act, bool *bp) const MOZ_OVERRIDE; + + virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const MOZ_OVERRIDE; + virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) const MOZ_OVERRIDE; + virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, + bool *bp) const MOZ_OVERRIDE; + virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) const MOZ_OVERRIDE; - virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, - MutableHandleValue vp) const MOZ_OVERRIDE; virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const MOZ_OVERRIDE; virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE; virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; + virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, + MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, - bool *bp) const MOZ_OVERRIDE; + // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded + // against. virtual bool watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable) const MOZ_OVERRIDE; virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id) const MOZ_OVERRIDE; - // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded - // against. - /* * Allow our subclasses to select the superclass behavior they want without * needing to specify an exact superclass. diff --git a/js/src/proxy/DeadObjectProxy.h b/js/src/proxy/DeadObjectProxy.h index 2883c6f2d32c..dbd574fb4a08 100644 --- a/js/src/proxy/DeadObjectProxy.h +++ b/js/src/proxy/DeadObjectProxy.h @@ -18,10 +18,7 @@ class DeadObjectProxy : public BaseProxyHandler : BaseProxyHandler(&family) { } - /* ES5 Harmony fundamental wrapper traps. */ - virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; + /* Standard internal methods. */ virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandle desc) const MOZ_OVERRIDE; virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, @@ -30,11 +27,16 @@ class DeadObjectProxy : public BaseProxyHandler AutoIdVector &props) const MOZ_OVERRIDE; virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; + virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, + MutableHandleObject protop) const MOZ_OVERRIDE; virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + + /* SpiderMonkey extensions. */ + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) const MOZ_OVERRIDE; virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, @@ -46,8 +48,6 @@ class DeadObjectProxy : public BaseProxyHandler virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, - MutableHandleObject protop) const MOZ_OVERRIDE; static const char family; static const DeadObjectProxy singleton; diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h index 90e36fec996a..7e26163089b0 100644 --- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -18,19 +18,14 @@ class RegExpGuard; /* * Dispatch point for handlers that executes the appropriate C++ or scripted traps. * - * Important: All proxy traps need either (a) an AutoEnterPolicy in their + * Important: All proxy methods need either (a) an AutoEnterPolicy in their * Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug * 945826 comment 0. */ class Proxy { public: - /* ES5 Harmony fundamental proxy traps. */ - static bool preventExtensions(JSContext *cx, HandleObject proxy); - static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc); - static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandleValue vp); + /* Standard internal methods. */ static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc); static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, @@ -40,21 +35,26 @@ class Proxy static bool ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props); static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); static bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props); - - /* ES5 Harmony derived proxy traps. */ + static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible); + static bool preventExtensions(JSContext *cx, HandleObject proxy); + static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); + static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); - static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp); static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, MutableHandleValue vp); - static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props); - static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp); - - /* Spidermonkey extensions. */ - static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible); static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args); static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args); + + /* SpiderMonkey extensions. */ + static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc); + static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandleValue vp); + static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); + static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props); + static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp); static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args); static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp); static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx); @@ -63,8 +63,6 @@ class Proxy static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g); static bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp); static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp); - static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); - static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable); static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id); diff --git a/js/src/proxy/ScriptedDirectProxyHandler.h b/js/src/proxy/ScriptedDirectProxyHandler.h index 2918a3ca3438..863b42dc0b3a 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.h +++ b/js/src/proxy/ScriptedDirectProxyHandler.h @@ -18,10 +18,7 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler { : DirectProxyHandler(&family) { } - /* ES5 Harmony fundamental proxy traps. */ - virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; + /* Standard internal methods. */ virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const MOZ_OVERRIDE; virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, @@ -30,16 +27,23 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler { AutoIdVector &props) const MOZ_OVERRIDE; virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; - - /* ES5 Harmony derived proxy traps. */ + virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE { - return BaseProxyHandler::hasOwn(cx, proxy, id, bp); - } virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE; virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + + /* SpiderMonkey extensions. */ + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE { + return BaseProxyHandler::hasOwn(cx, proxy, id, bp); + } + // Kick keys out to getOwnPropertyName and then filter. [[GetOwnProperty]] could potentially // change the enumerability of the target's properties. virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE { @@ -48,12 +52,6 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler { virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp) const MOZ_OVERRIDE; - /* ES6 Harmony traps */ - virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE; virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE { // For now we maintain the broken behavior that a scripted proxy is constructable if it's diff --git a/js/src/proxy/ScriptedIndirectProxyHandler.h b/js/src/proxy/ScriptedIndirectProxyHandler.h index ea0218b4f124..c77a9ce4e855 100644 --- a/js/src/proxy/ScriptedIndirectProxyHandler.h +++ b/js/src/proxy/ScriptedIndirectProxyHandler.h @@ -19,10 +19,7 @@ class ScriptedIndirectProxyHandler : public BaseProxyHandler : BaseProxyHandler(&family) { } - /* ES5 Harmony fundamental proxy traps. */ - virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; + /* Standard internal methods. */ virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const MOZ_OVERRIDE; virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, @@ -31,20 +28,21 @@ class ScriptedIndirectProxyHandler : public BaseProxyHandler AutoIdVector &props) const MOZ_OVERRIDE; virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; - - /* ES5 Harmony derived proxy traps. */ + virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE; virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, MutableHandleValue vp) const MOZ_OVERRIDE; + + /* SpiderMonkey extensions. */ + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp) const MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) const MOZ_OVERRIDE; virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE; diff --git a/js/src/vm/ProxyObject.h b/js/src/vm/ProxyObject.h index 1bd1b45ad974..f4a1ac48c646 100644 --- a/js/src/vm/ProxyObject.h +++ b/js/src/vm/ProxyObject.h @@ -91,7 +91,7 @@ class ProxyObject : public JSObject // friend api exposure. // Proxy classes are not allowed to have call or construct hooks directly. Their - // callability is instead decided by a trap call + // callability is instead decided by handler()->isCallable(). return clasp->isProxy() && (clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS) && clasp->trace == proxy_Trace && diff --git a/js/xpconnect/wrappers/AddonWrapper.h b/js/xpconnect/wrappers/AddonWrapper.h index 53de41c730b4..38a4573aedbf 100644 --- a/js/xpconnect/wrappers/AddonWrapper.h +++ b/js/xpconnect/wrappers/AddonWrapper.h @@ -24,21 +24,20 @@ class AddonWrapper : public Base { public: explicit MOZ_CONSTEXPR AddonWrapper(unsigned flags) : Base(flags) { } - virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, - JS::Handle id, - JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; - + virtual bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + JS::MutableHandle desc) const MOZ_OVERRIDE; + virtual bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const MOZ_OVERRIDE; virtual bool get(JSContext *cx, JS::Handle wrapper, JS::Handle receiver, JS::Handle id, JS::MutableHandle vp) const MOZ_OVERRIDE; virtual bool set(JSContext *cx, JS::HandleObject wrapper, JS::HandleObject receiver, JS::HandleId id, bool strict, JS::MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, - JS::MutableHandle desc) const MOZ_OVERRIDE; - virtual bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const MOZ_OVERRIDE; + virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, + JS::Handle id, + JS::MutableHandle desc) const MOZ_OVERRIDE; static const AddonWrapper singleton; }; diff --git a/js/xpconnect/wrappers/ChromeObjectWrapper.h b/js/xpconnect/wrappers/ChromeObjectWrapper.h index 7ce222f6ca15..ce07fa61d72e 100644 --- a/js/xpconnect/wrappers/ChromeObjectWrapper.h +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.h @@ -28,33 +28,30 @@ class ChromeObjectWrapper : public ChromeObjectWrapperBase public: MOZ_CONSTEXPR ChromeObjectWrapper() : ChromeObjectWrapperBase(0) {} - /* Custom traps. */ - virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, - JS::Handle id, - JS::MutableHandle desc) const MOZ_OVERRIDE; + virtual bool enter(JSContext *cx, JS::Handle wrapper, JS::Handle id, + js::Wrapper::Action act, bool *bp) const MOZ_OVERRIDE; + virtual bool defineProperty(JSContext *cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; - virtual bool set(JSContext *cx, JS::Handle wrapper, - JS::Handle receiver, JS::Handle id, - bool strict, JS::MutableHandle vp) const MOZ_OVERRIDE; - virtual bool has(JSContext *cx, JS::Handle wrapper, JS::Handle id, bool *bp) const MOZ_OVERRIDE; virtual bool get(JSContext *cx, JS::Handle wrapper, JS::Handle receiver, JS::Handle id, JS::MutableHandle vp) const MOZ_OVERRIDE; - + virtual bool set(JSContext *cx, JS::Handle wrapper, + JS::Handle receiver, JS::Handle id, + bool strict, JS::MutableHandle vp) const MOZ_OVERRIDE; virtual bool call(JSContext *cx, JS::Handle wrapper, const JS::CallArgs &args) const MOZ_OVERRIDE; virtual bool construct(JSContext *cx, JS::Handle wrapper, const JS::CallArgs &args) const MOZ_OVERRIDE; + virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, + JS::Handle id, + JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool objectClassIs(JS::Handle obj, js::ESClassValue classValue, JSContext *cx) const MOZ_OVERRIDE; - virtual bool enter(JSContext *cx, JS::Handle wrapper, JS::Handle id, - js::Wrapper::Action act, bool *bp) const MOZ_OVERRIDE; - // NB: One might think we'd need to implement enumerate(), keys(), iterate(), // and getPropertyKeys() here. However, ES5 built-in properties aren't // enumerable (and SpiderMonkey's implementation seems to match the spec diff --git a/js/xpconnect/wrappers/FilteringWrapper.h b/js/xpconnect/wrappers/FilteringWrapper.h index 2b5b72da9152..239c08f5c400 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.h +++ b/js/xpconnect/wrappers/FilteringWrapper.h @@ -25,9 +25,9 @@ class FilteringWrapper : public Base { public: MOZ_CONSTEXPR explicit FilteringWrapper(unsigned flags) : Base(flags) {} - virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, - JS::Handle id, - JS::MutableHandle desc) const MOZ_OVERRIDE; + virtual bool enter(JSContext *cx, JS::Handle wrapper, JS::Handle id, + js::Wrapper::Action act, bool *bp) const MOZ_OVERRIDE; + virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; @@ -35,6 +35,10 @@ class FilteringWrapper : public Base { JS::AutoIdVector &props) const MOZ_OVERRIDE; virtual bool enumerate(JSContext *cx, JS::Handle wrapper, JS::AutoIdVector &props) const MOZ_OVERRIDE; + + virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, + JS::Handle id, + JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool keys(JSContext *cx, JS::Handle wrapper, JS::AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, JS::Handle wrapper, unsigned flags, @@ -45,9 +49,6 @@ class FilteringWrapper : public Base { virtual bool defaultValue(JSContext *cx, JS::Handle obj, JSType hint, JS::MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool enter(JSContext *cx, JS::Handle wrapper, JS::Handle id, - js::Wrapper::Action act, bool *bp) const MOZ_OVERRIDE; - static const FilteringWrapper singleton; }; @@ -60,27 +61,24 @@ class CrossOriginXrayWrapper : public SecurityXrayDOM { public: explicit CrossOriginXrayWrapper(unsigned flags); - virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, - JS::Handle id, - JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; - - virtual bool ownPropertyKeys(JSContext *cx, JS::Handle wrapper, - JS::AutoIdVector &props) const MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; + virtual bool ownPropertyKeys(JSContext *cx, JS::Handle wrapper, + JS::AutoIdVector &props) const MOZ_OVERRIDE; virtual bool delete_(JSContext *cx, JS::Handle wrapper, JS::Handle id, bool *bp) const MOZ_OVERRIDE; - virtual bool enumerate(JSContext *cx, JS::Handle wrapper, JS::AutoIdVector &props) const MOZ_OVERRIDE; - virtual bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, JS::MutableHandleObject protop) const MOZ_OVERRIDE; + + virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, + JS::Handle id, + JS::MutableHandle desc) const MOZ_OVERRIDE; }; } diff --git a/js/xpconnect/wrappers/WaiveXrayWrapper.h b/js/xpconnect/wrappers/WaiveXrayWrapper.h index b62a7e5af040..816a53a05d80 100644 --- a/js/xpconnect/wrappers/WaiveXrayWrapper.h +++ b/js/xpconnect/wrappers/WaiveXrayWrapper.h @@ -17,28 +17,25 @@ class WaiveXrayWrapper : public js::CrossCompartmentWrapper { public: explicit MOZ_CONSTEXPR WaiveXrayWrapper(unsigned flags) : js::CrossCompartmentWrapper(flags) { } - virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, - JS::Handle id, - JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; + virtual bool getPrototypeOf(JSContext *cx, JS::Handle wrapper, + JS::MutableHandle protop) const MOZ_OVERRIDE; virtual bool get(JSContext *cx, JS::Handle wrapper, JS::Handle receiver, JS::Handle id, JS::MutableHandle vp) const MOZ_OVERRIDE; - virtual bool iterate(JSContext *cx, JS::Handle proxy, unsigned flags, - JS::MutableHandle vp) const MOZ_OVERRIDE; - - virtual bool call(JSContext *cx, JS::Handle wrapper, const JS::CallArgs &args) const MOZ_OVERRIDE; virtual bool construct(JSContext *cx, JS::Handle wrapper, const JS::CallArgs &args) const MOZ_OVERRIDE; + virtual bool iterate(JSContext *cx, JS::Handle proxy, unsigned flags, + JS::MutableHandle vp) const MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl, JS::CallArgs args) const MOZ_OVERRIDE; - - virtual bool getPrototypeOf(JSContext *cx, JS::Handle wrapper, - JS::MutableHandle protop) const MOZ_OVERRIDE; + virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, + JS::Handle id, + JS::MutableHandle desc) const MOZ_OVERRIDE; static const WaiveXrayWrapper singleton; }; diff --git a/js/xpconnect/wrappers/XrayWrapper.h b/js/xpconnect/wrappers/XrayWrapper.h index 1d5898ac4e4d..c41c377b0f6d 100644 --- a/js/xpconnect/wrappers/XrayWrapper.h +++ b/js/xpconnect/wrappers/XrayWrapper.h @@ -404,11 +404,7 @@ class XrayWrapper : public Base { : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG, Traits::HasPrototype) { }; - /* Fundamental proxy traps. */ - virtual bool isExtensible(JSContext *cx, JS::Handle wrapper, bool *extensible) const MOZ_OVERRIDE; - virtual bool preventExtensions(JSContext *cx, JS::Handle wrapper) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, JS::Handle id, - JS::MutableHandle desc) const MOZ_OVERRIDE; + /* Standard internal methods. */ virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool defineProperty(JSContext *cx, JS::Handle wrapper, JS::Handle id, @@ -418,14 +414,26 @@ class XrayWrapper : public Base { virtual bool delete_(JSContext *cx, JS::Handle wrapper, JS::Handle id, bool *bp) const MOZ_OVERRIDE; virtual bool enumerate(JSContext *cx, JS::Handle wrapper, JS::AutoIdVector &props) const MOZ_OVERRIDE; - - /* Derived proxy traps. */ + virtual bool isExtensible(JSContext *cx, JS::Handle wrapper, bool *extensible) const MOZ_OVERRIDE; + virtual bool preventExtensions(JSContext *cx, JS::Handle wrapper) const MOZ_OVERRIDE; + virtual bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, + JS::MutableHandleObject protop) const MOZ_OVERRIDE; + virtual bool setPrototypeOf(JSContext *cx, JS::HandleObject wrapper, + JS::HandleObject proto, bool *bp) const MOZ_OVERRIDE; + virtual bool has(JSContext *cx, JS::Handle wrapper, JS::Handle id, + bool *bp) const MOZ_OVERRIDE; virtual bool get(JSContext *cx, JS::Handle wrapper, JS::Handle receiver, JS::Handle id, JS::MutableHandle vp) const MOZ_OVERRIDE; virtual bool set(JSContext *cx, JS::Handle wrapper, JS::Handle receiver, JS::Handle id, bool strict, JS::MutableHandle vp) const MOZ_OVERRIDE; - virtual bool has(JSContext *cx, JS::Handle wrapper, JS::Handle id, - bool *bp) const MOZ_OVERRIDE; + virtual bool call(JSContext *cx, JS::Handle wrapper, + const JS::CallArgs &args) const MOZ_OVERRIDE; + virtual bool construct(JSContext *cx, JS::Handle wrapper, + const JS::CallArgs &args) const MOZ_OVERRIDE; + + /* SpiderMonkey extensions. */ + virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, JS::Handle id, + JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool hasOwn(JSContext *cx, JS::Handle wrapper, JS::Handle id, bool *bp) const MOZ_OVERRIDE; virtual bool keys(JSContext *cx, JS::Handle wrapper, @@ -433,21 +441,11 @@ class XrayWrapper : public Base { virtual bool iterate(JSContext *cx, JS::Handle wrapper, unsigned flags, JS::MutableHandle vp) const MOZ_OVERRIDE; - virtual bool call(JSContext *cx, JS::Handle wrapper, - const JS::CallArgs &args) const MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, JS::Handle wrapper, - const JS::CallArgs &args) const MOZ_OVERRIDE; - virtual const char *className(JSContext *cx, JS::HandleObject proxy) const MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, JS::HandleObject wrapper, JSType hint, JS::MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, - JS::MutableHandleObject protop) const MOZ_OVERRIDE; - virtual bool setPrototypeOf(JSContext *cx, JS::HandleObject wrapper, - JS::HandleObject proto, bool *bp) const MOZ_OVERRIDE; - static const XrayWrapper singleton; private: @@ -491,23 +489,24 @@ public: { } - virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle proxy, - JS::Handle id, - JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::Handle proxy, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; - // We just forward the derived traps to the BaseProxyHandler versions which - // implement them in terms of the fundamental traps. + // We just forward the high-level methods to the BaseProxyHandler versions + // which implement them in terms of lower-level methods. virtual bool has(JSContext *cx, JS::Handle proxy, JS::Handle id, bool *bp) const MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, JS::Handle proxy, JS::Handle id, - bool *bp) const MOZ_OVERRIDE; virtual bool get(JSContext *cx, JS::Handle proxy, JS::Handle receiver, JS::Handle id, JS::MutableHandle vp) const MOZ_OVERRIDE; virtual bool set(JSContext *cx, JS::Handle proxy, JS::Handle receiver, JS::Handle id, bool strict, JS::MutableHandle vp) const MOZ_OVERRIDE; + + virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle proxy, + JS::Handle id, + JS::MutableHandle desc) const MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, JS::Handle proxy, JS::Handle id, + bool *bp) const MOZ_OVERRIDE; virtual bool keys(JSContext *cx, JS::Handle proxy, JS::AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, JS::Handle proxy, unsigned flags, From ea94038f6d073db261c90c4ec086a895f0932141 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 8 Oct 2014 22:01:55 -0500 Subject: [PATCH 25/81] Bug 1081280 - Rename BaseProxyHandler::keys -> getOwnEnumerablePropertyKeys. r=efaust. --HG-- extra : rebase_source : 009323b607d53066b77191f9254918b61ea4d594 --- dom/base/nsGlobalWindow.cpp | 10 +++++----- dom/bindings/DOMJSProxyHandler.cpp | 8 ++++---- dom/bindings/DOMJSProxyHandler.h | 11 ++++++----- js/ipc/WrapperOwner.cpp | 10 ++++++---- js/ipc/WrapperOwner.h | 3 ++- js/src/jsiter.cpp | 2 +- js/src/jsproxy.h | 12 +++++++----- js/src/jswrapper.h | 3 ++- js/src/proxy/BaseProxyHandler.cpp | 5 +++-- js/src/proxy/CrossCompartmentWrapper.cpp | 5 +++-- js/src/proxy/DirectProxyHandler.cpp | 3 ++- js/src/proxy/Proxy.cpp | 8 ++++---- js/src/proxy/Proxy.h | 3 ++- js/src/proxy/ScriptedDirectProxyHandler.cpp | 3 ++- js/src/proxy/ScriptedDirectProxyHandler.h | 11 +++++++---- js/src/proxy/ScriptedIndirectProxyHandler.cpp | 5 +++-- js/src/proxy/ScriptedIndirectProxyHandler.h | 3 ++- js/xpconnect/src/Sandbox.cpp | 7 ++++--- js/xpconnect/wrappers/ChromeObjectWrapper.h | 15 ++++++++------- js/xpconnect/wrappers/FilteringWrapper.cpp | 7 ++++--- js/xpconnect/wrappers/FilteringWrapper.h | 4 ++-- js/xpconnect/wrappers/XrayWrapper.cpp | 7 ++++--- js/xpconnect/wrappers/XrayWrapper.h | 8 ++++---- 23 files changed, 87 insertions(+), 66 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index afce81803be1..50355c9702fd 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -642,8 +642,8 @@ public: const MOZ_OVERRIDE; virtual bool hasOwn(JSContext *cx, JS::Handle proxy, JS::Handle id, bool *bp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, JS::Handle proxy, - JS::AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle proxy, + JS::AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, JS::Handle proxy, unsigned flags, JS::MutableHandle vp) const MOZ_OVERRIDE; @@ -931,12 +931,12 @@ nsOuterWindowProxy::set(JSContext *cx, JS::Handle proxy, } bool -nsOuterWindowProxy::keys(JSContext *cx, JS::Handle proxy, - JS::AutoIdVector &props) const +nsOuterWindowProxy::getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle proxy, + JS::AutoIdVector &props) const { // BaseProxyHandler::keys seems to do what we want here: call // ownPropertyKeys and then filter out the non-enumerable properties. - return js::BaseProxyHandler::keys(cx, proxy, props); + return js::BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props); } bool diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index 66ef905360f7..a4a11139e26b 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -277,7 +277,7 @@ BaseDOMProxyHandler::enumerate(JSContext* cx, JS::Handle proxy, if (!JS_GetPrototype(cx, proxy, &proto)) { return false; } - return keys(cx, proxy, props) && + return getOwnEnumerablePropertyKeys(cx, proxy, props) && (!proto || js::GetPropertyKeys(cx, proto, 0, &props)); } @@ -303,9 +303,9 @@ BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx, } bool -BaseDOMProxyHandler::keys(JSContext* cx, - JS::Handle proxy, - JS::AutoIdVector& props) const +BaseDOMProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, + JS::Handle proxy, + JS::AutoIdVector& props) const { return ownPropNames(cx, proxy, JSITER_OWNONLY, props); } diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index ade0df8962f9..cc5324a7ca61 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -64,12 +64,13 @@ public: JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; - // We override keys() and implement it directly instead of using the - // default implementation, which would getOwnPropertyNames and then - // filter out the non-enumerable ones. This avoids doing + + // We override getOwnEnumerablePropertyKeys() and implement it directly + // instead of using the default implementation, which would call + // ownPropertyKeys and then filter out the non-enumerable ones. This avoids // unnecessary work during enumeration. - virtual bool keys(JSContext* cx, JS::Handle proxy, - JS::AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle proxy, + JS::AutoIdVector &props) const MOZ_OVERRIDE; bool watch(JSContext* cx, JS::Handle proxy, JS::Handle id, JS::Handle callable) const MOZ_OVERRIDE; diff --git a/js/ipc/WrapperOwner.cpp b/js/ipc/WrapperOwner.cpp index 2a27ca9ccac4..fb5767420acf 100644 --- a/js/ipc/WrapperOwner.cpp +++ b/js/ipc/WrapperOwner.cpp @@ -84,7 +84,8 @@ class CPOWProxyHandler : public BaseProxyHandler virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const MOZ_OVERRIDE; virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const MOZ_OVERRIDE; virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) const MOZ_OVERRIDE; virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue, @@ -462,13 +463,14 @@ WrapperOwner::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiv } bool -CPOWProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const +CPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const { - FORWARD(keys, (cx, proxy, props)); + FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props)); } bool -WrapperOwner::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) +WrapperOwner::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props) { return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props); } diff --git a/js/ipc/WrapperOwner.h b/js/ipc/WrapperOwner.h index 08bfd8f78a6f..a626ca1832a1 100644 --- a/js/ipc/WrapperOwner.h +++ b/js/ipc/WrapperOwner.h @@ -54,7 +54,8 @@ class WrapperOwner : public virtual JavaScriptShared bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::MutableHandle desc); bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp); - bool keys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props); + bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::HandleObject proxy, + JS::AutoIdVector &props); // We use "iterate" provided by the base class here. bool hasInstance(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool *bp); bool objectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue); diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 3dc61a75b5ab..d73ca4119966 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -300,7 +300,7 @@ Snapshot(JSContext *cx, JSObject *pobj_, unsigned flags, AutoIdVector *props) if (!Proxy::ownPropertyKeys(cx, pobj, proxyProps)) return false; } else { - if (!Proxy::keys(cx, pobj, proxyProps)) + if (!Proxy::getOwnEnumerablePropertyKeys(cx, pobj, proxyProps)) return false; } } else { diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 940ded54ba92..ae22a1b8d64e 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -167,8 +167,9 @@ class JS_FRIEND_API(BaseProxyHandler) * - When mHasPrototype is true, the engine never calls these methods: * getPropertyDescriptor, has, set, enumerate, iterate. Instead, for * these operations, it calls the "own" methods like - * getOwnPropertyDescriptor, hasOwn, defineProperty, keys, etc., and - * consults the prototype chain if needed. + * getOwnPropertyDescriptor, hasOwn, defineProperty, + * getOwnEnumerablePropertyKeys, etc., and consults the prototype chain + * if needed. * * - When mHasPrototype is true, the engine calls handler->get() only if * handler->hasOwn() says an own property exists on the proxy. If not, @@ -299,7 +300,8 @@ class JS_FRIEND_API(BaseProxyHandler) virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const = 0; virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const; - virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const; + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const; virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp) const; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) const; @@ -383,8 +385,8 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler MutableHandle desc) const MOZ_OVERRIDE; virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, HandleObject proxy, - AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp) const MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index dd7216752b99..86b91ba565b6 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -140,7 +140,8 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandle desc) const MOZ_OVERRIDE; virtual bool hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject wrapper, + AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, HandleObject wrapper, unsigned flags, MutableHandleValue vp) const MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index 3a374b58ff20..3466b7e23f32 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -165,7 +165,8 @@ js::SetPropertyIgnoringNamedGetter(JSContext *cx, const BaseProxyHandler *handle } bool -BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const +BaseProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const { assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); MOZ_ASSERT(props.length() == 0); @@ -204,7 +205,7 @@ BaseProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, AutoIdVector props(cx); if ((flags & JSITER_OWNONLY) - ? !keys(cx, proxy, props) + ? !getOwnEnumerablePropertyKeys(cx, proxy, props) : !enumerate(cx, proxy, props)) { return false; } diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp index 3659bb5efdf4..2db55d6b5d4d 100644 --- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -151,11 +151,12 @@ CrossCompartmentWrapper::set(JSContext *cx, HandleObject wrapper, HandleObject r } bool -CrossCompartmentWrapper::keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const +CrossCompartmentWrapper::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject wrapper, + AutoIdVector &props) const { PIERCE(cx, wrapper, NOTHING, - Wrapper::keys(cx, wrapper, props), + Wrapper::getOwnEnumerablePropertyKeys(cx, wrapper, props), NOTHING); } diff --git a/js/src/proxy/DirectProxyHandler.cpp b/js/src/proxy/DirectProxyHandler.cpp index 6dd64d908d7e..e6e028609c61 100644 --- a/js/src/proxy/DirectProxyHandler.cpp +++ b/js/src/proxy/DirectProxyHandler.cpp @@ -218,7 +218,8 @@ DirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver } bool -DirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const +DirectProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const { assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); RootedObject target(cx, proxy->as().target()); diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index d8b615b76057..a2003199e947 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -216,7 +216,7 @@ Proxy::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) return policy.returnValue(); if (!handler->hasPrototype()) return proxy->as().handler()->enumerate(cx, proxy, props); - if (!handler->keys(cx, proxy, props)) + if (!handler->getOwnEnumerablePropertyKeys(cx, proxy, props)) return false; AutoIdVector protoProps(cx); INVOKE_ON_PROTOTYPE(cx, handler, proxy, @@ -341,14 +341,14 @@ Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id } bool -Proxy::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) +Proxy::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props) { JS_CHECK_RECURSION(cx, return false); const BaseProxyHandler *handler = proxy->as().handler(); AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true); if (!policy.allowed()) return policy.returnValue(); - return handler->keys(cx, proxy, props); + return handler->getOwnEnumerablePropertyKeys(cx, proxy, props); } bool @@ -372,7 +372,7 @@ Proxy::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleV AutoIdVector props(cx); // The other Proxy::foo methods do the prototype-aware work for us here. if ((flags & JSITER_OWNONLY) - ? !Proxy::keys(cx, proxy, props) + ? !Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props) : !Proxy::enumerate(cx, proxy, props)) { return false; } diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h index 7e26163089b0..9f9262cedd41 100644 --- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -53,7 +53,8 @@ class Proxy static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp); static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); - static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props); + static bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props); static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp); static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args); static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp); diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp index 8fed01f208e6..31c74655cb13 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -172,7 +172,8 @@ ReportInvalidTrapResult(JSContext *cx, JSObject *proxy, JSAtom *atom) js::NullPtr(), bytes.ptr()); } -// This function is shared between ownPropertyKeys, enumerate, and keys +// This function is shared between ownPropertyKeys, enumerate, and +// getOwnEnumerablePropertyKeys. static bool ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleValue v, AutoIdVector &props, unsigned flags, JSAtom *trapName_) diff --git a/js/src/proxy/ScriptedDirectProxyHandler.h b/js/src/proxy/ScriptedDirectProxyHandler.h index 863b42dc0b3a..9ad0418229a4 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.h +++ b/js/src/proxy/ScriptedDirectProxyHandler.h @@ -44,10 +44,13 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler { return BaseProxyHandler::hasOwn(cx, proxy, id, bp); } - // Kick keys out to getOwnPropertyName and then filter. [[GetOwnProperty]] could potentially - // change the enumerability of the target's properties. - virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE { - return BaseProxyHandler::keys(cx, proxy, props); + + // Kick getOwnEnumerablePropertyKeys out to ownPropertyKeys and then + // filter. [[GetOwnProperty]] could potentially change the enumerability of + // the target's properties. + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const MOZ_OVERRIDE { + return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props); } virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp) const MOZ_OVERRIDE; diff --git a/js/src/proxy/ScriptedIndirectProxyHandler.cpp b/js/src/proxy/ScriptedIndirectProxyHandler.cpp index c71204618610..cc3c4aeda1c7 100644 --- a/js/src/proxy/ScriptedIndirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedIndirectProxyHandler.cpp @@ -302,14 +302,15 @@ ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObjec } bool -ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const +ScriptedIndirectProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const { RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); RootedValue value(cx); if (!GetDerivedTrap(cx, handler, cx->names().keys, &value)) return false; if (!IsCallable(value)) - return BaseProxyHandler::keys(cx, proxy, props); + return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props); return Trap(cx, handler, value, 0, nullptr, &value) && ArrayToIdVector(cx, value, props); } diff --git a/js/src/proxy/ScriptedIndirectProxyHandler.h b/js/src/proxy/ScriptedIndirectProxyHandler.h index c77a9ce4e855..1f51499e48e1 100644 --- a/js/src/proxy/ScriptedIndirectProxyHandler.h +++ b/js/src/proxy/ScriptedIndirectProxyHandler.h @@ -40,7 +40,8 @@ class ScriptedIndirectProxyHandler : public BaseProxyHandler virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const MOZ_OVERRIDE; virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp) const MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 4540045201eb..e33736929e2a 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -715,10 +715,11 @@ xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle proxy, } bool -xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle proxy, - AutoIdVector &props) const +xpc::SandboxProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, + JS::Handle proxy, + AutoIdVector &props) const { - return BaseProxyHandler::keys(cx, proxy, props); + return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props); } bool diff --git a/js/xpconnect/wrappers/ChromeObjectWrapper.h b/js/xpconnect/wrappers/ChromeObjectWrapper.h index ce07fa61d72e..6dc4d06d72c8 100644 --- a/js/xpconnect/wrappers/ChromeObjectWrapper.h +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.h @@ -52,13 +52,14 @@ class ChromeObjectWrapper : public ChromeObjectWrapperBase virtual bool objectClassIs(JS::Handle obj, js::ESClassValue classValue, JSContext *cx) const MOZ_OVERRIDE; - // NB: One might think we'd need to implement enumerate(), keys(), iterate(), - // and getPropertyKeys() here. However, ES5 built-in properties aren't - // enumerable (and SpiderMonkey's implementation seems to match the spec - // modulo Error.prototype.fileName and Error.prototype.lineNumber). Since - // we're only remapping the prototypes of standard objects, there would - // never be anything more to enumerate up the prototype chain. So we can - // actually skip these. + // NB: One might think we'd need to implement enumerate(), + // getOwnEnumerablePropertyKeys(), iterate(), and ownPropertyKeys() + // here. However, ES5 built-in properties aren't enumerable (and + // SpiderMonkey's implementation seems to match the spec modulo + // Error.prototype.fileName and Error.prototype.lineNumber). Since we're + // only remapping the prototypes of standard objects, there would never be + // anything more to enumerate up the prototype chain. So we can actually + // skip these. static const ChromeObjectWrapper singleton; }; diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index e357e02af75d..a40572fb56f9 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -112,11 +112,12 @@ FilteringWrapper::enumerate(JSContext *cx, HandleObject wrapper, template bool -FilteringWrapper::keys(JSContext *cx, HandleObject wrapper, - AutoIdVector &props) const +FilteringWrapper::getOwnEnumerablePropertyKeys(JSContext *cx, + HandleObject wrapper, + AutoIdVector &props) const { assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); - return Base::keys(cx, wrapper, props) && + return Base::getOwnEnumerablePropertyKeys(cx, wrapper, props) && Filter(cx, wrapper, props); } diff --git a/js/xpconnect/wrappers/FilteringWrapper.h b/js/xpconnect/wrappers/FilteringWrapper.h index 239c08f5c400..c6910e3b8d59 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.h +++ b/js/xpconnect/wrappers/FilteringWrapper.h @@ -39,8 +39,8 @@ class FilteringWrapper : public Base { virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, JS::Handle wrapper, - JS::AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle wrapper, + JS::AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, JS::Handle wrapper, unsigned flags, JS::MutableHandle vp) const MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl, diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index c72effa4e175..fac44a384685 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -2072,11 +2072,12 @@ XrayWrapper::hasOwn(JSContext *cx, HandleObject wrapper, template bool -XrayWrapper::keys(JSContext *cx, HandleObject wrapper, - AutoIdVector &props) const +XrayWrapper::getOwnEnumerablePropertyKeys(JSContext *cx, + HandleObject wrapper, + AutoIdVector &props) const { // Skip our Base if it isn't already ProxyHandler. - return js::BaseProxyHandler::keys(cx, wrapper, props); + return js::BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, wrapper, props); } template diff --git a/js/xpconnect/wrappers/XrayWrapper.h b/js/xpconnect/wrappers/XrayWrapper.h index c41c377b0f6d..c015ec18846a 100644 --- a/js/xpconnect/wrappers/XrayWrapper.h +++ b/js/xpconnect/wrappers/XrayWrapper.h @@ -436,8 +436,8 @@ class XrayWrapper : public Base { JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool hasOwn(JSContext *cx, JS::Handle wrapper, JS::Handle id, bool *bp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, JS::Handle wrapper, - JS::AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle wrapper, + JS::AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, JS::Handle wrapper, unsigned flags, JS::MutableHandle vp) const MOZ_OVERRIDE; @@ -507,8 +507,8 @@ public: JS::MutableHandle desc) const MOZ_OVERRIDE; virtual bool hasOwn(JSContext *cx, JS::Handle proxy, JS::Handle id, bool *bp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, JS::Handle proxy, - JS::AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle proxy, + JS::AutoIdVector &props) const MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, JS::Handle proxy, unsigned flags, JS::MutableHandle vp) const MOZ_OVERRIDE; }; From 73007e478423ff5f81b9683bdcce9c1f4bca8ac2 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Mon, 13 Oct 2014 14:20:13 -0500 Subject: [PATCH 26/81] Bug 1083204 - Handlify js::GetPropertyKeys and Snapshot. r=efaust. --HG-- extra : rebase_source : 8c8da9cd1612c4ed7aa1319e7e68f41f9c79a9fc --- js/src/jsfriendapi.h | 2 +- js/src/jsiter.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 086cf1309ec7..70a757d657c6 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -971,7 +971,7 @@ CopyFlatStringChars(char16_t *dest, JSFlatString *s, size_t len) } JS_FRIEND_API(bool) -GetPropertyKeys(JSContext *cx, JSObject *obj, unsigned flags, JS::AutoIdVector *props); +GetPropertyKeys(JSContext *cx, JS::HandleObject obj, unsigned flags, JS::AutoIdVector *props); JS_FRIEND_API(bool) AppendUnique(JSContext *cx, JS::AutoIdVector &base, JS::AutoIdVector &others); diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index d73ca4119966..75dfe35ab326 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -271,7 +271,7 @@ struct SortComparatorIds #endif /* JS_MORE_DETERMINISTIC */ static bool -Snapshot(JSContext *cx, JSObject *pobj_, unsigned flags, AutoIdVector *props) +Snapshot(JSContext *cx, HandleObject pobj_, unsigned flags, AutoIdVector *props) { IdSet ht(cx); if (!ht.init(32)) @@ -396,7 +396,7 @@ js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap) } JS_FRIEND_API(bool) -js::GetPropertyKeys(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props) +js::GetPropertyKeys(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector *props) { return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY), From f2a45bdf0d4fec4f59c9df3484e82029f33afc0d Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Thu, 16 Oct 2014 15:46:41 -0400 Subject: [PATCH 27/81] Bug 1013004 - Allow more time for ANR reports; r=snorp --- mobile/android/base/tests/testANRReporter.java | 11 +++++++++-- widget/android/AndroidJNI.cpp | 3 +-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/mobile/android/base/tests/testANRReporter.java b/mobile/android/base/tests/testANRReporter.java index 5856f9fdbe32..e897a93e448d 100644 --- a/mobile/android/base/tests/testANRReporter.java +++ b/mobile/android/base/tests/testANRReporter.java @@ -20,7 +20,7 @@ public class testANRReporter extends BaseTest { private static final String ANR_ACTION = "android.intent.action.ANR"; private static final String PING_DIR = "saved-telemetry-pings"; - private static final int WAIT_FOR_PING_TIMEOUT = 10000; + private static final int WAIT_FOR_PING_TIMEOUT = 60000; private static final String ANR_PATH = "/data/anr/traces.txt"; private static final String SAMPLE_ANR = "----- pid 1 at 2014-01-15 18:55:51 -----\n" @@ -159,10 +159,17 @@ public class testANRReporter extends BaseTest { mAsserter.info("Triggering second ANR", null); testContext.sendBroadcast(new Intent(anrIntent)); - mAsserter.info("Waiting for ping", null); waitForCondition(new Condition() { @Override public boolean isSatisfied() { + mAsserter.info("Waiting for ping", null); + + try { + // Sleep to allow the ANR reporter thread time to process the ANR. + Thread.sleep(1000); + } catch (final InterruptedException e) { + } + final File[] newFiles = pingDir.listFiles(); if (newFiles == null || newFiles.length == 0) { // Keep waiting. diff --git a/widget/android/AndroidJNI.cpp b/widget/android/AndroidJNI.cpp index 58702163f806..75accb2524e6 100644 --- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include "nsAppShell.h" #include "nsWindow.h" @@ -1029,7 +1028,7 @@ Java_org_mozilla_gecko_ANRReporter_getNativeStack(JNIEnv* jenv, jclass) if (PR_IntervalNow() - startTime >= timeout) { return nullptr; } - sched_yield(); + usleep(100000ul); // Sleep for 100ms profile = ProfilePtr(profiler_get_profile()); } From e37e3a23b5be2e4244bada4498df9eb54f744639 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 16 Oct 2014 13:04:35 -0700 Subject: [PATCH 28/81] Backout 2cf543c271dc (wrong patch, no bug, r=me). --HG-- extra : rebase_source : 7a88c22454dcfeb8ac848c5807ead7679a6e8d9f --- dom/canvas/WebGL2ContextTextures.cpp | 2 -- dom/canvas/WebGLContextGL.cpp | 15 +--------- dom/canvas/WebGLTexture.cpp | 41 +++++++++++++++------------- dom/canvas/WebGLTexture.h | 14 ---------- 4 files changed, 23 insertions(+), 49 deletions(-) diff --git a/dom/canvas/WebGL2ContextTextures.cpp b/dom/canvas/WebGL2ContextTextures.cpp index 21028d09e5b6..e134ed100c28 100644 --- a/dom/canvas/WebGL2ContextTextures.cpp +++ b/dom/canvas/WebGL2ContextTextures.cpp @@ -351,8 +351,6 @@ WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname) { switch (pname) { case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: - case LOCAL_GL_TEXTURE_BASE_LEVEL: - case LOCAL_GL_TEXTURE_MAX_LEVEL: { GLint i = 0; gl->fGetTexParameteriv(target.get(), pname, &i); diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp index 74e67e38a489..cf8be3fed833 100644 --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -922,7 +922,7 @@ WebGLContext::GenerateMipmap(GLenum rawTarget) const TexImageTarget imageTarget = (target == LOCAL_GL_TEXTURE_2D) ? LOCAL_GL_TEXTURE_2D : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; - if (!tex->HasImageInfoAt(imageTarget, tex->GetBaseMipmapLevel())) + if (!tex->HasImageInfoAt(imageTarget, 0)) { return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined."); } @@ -1510,19 +1510,6 @@ void WebGLContext::TexParameter_base(GLenum rawTarget, GLenum pname, bool paramValueInvalid = false; switch (pname) { - case LOCAL_GL_TEXTURE_BASE_LEVEL: - case LOCAL_GL_TEXTURE_MAX_LEVEL: - if (!IsWebGL2()) - return ErrorInvalidEnumInfo("texParameter: pname", pname); - if (intParam < 0) { - paramValueInvalid = true; - break; - } - if (pname == LOCAL_GL_TEXTURE_BASE_LEVEL) - tex->SetBaseMipmapLevel(intParam); - else - tex->SetMaxMipmapLevel(intParam); - break; case LOCAL_GL_TEXTURE_MIN_FILTER: switch (intParam) { case LOCAL_GL_NEAREST: diff --git a/dom/canvas/WebGLTexture.cpp b/dom/canvas/WebGLTexture.cpp index e14bbc4d7c7a..45914f8e9794 100644 --- a/dom/canvas/WebGLTexture.cpp +++ b/dom/canvas/WebGLTexture.cpp @@ -33,8 +33,6 @@ WebGLTexture::WebGLTexture(WebGLContext *context) , mMaxLevelWithCustomImages(0) , mHaveGeneratedMipmap(false) , mImmutable(false) - , mBaseMipmapLevel(0) - , mMaxMipmapLevel(1000) , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture) { mContext->MakeContextCurrent(); @@ -64,8 +62,22 @@ WebGLTexture::MemoryUsage() const { return 0; size_t result = 0; for(size_t face = 0; face < mFacesCount; face++) { - for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++) - result += ImageInfoAtFace(face, level).MemoryUsage(); + if (mHaveGeneratedMipmap) { + size_t level0MemoryUsage = ImageInfoAtFace(face, 0).MemoryUsage(); + // Each mipmap level is 1/(2^d) the size of the previous level, + // where d is 2 or 3 depending on whether the images are 2D or 3D + // 1 + x + x^2 + ... = 1/(1-x) + // for x = 1/(2^2), we get 1/(1-1/4) = 4/3 + // for x = 1/(2^3), we get 1/(1-1/8) = 8/7 + size_t allLevelsMemoryUsage = + mTarget == LOCAL_GL_TEXTURE_3D + ? level0MemoryUsage * 8 / 7 + : level0MemoryUsage * 4 / 3; + result += allLevelsMemoryUsage; + } else { + for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++) + result += ImageInfoAtFace(face, level).MemoryUsage(); + } } return result; } @@ -76,24 +88,15 @@ WebGLTexture::DoesMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImage if (mHaveGeneratedMipmap) return true; - if (GetMaxMipmapLevel() < GetBaseMipmapLevel()) - return false; - // We want a copy here so we can modify it temporarily. - ImageInfo expected = ImageInfoAt(texImageTarget, GetBaseMipmapLevel()); + ImageInfo expected = ImageInfoAt(texImageTarget, 0); // checks if custom level>0 images are all defined up to the highest level defined // and have the expected dimensions - for (size_t level = GetBaseMipmapLevel(); level <= GetMaxMipmapLevel(); ++level) { + for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { const ImageInfo& actual = ImageInfoAt(texImageTarget, level); if (actual != expected) return false; - - // Check the raw value here, not the clamped one, since we don't want - // to terminate early if there aren't enough levels defined. - if (level == mMaxMipmapLevel) - return true; - expected.mWidth = std::max(1, expected.mWidth / 2); expected.mHeight = std::max(1, expected.mHeight / 2); expected.mDepth = std::max(1, expected.mDepth / 2); @@ -182,7 +185,7 @@ WebGLTexture::SetCustomMipmap() { // since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info, // and are power-of-two. - ImageInfo imageInfo = ImageInfoAtFace(0, GetBaseMipmapLevel()); + ImageInfo imageInfo = ImageInfoAtFace(0, 0); NS_ASSERTION(mContext->IsWebGL2() || imageInfo.IsPowerOfTwo(), "this texture is NPOT, so how could GenerateMipmap() ever accept it?"); @@ -195,7 +198,7 @@ WebGLTexture::SetCustomMipmap() { EnsureMaxLevelWithCustomImagesAtLeast(maxLevel); - for (size_t level = GetBaseMipmapLevel() + 1; level <= GetMaxMipmapLevel(); ++level) { + for (size_t level = 1; level <= maxLevel; ++level) { imageInfo.mWidth = std::max(imageInfo.mWidth / 2, 1); imageInfo.mHeight = std::max(imageInfo.mHeight / 2, 1); imageInfo.mDepth = std::max(imageInfo.mDepth / 2, 1); @@ -220,7 +223,7 @@ WebGLTexture::IsMipmapComplete() const { MOZ_ASSERT(mTarget == LOCAL_GL_TEXTURE_2D || mTarget == LOCAL_GL_TEXTURE_3D); - if (!ImageInfoAtFace(0, GetBaseMipmapLevel()).IsPositive()) + if (!ImageInfoAtFace(0, 0).IsPositive()) return false; if (mHaveGeneratedMipmap) return true; @@ -259,7 +262,7 @@ WebGLTexture::ResolvedFakeBlackStatus() { // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec. for (size_t face = 0; face < mFacesCount; ++face) { - if (ImageInfoAtFace(face, GetBaseMipmapLevel()).mImageDataStatus == WebGLImageDataStatus::NoImageData) { + if (ImageInfoAtFace(face, 0).mImageDataStatus == WebGLImageDataStatus::NoImageData) { // In case of undefined texture image, we don't print any message because this is a very common // and often legitimate case (asynchronous texture loading). mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; diff --git a/dom/canvas/WebGLTexture.h b/dom/canvas/WebGLTexture.h index 134f25094677..b7cca286be05 100644 --- a/dom/canvas/WebGLTexture.h +++ b/dom/canvas/WebGLTexture.h @@ -213,9 +213,6 @@ protected: bool mHaveGeneratedMipmap; // set by generateMipmap bool mImmutable; // set by texStorage* - size_t mBaseMipmapLevel; // set by texParameter (defaults to 0) - size_t mMaxMipmapLevel; // set by texParameter (defaults to 1000) - WebGLTextureFakeBlackStatus mFakeBlackStatus; void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) { @@ -286,17 +283,6 @@ public: bool IsImmutable() const { return mImmutable; } void SetImmutable() { mImmutable = true; } - void SetBaseMipmapLevel(unsigned level) { mBaseMipmapLevel = level; } - void SetMaxMipmapLevel(unsigned level) { mMaxMipmapLevel = level; } - size_t GetBaseMipmapLevel() const { - // Clamp to [0, levels - 1] - return std::min(mBaseMipmapLevel, mMaxLevelWithCustomImages); - } - size_t GetMaxMipmapLevel() const { - // Clamp to [base, levels - 1] - return std::min(mMaxMipmapLevel, mMaxLevelWithCustomImages); - } - size_t MaxLevelWithCustomImages() const { return mMaxLevelWithCustomImages; } // Returns the current fake-black-status, except if it was Unknown, From 785bc4dd68f999857f6ead3ae222ae3010273f3e Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 16 Oct 2014 13:07:01 -0700 Subject: [PATCH 29/81] WebGL2: support TEXTURE_BASE_LEVEL and TEXTURE_MAX_LEVEL (bug 1080957, r=jgilbert). --- dom/canvas/WebGL2ContextTextures.cpp | 2 + dom/canvas/WebGLContextGL.cpp | 19 ++++- dom/canvas/WebGLTexture.cpp | 101 +++++++++++++++------------ dom/canvas/WebGLTexture.h | 21 ++++++ 4 files changed, 99 insertions(+), 44 deletions(-) diff --git a/dom/canvas/WebGL2ContextTextures.cpp b/dom/canvas/WebGL2ContextTextures.cpp index e134ed100c28..21028d09e5b6 100644 --- a/dom/canvas/WebGL2ContextTextures.cpp +++ b/dom/canvas/WebGL2ContextTextures.cpp @@ -351,6 +351,8 @@ WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname) { switch (pname) { case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: + case LOCAL_GL_TEXTURE_BASE_LEVEL: + case LOCAL_GL_TEXTURE_MAX_LEVEL: { GLint i = 0; gl->fGetTexParameteriv(target.get(), pname, &i); diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp index cf8be3fed833..be3cd4901871 100644 --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -922,7 +922,11 @@ WebGLContext::GenerateMipmap(GLenum rawTarget) const TexImageTarget imageTarget = (target == LOCAL_GL_TEXTURE_2D) ? LOCAL_GL_TEXTURE_2D : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; - if (!tex->HasImageInfoAt(imageTarget, 0)) + if (!tex->IsMipmapRangeValid()) + { + return ErrorInvalidOperation("generateMipmap: Texture does not have a valid mipmap range."); + } + if (!tex->HasImageInfoAt(imageTarget, tex->EffectiveBaseMipmapLevel())) { return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined."); } @@ -1510,6 +1514,19 @@ void WebGLContext::TexParameter_base(GLenum rawTarget, GLenum pname, bool paramValueInvalid = false; switch (pname) { + case LOCAL_GL_TEXTURE_BASE_LEVEL: + case LOCAL_GL_TEXTURE_MAX_LEVEL: + if (!IsWebGL2()) + return ErrorInvalidEnumInfo("texParameter: pname", pname); + if (intParam < 0) { + paramValueInvalid = true; + break; + } + if (pname == LOCAL_GL_TEXTURE_BASE_LEVEL) + tex->SetBaseMipmapLevel(intParam); + else + tex->SetMaxMipmapLevel(intParam); + break; case LOCAL_GL_TEXTURE_MIN_FILTER: switch (intParam) { case LOCAL_GL_NEAREST: diff --git a/dom/canvas/WebGLTexture.cpp b/dom/canvas/WebGLTexture.cpp index 45914f8e9794..1f5e6226b629 100644 --- a/dom/canvas/WebGLTexture.cpp +++ b/dom/canvas/WebGLTexture.cpp @@ -14,6 +14,7 @@ #include "WebGLTexelConversions.h" #include +#include "mozilla/MathAlgorithms.h" using namespace mozilla; @@ -33,6 +34,8 @@ WebGLTexture::WebGLTexture(WebGLContext *context) , mMaxLevelWithCustomImages(0) , mHaveGeneratedMipmap(false) , mImmutable(false) + , mBaseMipmapLevel(0) + , mMaxMipmapLevel(1000) , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture) { mContext->MakeContextCurrent(); @@ -62,41 +65,50 @@ WebGLTexture::MemoryUsage() const { return 0; size_t result = 0; for(size_t face = 0; face < mFacesCount; face++) { - if (mHaveGeneratedMipmap) { - size_t level0MemoryUsage = ImageInfoAtFace(face, 0).MemoryUsage(); - // Each mipmap level is 1/(2^d) the size of the previous level, - // where d is 2 or 3 depending on whether the images are 2D or 3D - // 1 + x + x^2 + ... = 1/(1-x) - // for x = 1/(2^2), we get 1/(1-1/4) = 4/3 - // for x = 1/(2^3), we get 1/(1-1/8) = 8/7 - size_t allLevelsMemoryUsage = - mTarget == LOCAL_GL_TEXTURE_3D - ? level0MemoryUsage * 8 / 7 - : level0MemoryUsage * 4 / 3; - result += allLevelsMemoryUsage; - } else { for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++) result += ImageInfoAtFace(face, level).MemoryUsage(); } - } return result; } +static inline size_t +MipmapLevelsForSize(const WebGLTexture::ImageInfo &info) +{ + GLsizei size = std::max(std::max(info.Width(), info.Height()), info.Depth()); + + // Find floor(log2(size)). (ES 3.0.4, 3.8 - Mipmapping). + return mozilla::FloorLog2(size); +} + bool WebGLTexture::DoesMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImageTarget) const { + // We could not have generated a mipmap if the base image wasn't defined. if (mHaveGeneratedMipmap) return true; - // We want a copy here so we can modify it temporarily. - ImageInfo expected = ImageInfoAt(texImageTarget, 0); + if (!IsMipmapRangeValid()) + return false; - // checks if custom level>0 images are all defined up to the highest level defined - // and have the expected dimensions - for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { + // We want a copy here so we can modify it temporarily. + ImageInfo expected = ImageInfoAt(texImageTarget, EffectiveBaseMipmapLevel()); + if (!expected.IsPositive()) + return false; + + // If Level{max} is > mMaxLevelWithCustomImages, then check if we are + // missing any image levels. + if (mMaxMipmapLevel > mMaxLevelWithCustomImages) { + if (MipmapLevelsForSize(expected) > mMaxLevelWithCustomImages) + return false; + } + + // Checks if custom images are all defined up to the highest level and + // have the expected dimensions. + for (size_t level = EffectiveBaseMipmapLevel(); level <= EffectiveMaxMipmapLevel(); ++level) { const ImageInfo& actual = ImageInfoAt(texImageTarget, level); if (actual != expected) return false; + expected.mWidth = std::max(1, expected.mWidth / 2); expected.mHeight = std::max(1, expected.mHeight / 2); expected.mDepth = std::max(1, expected.mDepth / 2); @@ -111,8 +123,7 @@ WebGLTexture::DoesMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImage } } - // if we're here, we've exhausted all levels without finding a 1x1 image - return false; + return true; } void @@ -180,25 +191,19 @@ WebGLTexture::SetGeneratedMipmap() { void WebGLTexture::SetCustomMipmap() { if (mHaveGeneratedMipmap) { - // if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode, - // we need to compute now all the mipmap image info. + if (!IsMipmapRangeValid()) + return; - // since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info, - // and are power-of-two. - ImageInfo imageInfo = ImageInfoAtFace(0, 0); + // If we were in GeneratedMipmap mode and are now switching to CustomMipmap mode, + // we now need to compute all the mipmap image info. + ImageInfo imageInfo = ImageInfoAtFace(0, EffectiveBaseMipmapLevel()); NS_ASSERTION(mContext->IsWebGL2() || imageInfo.IsPowerOfTwo(), "this texture is NPOT, so how could GenerateMipmap() ever accept it?"); - GLsizei size = std::max(std::max(imageInfo.mWidth, imageInfo.mHeight), imageInfo.mDepth); + size_t maxLevel = MipmapLevelsForSize(imageInfo); + EnsureMaxLevelWithCustomImagesAtLeast(EffectiveBaseMipmapLevel() + maxLevel); - // Find floor(log2(size)). (ES 3.0.4, 3.8 - Mipmapping). - size_t maxLevel = 0; - for (GLsizei n = size; n > 1; n >>= 1) - ++maxLevel; - - EnsureMaxLevelWithCustomImagesAtLeast(maxLevel); - - for (size_t level = 1; level <= maxLevel; ++level) { + for (size_t level = EffectiveBaseMipmapLevel() + 1; level <= EffectiveMaxMipmapLevel(); ++level) { imageInfo.mWidth = std::max(imageInfo.mWidth / 2, 1); imageInfo.mHeight = std::max(imageInfo.mHeight / 2, 1); imageInfo.mDepth = std::max(imageInfo.mDepth / 2, 1); @@ -222,11 +227,6 @@ bool WebGLTexture::IsMipmapComplete() const { MOZ_ASSERT(mTarget == LOCAL_GL_TEXTURE_2D || mTarget == LOCAL_GL_TEXTURE_3D); - - if (!ImageInfoAtFace(0, 0).IsPositive()) - return false; - if (mHaveGeneratedMipmap) - return true; return DoesMipmapHaveAllLevelsConsistentlyDefined(LOCAL_GL_TEXTURE_2D); } @@ -252,6 +252,17 @@ WebGLTexture::IsMipmapCubeComplete() const { return true; } +bool +WebGLTexture::IsMipmapRangeValid() const +{ + // In ES3, if a texture is immutable, the mipmap levels are clamped. + if (IsImmutable()) + return true; + if (mBaseMipmapLevel > std::min(mMaxLevelWithCustomImages, mMaxMipmapLevel)) + return false; + return true; +} + WebGLTextureFakeBlackStatus WebGLTexture::ResolvedFakeBlackStatus() { if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) { @@ -259,10 +270,14 @@ WebGLTexture::ResolvedFakeBlackStatus() { } // Determine if the texture needs to be faked as a black texture. - // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec. - + // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec, and 3.8.13 in + // the OpenGL ES 3.0.4 spec. + if (!IsMipmapRangeValid()) { + mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; + return mFakeBlackStatus; + } for (size_t face = 0; face < mFacesCount; ++face) { - if (ImageInfoAtFace(face, 0).mImageDataStatus == WebGLImageDataStatus::NoImageData) { + if (ImageInfoAtFace(face, EffectiveBaseMipmapLevel()).mImageDataStatus == WebGLImageDataStatus::NoImageData) { // In case of undefined texture image, we don't print any message because this is a very common // and often legitimate case (asynchronous texture loading). mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; diff --git a/dom/canvas/WebGLTexture.h b/dom/canvas/WebGLTexture.h index b7cca286be05..0769fbf68206 100644 --- a/dom/canvas/WebGLTexture.h +++ b/dom/canvas/WebGLTexture.h @@ -17,6 +17,7 @@ #include "mozilla/LinkedList.h" #include "mozilla/Assertions.h" #include +#include "nsAlgorithm.h" namespace mozilla { @@ -213,6 +214,9 @@ protected: bool mHaveGeneratedMipmap; // set by generateMipmap bool mImmutable; // set by texStorage* + size_t mBaseMipmapLevel; // set by texParameter (defaults to 0) + size_t mMaxMipmapLevel; // set by texParameter (defaults to 1000) + WebGLTextureFakeBlackStatus mFakeBlackStatus; void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) { @@ -283,6 +287,23 @@ public: bool IsImmutable() const { return mImmutable; } void SetImmutable() { mImmutable = true; } + void SetBaseMipmapLevel(size_t level) { mBaseMipmapLevel = level; } + void SetMaxMipmapLevel(size_t level) { mMaxMipmapLevel = level; } + + // Clamping (from ES 3.0.4, section 3.8 - Texturing). When not immutable, + // the ranges must be guarded. + size_t EffectiveBaseMipmapLevel() const { + if (IsImmutable()) + return std::min(mBaseMipmapLevel, mMaxLevelWithCustomImages); + return mBaseMipmapLevel; + } + size_t EffectiveMaxMipmapLevel() const { + if (IsImmutable()) + return mozilla::clamped(mMaxMipmapLevel, EffectiveBaseMipmapLevel(), mMaxLevelWithCustomImages); + return std::min(mMaxMipmapLevel, mMaxLevelWithCustomImages); + } + bool IsMipmapRangeValid() const; + size_t MaxLevelWithCustomImages() const { return mMaxLevelWithCustomImages; } // Returns the current fake-black-status, except if it was Unknown, From 60a12fdf281bca7fad86997c2214f186f3ff670e Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Thu, 16 Oct 2014 13:37:37 -0700 Subject: [PATCH 30/81] Backed out 4 changesets (bug 1064333) for XPCShell bustage Backed out changeset 378314f2892c (bug 1064333) Backed out changeset ccb8a2ac9746 (bug 1064333) Backed out changeset 3466ed3b9b61 (bug 1064333) Backed out changeset 553c113c8f4e (bug 1064333) --- services/common/utils.js | 5 +- .../datareporting/DataReportingService.js | 116 ------------------ services/datareporting/policy.jsm | 3 +- .../tests/xpcshell/test_client_id.js | 86 ------------- .../datareporting/tests/xpcshell/xpcshell.ini | 1 - services/healthreport/healthreporter.jsm | 47 ++++--- .../healthreport/modules-testing/utils.jsm | 14 +-- .../tests/xpcshell/test_healthreporter.js | 47 ++++--- .../tests/xpcshell/test_provider_appinfo.js | 15 +-- .../components/telemetry/TelemetryPing.jsm | 22 +--- .../tests/unit/test_TelemetryPing.js | 35 ------ .../tests/unit/test_TelemetryPingBuildID.js | 16 +-- .../tests/unit/test_TelemetrySendOldPings.js | 12 -- 13 files changed, 72 insertions(+), 347 deletions(-) delete mode 100644 services/datareporting/tests/xpcshell/test_client_id.js diff --git a/services/common/utils.js b/services/common/utils.js index 8ad3c90d1d21..6d7b30b04e8a 100644 --- a/services/common/utils.js +++ b/services/common/utils.js @@ -398,8 +398,9 @@ this.CommonUtils = { * @return a promise, as produced by OS.File.writeAtomic. */ writeJSON: function(contents, path) { - let data = JSON.stringify(contents); - return OS.File.writeAtomic(path, data, {encoding: "utf-8", tmpPath: path + ".tmp"}); + let encoder = new TextEncoder(); + let array = encoder.encode(JSON.stringify(contents)); + return OS.File.writeAtomic(path, array, {tmpPath: path + ".tmp"}); }, diff --git a/services/datareporting/DataReportingService.js b/services/datareporting/DataReportingService.js index e7a1aed38a11..457c4a3bacb2 100644 --- a/services/datareporting/DataReportingService.js +++ b/services/datareporting/DataReportingService.js @@ -9,9 +9,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); const ROOT_BRANCH = "datareporting."; @@ -64,13 +61,6 @@ this.DataReportingService = function () { this._os = Cc["@mozilla.org/observer-service;1"] .getService(Ci.nsIObserverService); - - this._clientID = null; - this._loadClientIdTask = null; - this._saveClientIdTask = null; - - this._stateDir = null; - this._stateFilePath = null; } DataReportingService.prototype = Object.freeze({ @@ -136,9 +126,6 @@ DataReportingService.prototype = Object.freeze({ let policyPrefs = new Preferences(POLICY_BRANCH); this.policy = new DataReportingPolicy(policyPrefs, this._prefs, this); - this._stateDir = OS.Path.join(OS.Constants.Path.profileDir, "datareporting"); - this._stateFilePath = OS.Path.join(this._stateDir, "state.json"); - this._os.addObserver(this, "sessionstore-windows-restored", true); } catch (ex) { Cu.reportError("Exception when initializing data reporting service: " + @@ -297,109 +284,6 @@ DataReportingService.prototype = Object.freeze({ this._prefs.set("service.firstRun", true); }.bind(this)); }, - - _loadClientID: Task.async(function* () { - if (this._loadClientIdTask) { - return this._loadClientIdTask; - } - - // Previously we had the stable client ID managed in FHR. - // As we want to start correlating FHR and telemetry data (and moving towards - // unifying the two), we moved the ID management to the datareporting - // service. Consequently, we try to import the FHR ID first, so we can keep - // using it. - - // Try to load the client id from the DRS state file first. - try { - let state = yield CommonUtils.readJSON(this._stateFilePath); - if (state && 'clientID' in state && typeof(state.clientID) == 'string') { - this._clientID = state.clientID; - this._loadClientIdTask = null; - return this._clientID; - } - } catch (e) { - // fall through to next option - } - - // If we dont have DRS state yet, try to import from the FHR state. - try { - let fhrStatePath = OS.Path.join(OS.Constants.Path.profileDir, "healthreport", "state.json"); - let state = yield CommonUtils.readJSON(fhrStatePath); - if (state && 'clientID' in state && typeof(state.clientID) == 'string') { - this._clientID = state.clientID; - this._loadClientIdTask = null; - this._saveClientID(); - return this._clientID; - } - } catch (e) { - // fall through to next option - } - - // We dont have an id from FHR yet, generate a new ID. - this._clientID = CommonUtils.generateUUID(); - this._loadClientIdTask = null; - this._saveClientIdTask = this._saveClientID(); - - // Wait on persisting the id. Otherwise failure to save the ID would result in - // the client creating and subsequently sending multiple IDs to the server. - // This would appear as multiple clients submitting similar data, which would - // result in orphaning. - yield this._saveClientIdTask; - - return this._clientID; - }), - - _saveClientID: Task.async(function* () { - let obj = { clientID: this._clientID }; - yield OS.File.makeDir(this._stateDir); - yield CommonUtils.writeJSON(obj, this._stateFilePath); - this._saveClientIdTask = null; - }), - - /** - * This returns a promise resolving to the the stable client ID we use for - * data reporting (FHR & Telemetry). Previously exising FHR client IDs are - * migrated to this. - * - * @return Promise The stable client ID. - */ - getClientID: function() { - if (this._loadClientIdTask) { - return this._loadClientIdTask; - } - - if (!this._clientID) { - this._loadClientIdTask = this._loadClientID(); - return this._loadClientIdTask; - } - - return Promise.resolve(this._clientID); - }, - - /** - * Reset the stable client id. - * - * @return Promise The new client ID. - */ - resetClientID: Task.async(function* () { - yield this._loadClientIdTask; - yield this._saveClientIdTask; - - this._clientID = CommonUtils.generateUUID(); - this._saveClientIdTask = this._saveClientID(); - yield this._saveClientIdTask; - - return this._clientID; - }), - - /* - * Simulate a restart of the service. This is for testing only. - */ - _reset: Task.async(function* () { - yield this._loadClientIdTask; - yield this._saveClientIdTask; - this._clientID = null; - }), }); this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataReportingService]); diff --git a/services/datareporting/policy.jsm b/services/datareporting/policy.jsm index b95d12f9c0f6..f68c7885f0a3 100644 --- a/services/datareporting/policy.jsm +++ b/services/datareporting/policy.jsm @@ -817,8 +817,7 @@ this.DataReportingPolicy.prototype = Object.freeze({ this._log.info("Requesting data submission. Will expire at " + requestExpiresDate); try { - let promise = this._listener[handler](this._inProgressSubmissionRequest); - chained = chained.then(() => promise, null); + this._listener[handler](this._inProgressSubmissionRequest); } catch (ex) { this._log.warn("Exception when calling " + handler + ": " + CommonUtils.exceptionStr(ex)); diff --git a/services/datareporting/tests/xpcshell/test_client_id.js b/services/datareporting/tests/xpcshell/test_client_id.js deleted file mode 100644 index d72b66b2c4b2..000000000000 --- a/services/datareporting/tests/xpcshell/test_client_id.js +++ /dev/null @@ -1,86 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gDatareportingService", - () => Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject); - -function run_test() { - do_get_profile(); - - // Send the needed startup notifications to the datareporting service - // to ensure that it has been initialized. - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); - - run_next_test(); -} - -add_task(function* () { - const drsPath = gDatareportingService._stateFilePath; - const fhrDir = OS.Path.join(OS.Constants.Path.profileDir, "healthreport"); - const fhrPath = OS.Path.join(fhrDir, "state.json"); - const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; - - yield OS.File.makeDir(fhrDir); - - // Check that we are importing the FHR client ID. - let clientID = CommonUtils.generateUUID(); - yield CommonUtils.writeJSON({clientID: clientID}, fhrPath); - Assert.equal(clientID, yield gDatareportingService.getClientID()); - - // We should persist the ID in DRS now and not pick up a differing ID from FHR. - yield gDatareportingService._reset(); - yield CommonUtils.writeJSON({clientID: CommonUtils.generateUUID()}, fhrPath); - Assert.equal(clientID, yield gDatareportingService.getClientID()); - - // We should be guarded against broken FHR data. - yield gDatareportingService._reset(); - yield OS.File.remove(drsPath); - yield CommonUtils.writeJSON({clientID: -1}, fhrPath); - clientID = yield gDatareportingService.getClientID(); - Assert.equal(typeof(clientID), 'string'); - Assert.ok(uuidRegex.test(clientID)); - - // We should be guarded against invalid FHR json. - yield gDatareportingService._reset(); - yield OS.File.remove(drsPath); - yield OS.File.writeAtomic(fhrPath, "abcd", {encoding: "utf-8", tmpPath: fhrPath + ".tmp"}); - clientID = yield gDatareportingService.getClientID(); - Assert.equal(typeof(clientID), 'string'); - Assert.ok(uuidRegex.test(clientID)); - - // We should be guarded against broken DRS data too and fall back to loading - // the FHR ID. - yield gDatareportingService._reset(); - clientID = CommonUtils.generateUUID(); - yield CommonUtils.writeJSON({clientID: clientID}, fhrPath); - yield CommonUtils.writeJSON({clientID: -1}, drsPath); - Assert.equal(clientID, yield gDatareportingService.getClientID()); - - // We should be guarded against invalid DRS json too. - yield gDatareportingService._reset(); - yield OS.File.remove(fhrPath); - yield OS.File.writeAtomic(drsPath, "abcd", {encoding: "utf-8", tmpPath: drsPath + ".tmp"}); - clientID = yield gDatareportingService.getClientID(); - Assert.equal(typeof(clientID), 'string'); - Assert.ok(uuidRegex.test(clientID)); - - // If both the FHR and DSR data are broken, we should end up with a new client ID. - yield gDatareportingService._reset(); - yield CommonUtils.writeJSON({clientID: -1}, fhrPath); - yield CommonUtils.writeJSON({clientID: -1}, drsPath); - clientID = yield gDatareportingService.getClientID(); - Assert.equal(typeof(clientID), 'string'); - Assert.ok(uuidRegex.test(clientID)); -}); diff --git a/services/datareporting/tests/xpcshell/xpcshell.ini b/services/datareporting/tests/xpcshell/xpcshell.ini index fbd88acf2b53..934227e192db 100644 --- a/services/datareporting/tests/xpcshell/xpcshell.ini +++ b/services/datareporting/tests/xpcshell/xpcshell.ini @@ -5,4 +5,3 @@ skip-if = toolkit == 'android' || toolkit == 'gonk' [test_policy.js] [test_session_recorder.js] -[test_client_id.js] diff --git a/services/healthreport/healthreporter.jsm b/services/healthreport/healthreporter.jsm index 2c79bfaaf0c3..ef0a4c287436 100644 --- a/services/healthreport/healthreporter.jsm +++ b/services/healthreport/healthreporter.jsm @@ -130,18 +130,13 @@ HealthReporterState.prototype = Object.freeze({ return Task.spawn(function* init() { yield OS.File.makeDir(this._stateDir); - let drs = Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject; - let drsClientID = yield drs.getClientID(); - let resetObjectState = function () { this._s = { // The payload version. This is bumped whenever there is a // backwards-incompatible change. v: 1, // The persistent client identifier. - clientID: drsClientID, + clientID: CommonUtils.generateUUID(), // Denotes the mechanism used to generate the client identifier. // 1: Random UUID. clientIDVersion: 1, @@ -181,7 +176,22 @@ HealthReporterState.prototype = Object.freeze({ // comes along and fixes us. } - this._s.clientID = drsClientID; + let regen = false; + if (!this._s.clientID) { + this._log.warn("No client ID stored. Generating random ID."); + regen = true; + } + + if (typeof(this._s.clientID) != "string") { + this._log.warn("Client ID is not a string. Regenerating."); + regen = true; + } + + if (regen) { + this._s.clientID = CommonUtils.generateUUID(); + this._s.clientIDVersion = 1; + yield this.save(); + } // Always look for preferences. This ensures that downgrades followed // by reupgrades don't result in excessive data loss. @@ -245,18 +255,21 @@ HealthReporterState.prototype = Object.freeze({ /** * Reset the client ID to something else. - * Returns a promise that is resolved when completed. + * + * This fails if remote IDs are stored because changing the client ID + * while there is remote data will create orphaned records. */ - resetClientID: Task.async(function* () { - let drs = Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject; - yield drs.resetClientID(); - this._s.clientID = yield drs.getClientID(); - this._log.info("Reset client id to " + this._s.clientID + "."); + resetClientID: function () { + if (this.remoteIDs.length) { + throw new Error("Cannot reset client ID while remote IDs are stored."); + } - yield this.save(); - }), + this._log.warn("Resetting client ID."); + this._s.clientID = CommonUtils.generateUUID(); + this._s.clientIDVersion = 1; + + return this.save(); + }, _migratePrefs: function () { let prefs = this._reporter._prefs; diff --git a/services/healthreport/modules-testing/utils.jsm b/services/healthreport/modules-testing/utils.jsm index bad2f6c8cca4..a08ce954c63c 100644 --- a/services/healthreport/modules-testing/utils.jsm +++ b/services/healthreport/modules-testing/utils.jsm @@ -182,14 +182,6 @@ InspectedHealthReporter.prototype = { const DUMMY_URI="http://localhost:62013/"; this.getHealthReporter = function (name, uri=DUMMY_URI, inspected=false) { - // The healthreporters use the client id from the datareporting service, - // so we need to ensure it is initialized. - let drs = Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject; - drs.observe(null, "app-startup", null); - drs.observe(null, "profile-after-change", null); - let branch = "healthreport.testing." + name + "."; let prefs = new Preferences(branch + "healthreport."); @@ -201,14 +193,12 @@ this.getHealthReporter = function (name, uri=DUMMY_URI, inspected=false) { let policyPrefs = new Preferences(branch + "policy."); let listener = new MockPolicyListener(); listener.onRequestDataUpload = function (request) { - let promise = reporter.requestDataUpload(request); + reporter.requestDataUpload(request); MockPolicyListener.prototype.onRequestDataUpload.call(this, request); - return promise; } listener.onRequestRemoteDelete = function (request) { - let promise = reporter.deleteRemoteData(request); + reporter.deleteRemoteData(request); MockPolicyListener.prototype.onRequestRemoteDelete.call(this, request); - return promise; } let policy = new DataReportingPolicy(policyPrefs, prefs, listener); let type = inspected ? InspectedHealthReporter : HealthReporter; diff --git a/services/healthreport/tests/xpcshell/test_healthreporter.js b/services/healthreport/tests/xpcshell/test_healthreporter.js index 9bb2b3ed2276..1eecc8c4ad4e 100644 --- a/services/healthreport/tests/xpcshell/test_healthreporter.js +++ b/services/healthreport/tests/xpcshell/test_healthreporter.js @@ -21,12 +21,6 @@ Cu.import("resource://testing-common/services/common/bagheeraserver.js"); Cu.import("resource://testing-common/services/metrics/mocks.jsm"); Cu.import("resource://testing-common/services/healthreport/utils.jsm"); Cu.import("resource://testing-common/AppData.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gDatareportingService", - () => Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject); const DUMMY_URI = "http://localhost:62013/"; @@ -114,13 +108,6 @@ function ensureUserNotified (reporter) { } function run_test() { - do_get_profile(); - - // Send the needed startup notifications to the datareporting service - // to ensure that it has been initialized. - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); - run_next_test(); } @@ -758,7 +745,7 @@ add_task(function test_request_remote_data_deletion() { do_check_false(reporter.haveRemoteData()); do_check_false(server.hasDocument(reporter.serverNamespace, id)); - // Client ID should be updated. + // Client ID should be updated. do_check_neq(reporter._state.clientID, null); do_check_neq(reporter._state.clientID, clientID); do_check_eq(reporter._state.clientIDVersion, 1); @@ -1184,6 +1171,38 @@ add_task(function test_state_downgrade_upgrade() { } }); +// Missing client ID in state should be created on state load. +add_task(function* test_state_create_client_id() { + let reporter = getHealthReporter("state_create_client_id"); + + yield CommonUtils.writeJSON({ + v: 1, + remoteIDs: ["id1", "id2"], + lastPingTime: Date.now(), + removeOutdatedLastPayload: true, + }, reporter._state._filename); + + try { + yield reporter.init(); + + do_check_eq(reporter.lastSubmitID, "id1"); + do_check_neq(reporter._state.clientID, null); + do_check_eq(reporter._state.clientID.length, 36); + do_check_eq(reporter._state.clientIDVersion, 1); + + let clientID = reporter._state.clientID; + + // The client ID should be persisted as soon as it is created. + reporter._shutdown(); + + reporter = getHealthReporter("state_create_client_id"); + yield reporter.init(); + do_check_eq(reporter._state.clientID, clientID); + } finally { + reporter._shutdown(); + } +}); + // Invalid stored client ID is reset automatically. add_task(function* test_empty_client_id() { let reporter = getHealthReporter("state_empty_client_id"); diff --git a/services/healthreport/tests/xpcshell/test_provider_appinfo.js b/services/healthreport/tests/xpcshell/test_provider_appinfo.js index 6f5115110478..97b93ef96a54 100644 --- a/services/healthreport/tests/xpcshell/test_provider_appinfo.js +++ b/services/healthreport/tests/xpcshell/test_provider_appinfo.js @@ -3,29 +3,16 @@ "use strict"; -const {interfaces: Ci, results: Cr, utils: Cu, classes: Cc} = Components; +const {interfaces: Ci, results: Cr, utils: Cu} = Components; Cu.import("resource://gre/modules/Metrics.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); Cu.import("resource://testing-common/services/healthreport/utils.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gDatareportingService", - () => Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject); function run_test() { - do_get_profile(); - - // Send the needed startup notifications to the datareporting service - // to ensure that it has been initialized. - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); - run_next_test(); } diff --git a/toolkit/components/telemetry/TelemetryPing.jsm b/toolkit/components/telemetry/TelemetryPing.jsm index 6a13db165ac3..d9c9f6132ee2 100644 --- a/toolkit/components/telemetry/TelemetryPing.jsm +++ b/toolkit/components/telemetry/TelemetryPing.jsm @@ -32,7 +32,6 @@ const PREF_BRANCH = "toolkit.telemetry."; const PREF_SERVER = PREF_BRANCH + "server"; const PREF_ENABLED = PREF_BRANCH + "enabled"; const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID"; -const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; // Do not gather data more than once a minute const TELEMETRY_INTERVAL = 60000; @@ -248,7 +247,6 @@ let Impl = { // The previous build ID, if this is the first run with a new build. // Undefined if this is not the first run, or the previous build ID is unknown. _previousBuildID: undefined, - _clientID: null, /** * Gets a series of simple measurements (counters). At the moment, this @@ -703,7 +701,7 @@ let Impl = { addonDetails: AddonManagerPrivate.getTelemetryDetails(), UIMeasurements: UITelemetry.getUIMeasurements(), log: TelemetryLog.entries(), - info: info, + info: info }; if (Object.keys(this._slowSQLStartup).length != 0 && @@ -712,17 +710,6 @@ let Impl = { payloadObj.slowSQLStartup = this._slowSQLStartup; } - let fhrUploadEnabled = false; - try { - fhrUploadEnabled = Services.prefs.getBoolPref(PREF_FHR_UPLOAD_ENABLED); - } catch (e) { - // Pref not set. - } - - if (this._clientID && fhrUploadEnabled) { - payloadObj.clientID = this._clientID; - } - return payloadObj; }, @@ -970,13 +957,6 @@ let Impl = { this.attachObservers(); this.gatherMemory(); - if ("@mozilla.org/datareporting/service;1" in Cc) { - let drs = Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject; - this._clientID = yield drs.getClientID(); - } - Telemetry.asyncFetchTelemetryData(function () {}); delete this._timer; deferred.resolve(); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 23a608545f78..7cc90e0d872e 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -41,21 +41,11 @@ const RW_OWNER = 0600; const NUMBER_OF_THREADS_TO_LAUNCH = 30; let gNumberOfThreadsLaunched = 0; -const PREF_BRANCH = "toolkit.telemetry."; -const PREF_ENABLED = PREF_BRANCH + "enabled"; -const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; - const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); let gHttpServer = new HttpServer(); let gServerStarted = false; let gRequestIterator = null; -let gDataReportingClientID = null; - -XPCOMUtils.defineLazyGetter(this, "gDatareportingService", - () => Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject); function sendPing () { TelemetryPing.gatherStartup(); @@ -172,13 +162,6 @@ function checkPayloadInfo(payload, reason) { do_check_true(payload.info.revision.startsWith("http")); } - if ("@mozilla.org/datareporting/service;1" in Cc && - Services.prefs.getBoolPref(PREF_FHR_UPLOAD_ENABLED)) { - do_check_true("clientID" in payload); - do_check_neq(payload.clientID, null); - do_check_eq(payload.clientID, gDataReportingClientID); - } - try { // If we've not got nsIGfxInfoDebug, then this will throw and stop us doing // this test. @@ -396,16 +379,6 @@ function run_test() { do_get_profile(); createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - Services.prefs.setBoolPref(PREF_ENABLED, true); - Services.prefs.setBoolPref(PREF_FHR_UPLOAD_ENABLED, true); - - // Send the needed startup notifications to the datareporting service - // to ensure that it has been initialized. - if ("@mozilla.org/datareporting/service;1" in Cc) { - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); - } - // Make it look like we've previously failed to lock a profile a couple times. write_fake_failedprofilelocks_file(); @@ -452,14 +425,6 @@ function actualTest() { run_next_test(); } -add_task(function* asyncSetup() { - yield TelemetryPing.setup(); - - if ("@mozilla.org/datareporting/service;1" in Cc) { - gDataReportingClientID = yield gDatareportingService.getClientID(); - } -}); - // Ensure that not overwriting an existing file fails silently add_task(function* test_overwritePing() { let ping = {slug: "foo"} diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js index 4e234d417042..9fca40c07390 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPingBuildID.js @@ -15,16 +15,10 @@ "use strict" -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; +const Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/TelemetryPing.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gDatareportingService", - () => Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject); // Force the Telemetry enabled preference so that TelemetryPing.reset() doesn't exit early. Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true); @@ -71,13 +65,5 @@ add_task(function* test_newBuild() { function run_test() { // Make sure we have a profile directory. do_get_profile(); - - // Send the needed startup notifications to the datareporting service - // to ensure that it has been initialized. - if ("@mozilla.org/datareporting/service;1" in Cc) { - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); - } - run_next_test(); } diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js index 135bf4812766..ec3c4a1bc619 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySendOldPings.js @@ -23,14 +23,8 @@ Cu.import("resource://gre/modules/Promise.jsm", this); Cu.import("resource://gre/modules/TelemetryFile.jsm", this); Cu.import("resource://gre/modules/TelemetryPing.jsm", this); Cu.import("resource://gre/modules/Task.jsm", this); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); let {OS: {File, Path, Constants}} = Cu.import("resource://gre/modules/osfile.jsm", {}); -XPCOMUtils.defineLazyGetter(this, "gDatareportingService", - () => Cc["@mozilla.org/datareporting/service;1"] - .getService(Ci.nsISupports) - .wrappedJSObject); - // We increment TelemetryFile's MAX_PING_FILE_AGE and // OVERDUE_PING_FILE_AGE by 1 minute so that our test pings exceed // those points in time, even taking into account file system imprecision. @@ -196,12 +190,6 @@ function run_test() { gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler); gHttpServer.start(-1); do_get_profile(); - - // Send the needed startup notifications to the datareporting service - // to ensure that it has been initialized. - gDatareportingService.observe(null, "app-startup", null); - gDatareportingService.observe(null, "profile-after-change", null); - Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true); Services.prefs.setCharPref(TelemetryPing.Constants.PREF_SERVER, "http://localhost:" + gHttpServer.identity.primaryPort); From 547eca961f1f923f28b3afce7af4b4d5ae0be1ad Mon Sep 17 00:00:00 2001 From: Steve Workman Date: Tue, 7 Oct 2014 14:57:48 -0700 Subject: [PATCH 31/81] Bug 1074789 - Adjust DNS renewal time telemetry to take TTL experiments into consideration r=mcmanus --- netwerk/dns/nsHostResolver.cpp | 60 ++++++++++++++++++-- toolkit/components/telemetry/Histograms.json | 40 +++++++++++++ 2 files changed, 94 insertions(+), 6 deletions(-) diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index deb3880a698a..ac4967b70159 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -266,6 +266,47 @@ GetBlacklistCountHistogram(DnsExpirationVariant aVariant, return DNS_BLACKLIST_COUNT_VAR_CONTROL_LOW; } +static mozilla::Telemetry::ID +GetRenewalTimeHistogram(DnsExpirationVariant aVariant) +{ + using namespace mozilla::Telemetry; + +#ifdef TTL_AVAILABLE + switch (sDnsVariant) { + case DNS_EXP_VARIANT_CONTROL: + return DNS_RENEWAL_TIME; + case DNS_EXP_VARIANT_TTL_ONLY: + return DNS_RENEWAL_TIME__TTL_ONLY_EXPT; + case DNS_EXP_VARIANT_TTL_PLUS_CONST_GRACE: + return DNS_RENEWAL_TIME__TTL_PLUS_CONST_GRACE_EXPT; + default: + MOZ_ASSERT_UNREACHABLE("Invalid variant."); + } +#endif + return DNS_RENEWAL_TIME; +} + +static mozilla::Telemetry::ID +GetRenewalTimeForTTLHistogram(DnsExpirationVariant aVariant) +{ + using namespace mozilla::Telemetry; + +#ifdef TTL_AVAILABLE + switch (sDnsVariant) { + case DNS_EXP_VARIANT_CONTROL: + MOZ_ASSERT_UNREACHABLE("No TTL for Control Expt."); + return DNS_RENEWAL_TIME; + case DNS_EXP_VARIANT_TTL_ONLY: + return DNS_RENEWAL_TIME_FOR_TTL__TTL_ONLY_EXPT; + case DNS_EXP_VARIANT_TTL_PLUS_CONST_GRACE: + return DNS_RENEWAL_TIME_FOR_TTL__TTL_PLUS_CONST_GRACE_EXPT; + default: + MOZ_ASSERT_UNREACHABLE("Invalid variant."); + } +#endif + return DNS_RENEWAL_TIME; +} + // this macro filters out any flags that are not used when constructing the // host key. the significant flags are those that would affect the resulting // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName). @@ -1610,12 +1651,19 @@ nsHostResolver::ThreadFunc(void *arg) uint32_t millis = static_cast(elapsed.ToMilliseconds()); if (NS_SUCCEEDED(status)) { - Telemetry::Accumulate(!rec->addr_info_gencnt ? - Telemetry::DNS_LOOKUP_TIME : - Telemetry::DNS_RENEWAL_TIME, - millis); - } - else { + Telemetry::ID histogramID; + if (!rec->addr_info_gencnt) { + // Time for initial lookup. + histogramID = Telemetry::DNS_LOOKUP_TIME; + } else if (!getTtl) { + // Time for renewal; categorized by expiration strategy. + histogramID = GetRenewalTimeHistogram(sDnsVariant); + } else { + // Time to get TTL; categorized by expiration strategy. + histogramID = GetRenewalTimeForTTLHistogram(sDnsVariant); + } + Telemetry::Accumulate(histogramID, millis); + } else { Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis); } diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 67e6cc689ef2..61aa3cd0bcce 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -2145,6 +2145,46 @@ "extended_statistics_ok": true, "description": "Time for a renewed DNS OS resolution (msec)" }, + "DNS_RENEWAL_TIME_FOR_TTL": { + "expires_in_version": "never", + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Time for a DNS OS resolution (msec) used to get TTL" + }, + "DNS_RENEWAL_TIME_FOR_TTL__TTL_ONLY_EXPT": { + "expires_in_version": "never", + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Time for a DNS OS resolution (msec) used to get TTL - TTL Only Experiment" + }, + "DNS_RENEWAL_TIME_FOR_TTL__TTL_PLUS_CONST_GRACE_EXPT": { + "expires_in_version": "never", + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Time for a DNS OS resolution (msec) used to get TTL - TTL + Const Grace Period Experiment" + }, + "DNS_RENEWAL_TIME__TTL_ONLY_EXPT": { + "expires_in_version": "never", + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Time for a renewed DNS OS resolution (msec) - TTL Only Experiment" + }, + "DNS_RENEWAL_TIME__TTL_PLUS_CONST_GRACE_EXPT": { + "expires_in_version": "never", + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Time for a renewed DNS OS resolution (msec) - TTL + Const Grace Period Experiment" + }, "DNS_FAILED_LOOKUP_TIME": { "expires_in_version": "never", "kind": "exponential", From 2018b51d54c418283204bd16f991313cba084552 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 1 Jul 2014 15:33:48 -0500 Subject: [PATCH 32/81] Add some test cases involving Symbol.iterator as a prelude to bug 918828. no_r=me, testonly. --HG-- extra : rebase_source : 1bd8721f904456a7805d75e6e6b91c6cdf9eabb1 --- js/src/tests/ecma_6/Symbol/equality.js | 3 ++- js/src/tests/ecma_6/Symbol/surfaces.js | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/js/src/tests/ecma_6/Symbol/equality.js b/js/src/tests/ecma_6/Symbol/equality.js index 991db21442f0..66eaa7902adb 100644 --- a/js/src/tests/ecma_6/Symbol/equality.js +++ b/js/src/tests/ecma_6/Symbol/equality.js @@ -10,7 +10,8 @@ if (typeof Symbol === "function") { Symbol(), Symbol("Symbol.iterator"), Symbol("Symbol.iterator"), // distinct new symbol with the same description - Symbol.for("Symbol.iterator") + Symbol.for("Symbol.iterator"), + Symbol.iterator ]; // Distinct symbols are never equal to each other, even if they have the same diff --git a/js/src/tests/ecma_6/Symbol/surfaces.js b/js/src/tests/ecma_6/Symbol/surfaces.js index edbcb7758751..c318a939c57a 100644 --- a/js/src/tests/ecma_6/Symbol/surfaces.js +++ b/js/src/tests/ecma_6/Symbol/surfaces.js @@ -22,6 +22,11 @@ if (typeof Symbol === "function") { assertEq(desc.enumerable, false); assertEq(desc.writable, true); + desc = Object.getOwnPropertyDescriptor(Symbol, "iterator"); + assertEq(desc.configurable, false); + assertEq(desc.enumerable, false); + assertEq(desc.writable, false); + assertEq(Symbol.for.length, 1); assertEq(Symbol.prototype.toString.length, 0); assertEq(Symbol.prototype.valueOf.length, 0); From 07d717f6df4689b2fc66807c6e2d8c449b7faf9f Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 25 Jul 2014 18:50:48 -0500 Subject: [PATCH 33/81] Bug 1082672, part 1 - Add JSAPI macros JS_SYM_FN etc. to support defining functions with well-known symbol keys. r=Waldo. --HG-- extra : rebase_source : 9791c940599844802c9a262fe8b1610a0de3ef40 --- dom/bindings/BindingUtils.h | 2 +- js/src/jsapi.cpp | 133 ++++++++++++++++++++---------------- js/src/jsapi.h | 34 +++++++++ js/src/jscntxt.h | 1 + js/src/jsfun.cpp | 39 +++++++++-- js/src/jsfun.h | 6 +- js/src/vm/Runtime.h | 2 +- 7 files changed, 151 insertions(+), 66 deletions(-) diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 6e53ee6f8d49..289747b940e7 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -1685,7 +1685,7 @@ InitIds(JSContext* cx, const Prefable* prefableSpecs, jsid* ids) // because this is only done once per application runtime. Spec* spec = prefableSpecs->specs; do { - if (!InternJSString(cx, *ids, spec->name)) { + if (!JS::PropertySpecNameToPermanentId(cx, spec->name, ids)) { return false; } } while (++ids, (++spec)->name); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e482bc962b91..30f8f13f4d65 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2781,7 +2781,8 @@ JS_AlreadyHasOwnUCProperty(JSContext *cx, HandleObject obj, const char16_t *name return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp); } -/* Wrapper functions to create wrappers with no corresponding JSJitInfo from API +/* + * Wrapper functions to create wrappers with no corresponding JSJitInfo from API * function arguments. */ static JSPropertyOpWrapper @@ -2825,8 +2826,6 @@ DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue val MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER))); JSFunction::Flags zeroFlags = JSAPIToJSFunctionFlags(0); - // We can't just use JS_NewFunctionById here because it assumes a - // string id. RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : nullptr); attrs &= ~JSPROP_NATIVE_ACCESSORS; if (getter) { @@ -2863,12 +2862,12 @@ DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue val AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id, value, - (attrs & JSPROP_GETTER) - ? JS_FUNC_TO_DATA_PTR(JSObject *, getter) - : nullptr, - (attrs & JSPROP_SETTER) - ? JS_FUNC_TO_DATA_PTR(JSObject *, setter) - : nullptr); + (attrs & JSPROP_GETTER) + ? JS_FUNC_TO_DATA_PTR(JSObject *, getter) + : nullptr, + (attrs & JSPROP_SETTER) + ? JS_FUNC_TO_DATA_PTR(JSObject *, setter) + : nullptr); return JSObject::defineGeneric(cx, obj, id, value, getter, setter, attrs); } @@ -3012,30 +3011,22 @@ DefineProperty(JSContext *cx, HandleObject obj, const char *name, HandleValue va return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags); } - static bool -DefineSelfHostedProperty(JSContext *cx, - HandleObject obj, - const char *name, - const char *getterName, - const char *setterName, - unsigned attrs, - unsigned flags) +DefineSelfHostedProperty(JSContext *cx, HandleObject obj, HandleId id, + const char *getterName, const char *setterName, + unsigned attrs, unsigned flags) { - RootedAtom nameAtom(cx, Atomize(cx, name, strlen(name))); - if (!nameAtom) - return false; - RootedAtom getterNameAtom(cx, Atomize(cx, getterName, strlen(getterName))); if (!getterNameAtom) return false; - RootedValue getterValue(cx); - if (!cx->global()->getSelfHostedFunction(cx, getterNameAtom, nameAtom, - 0, &getterValue)) - { + RootedAtom name(cx, IdToFunctionName(cx, id)); + if (!name) + return false; + + RootedValue getterValue(cx); + if (!cx->global()->getSelfHostedFunction(cx, getterNameAtom, name, 0, &getterValue)) return false; - } MOZ_ASSERT(getterValue.isObject() && getterValue.toObject().is()); RootedFunction getterFunc(cx, &getterValue.toObject().as()); JSPropertyOp getterOp = JS_DATA_TO_FUNC_PTR(PropertyOp, getterFunc.get()); @@ -3047,19 +3038,16 @@ DefineSelfHostedProperty(JSContext *cx, return false; RootedValue setterValue(cx); - if (!cx->global()->getSelfHostedFunction(cx, setterNameAtom, nameAtom, - 0, &setterValue)) - { + if (!cx->global()->getSelfHostedFunction(cx, setterNameAtom, name, 0, &setterValue)) return false; - } MOZ_ASSERT(setterValue.isObject() && setterValue.toObject().is()); setterFunc = &getterValue.toObject().as(); } JSStrictPropertyOp setterOp = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setterFunc.get()); - return DefineProperty(cx, obj, name, JS::UndefinedHandleValue, - GetterWrapper(getterOp), SetterWrapper(setterOp), - attrs, flags); + return DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue, + GetterWrapper(getterOp), SetterWrapper(setterOp), + attrs, flags); } JS_PUBLIC_API(bool) @@ -3256,37 +3244,73 @@ JS_DefineConstIntegers(JSContext *cx, HandleObject obj, const JSConstIntegerSpec return DefineConstScalar(cx, obj, cis); } +static bool +PropertySpecNameToId(JSContext *cx, const char *name, MutableHandleId id, + js::InternBehavior ib = js::DoNotInternAtom) +{ + if (JS::PropertySpecNameIsSymbol(name)) { + uintptr_t u = reinterpret_cast(name); + id.set(SYMBOL_TO_JSID(cx->wellKnownSymbols().get(u - 1))); + } else { + JSAtom *atom = Atomize(cx, name, strlen(name), ib); + if (!atom) + return false; + id.set(AtomToId(atom)); + } + return true; +} + +JS_PUBLIC_API(bool) +JS::PropertySpecNameToPermanentId(JSContext *cx, const char *name, jsid *idp) +{ + // We are calling fromMarkedLocation(idp) even though idp points to a + // location that will never be marked. This is OK because the whole point + // of this API is to populate *idp with a jsid that does not need to be + // marked. + return PropertySpecNameToId(cx, name, MutableHandleId::fromMarkedLocation(idp), + js::InternAtom); +} + JS_PUBLIC_API(bool) JS_DefineProperties(JSContext *cx, HandleObject obj, const JSPropertySpec *ps) { - bool ok; - for (ok = true; ps->name; ps++) { + RootedId id(cx); + + for (; ps->name; ps++) { + if (!PropertySpecNameToId(cx, ps->name, &id)) + return false; + if (ps->flags & JSPROP_NATIVE_ACCESSORS) { // If you declare native accessors, then you should have a native // getter. MOZ_ASSERT(ps->getter.propertyOp.op); + // If you do not have a self-hosted getter, you should not have a // self-hosted setter. This is the closest approximation to that // assertion we can have with our setup. MOZ_ASSERT_IF(ps->setter.propertyOp.info, ps->setter.propertyOp.op); - ok = DefineProperty(cx, obj, ps->name, JS::UndefinedHandleValue, - ps->getter.propertyOp, ps->setter.propertyOp, ps->flags, 0); + if (!DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue, + ps->getter.propertyOp, ps->setter.propertyOp, ps->flags, 0)) + { + return false; + } } else { // If you have self-hosted getter/setter, you can't have a // native one. MOZ_ASSERT(!ps->getter.propertyOp.op && !ps->setter.propertyOp.op); MOZ_ASSERT(ps->flags & JSPROP_GETTER); - ok = DefineSelfHostedProperty(cx, obj, ps->name, + if (!DefineSelfHostedProperty(cx, obj, id, ps->getter.selfHosted.funname, ps->setter.selfHosted.funname, - ps->flags, 0); + ps->flags, 0)) + { + return false; + } } - if (!ok) - break; } - return ok; + return true; } JS_PUBLIC_API(bool) @@ -3768,12 +3792,14 @@ JS_NewFunctionById(JSContext *cx, JSNative native, unsigned nargs, unsigned flag JS_PUBLIC_API(JSFunction *) JS::GetSelfHostedFunction(JSContext *cx, const char *selfHostedName, HandleId id, unsigned nargs) { - MOZ_ASSERT(JSID_IS_STRING(id)); MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - RootedAtom name(cx, JSID_TO_ATOM(id)); + RootedAtom name(cx, IdToFunctionName(cx, id)); + if (!name) + return nullptr; + RootedAtom shName(cx, Atomize(cx, selfHostedName, strlen(selfHostedName))); if (!shName) return nullptr; @@ -3937,21 +3963,11 @@ JS_DefineFunctions(JSContext *cx, HandleObject obj, const JSFunctionSpec *fs) CHECK_REQUEST(cx); assertSameCompartment(cx, obj); + RootedId id(cx); for (; fs->name; fs++) { - RootedAtom atom(cx); - // If the name starts with "@@", it must be a well-known symbol. - if (fs->name[0] != '@' || fs->name[1] != '@') - atom = Atomize(cx, fs->name, strlen(fs->name)); - else if (strcmp(fs->name, "@@iterator") == 0) - // FIXME: This atom should be a symbol: bug 918828. - atom = cx->names().std_iterator; - else - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_SYMBOL, fs->name); - if (!atom) + if (!PropertySpecNameToId(cx, fs->name, &id)) return false; - Rooted id(cx, AtomToId(atom)); - /* * Define a generic arity N+1 static method for the arity N prototype * method if flags contains JSFUN_GENERIC_NATIVE. @@ -3993,8 +4009,11 @@ JS_DefineFunctions(JSContext *cx, HandleObject obj, const JSFunctionSpec *fs) RootedAtom shName(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName))); if (!shName) return false; + RootedAtom name(cx, IdToFunctionName(cx, id)); + if (!name) + return false; RootedValue funVal(cx); - if (!cx->global()->getSelfHostedFunction(cx, shName, atom, fs->nargs, &funVal)) + if (!cx->global()->getSelfHostedFunction(cx, shName, name, fs->nargs, &funVal)) return false; if (!JSObject::defineGeneric(cx, obj, id, funVal, nullptr, nullptr, flags)) return false; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 60a7f8328e86..a49b10936f06 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2475,15 +2475,27 @@ struct JSFunctionSpec { * JSFUN_STUB_GSOPS. JS_FNINFO allows the simple adding of * JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted function. Finally * JS_FNSPEC has slots for all the fields. + * + * The _SYM variants allow defining a function with a symbol key rather than a + * string key. For example, use JS_SYM_FN(iterator, ...) to define an + * @@iterator method. */ #define JS_FS(name,call,nargs,flags) \ JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr) #define JS_FN(name,call,nargs,flags) \ JS_FNSPEC(name, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr) +#define JS_SYM_FN(name,call,nargs,flags) \ + JS_SYM_FNSPEC(symbol, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr) #define JS_FNINFO(name,call,info,nargs,flags) \ JS_FNSPEC(name, call, info, nargs, flags, nullptr) #define JS_SELF_HOSTED_FN(name,selfHostedName,nargs,flags) \ JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName) +#define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \ + JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName) +#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \ + JS_FNSPEC(reinterpret_cast( \ + uint32_t(::JS::SymbolCode::symbol) + 1), \ + call, info, nargs, flags, selfHostedName) #define JS_FNSPEC(name,call,info,nargs,flags,selfHostedName) \ {name, {call, info}, nargs, flags, selfHostedName} @@ -4494,6 +4506,28 @@ GetSymbolCode(Handle symbol); JS_PUBLIC_API(Symbol *) GetWellKnownSymbol(JSContext *cx, SymbolCode which); +/* + * Return true if the given JSPropertySpec::name or JSFunctionSpec::name value + * is actually a symbol code and not a string. See JS_SYM_FN. + */ +inline bool +PropertySpecNameIsSymbol(const char *name) +{ + uintptr_t u = reinterpret_cast(name); + return u != 0 && u - 1 < WellKnownSymbolLimit; +} + +/* + * Create a jsid that does not need to be marked for GC. + * + * 'name' is a JSPropertySpec::name or JSFunctionSpec::name value. The + * resulting jsid, on success, is either an interned string or a well-known + * symbol; either way it is immune to GC so there is no need to visit *idp + * during GC marking. + */ +JS_PUBLIC_API(bool) +PropertySpecNameToPermanentId(JSContext *cx, const char *name, jsid *idp); + } /* namespace JS */ /************************************************************************/ diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index d044b1dca164..55fc9056b927 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -284,6 +284,7 @@ struct ThreadSafeContext : ContextFriendFields, JSAtomState &names() { return *runtime_->commonNames; } StaticStrings &staticStrings() { return *runtime_->staticStrings; } AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; } + WellKnownSymbols &wellKnownSymbols() { return *runtime_->wellKnownSymbols; } const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; } PropertyName *emptyString() { return runtime_->emptyString; } FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 24f8fd20c1be..76f6db09c5f2 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2063,6 +2063,33 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, return cloneRoot; } +/* + * Return an atom for use as the name of a builtin method with the given + * property id. + * + * Function names are always strings. If id is the well-known @@iterator + * symbol, this returns "[Symbol.iterator]". + * + * Implements step 4 of SetFunctionName in ES6 draft rev 27 (24 Aug 2014). + */ +JSAtom * +js::IdToFunctionName(JSContext *cx, HandleId id) +{ + if (JSID_IS_ATOM(id)) + return JSID_TO_ATOM(id); + + if (JSID_IS_SYMBOL(id)) { + RootedAtom desc(cx, JSID_TO_SYMBOL(id)->description()); + StringBuffer sb(cx); + if (!sb.append('[') || !sb.append(desc) || !sb.append(']')) + return nullptr; + return sb.finishAtom(); + } + + RootedValue idv(cx, IdToValue(id)); + return ToAtom(cx, idv); +} + JSFunction * js::DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native, unsigned nargs, unsigned flags, AllocKind allocKind /* = FinalizeKind */, @@ -2070,9 +2097,6 @@ js::DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native, { PropertyOp gop; StrictPropertyOp sop; - - RootedFunction fun(cx); - if (flags & JSFUN_STUB_GSOPS) { /* * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or @@ -2093,8 +2117,13 @@ js::DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native, funFlags = JSFunction::INTERPRETED_LAZY; else funFlags = JSAPIToJSFunctionFlags(flags); - RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : nullptr); - fun = NewFunction(cx, NullPtr(), native, nargs, funFlags, obj, atom, allocKind, newKind); + + RootedAtom atom(cx, IdToFunctionName(cx, id)); + if (!atom) + return nullptr; + + RootedFunction fun(cx, NewFunction(cx, NullPtr(), native, nargs, funFlags, obj, atom, + allocKind, newKind)); if (!fun) return nullptr; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 82360b3fac1c..f36c90ea0370 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -521,6 +521,9 @@ NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobj, JSNative native, JSObject *proto, gc::AllocKind allocKind = JSFunction::FinalizeKind, NewObjectKind newKind = GenericObject); +extern JSAtom * +IdToFunctionName(JSContext *cx, HandleId id); + extern JSFunction * DefineFunction(JSContext *cx, HandleObject obj, HandleId id, JSNative native, unsigned nargs, unsigned flags, @@ -572,7 +575,6 @@ CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, gc::AllocKind kind = JSFunction::FinalizeKind, NewObjectKind newKindArg = GenericObject); - extern bool FindBody(JSContext *cx, HandleFunction fun, HandleLinearString src, size_t *bodyStart, size_t *bodyEnd); @@ -664,7 +666,7 @@ js_fun_apply(JSContext *cx, unsigned argc, js::Value *vp); extern bool js_fun_call(JSContext *cx, unsigned argc, js::Value *vp); -extern JSObject* +extern JSObject * js_fun_bind(JSContext *cx, js::HandleObject target, js::HandleValue thisArg, js::Value *boundArgs, unsigned argslen); diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 572bd27ab374..711bbd3ca1e2 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -414,7 +414,7 @@ namespace js { * Storage for well-known symbols. It's a separate struct from the Runtime so * that it can be shared across multiple runtimes. As in JSAtomState, each * field is a smart pointer that's immutable once initialized. - * `rt->wellKnownSymbols.iterator` is convertible to Handle. + * `rt->wellKnownSymbols->iterator` is convertible to Handle. * * Well-known symbols are never GC'd. The description() of each well-known * symbol is a permanent atom. From 35b526eb20f48bce3bd75b6ea720e60407687a61 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 17 Sep 2014 15:37:58 -0500 Subject: [PATCH 34/81] Bug 1082672, part 2 - Change mozilla::dom::GetArrayIndexFromId to cope with symbols. r=bz. --HG-- extra : rebase_source : f183b8c543e42664e2374954b627600a5de5a42d --- dom/base/test/mochitest.ini | 1 + dom/base/test/test_window_define_symbol.html | 25 ++++++++++++++++++++ dom/bindings/DOMJSProxyHandler.cpp | 3 ++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 dom/base/test/test_window_define_symbol.html diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 8d2e82156f1f..96e0919beb78 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -82,6 +82,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec [test_urlutils_stringify.html] [test_window_constructor.html] [test_window_cross_origin_props.html] +[test_window_define_symbol.html] [test_window_enumeration.html] [test_window_extensible.html] [test_window_indexing.html] diff --git a/dom/base/test/test_window_define_symbol.html b/dom/base/test/test_window_define_symbol.html new file mode 100644 index 000000000000..b5b936542d90 --- /dev/null +++ b/dom/base/test/test_window_define_symbol.html @@ -0,0 +1,25 @@ + + + + + + Test for Bug 1082672 part 2 + + + + +Mozilla Bug 1082672 +

    + +
    +
    +
    + + diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index a4a11139e26b..2c26eebc836d 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -345,7 +345,8 @@ IdToInt32(JSContext* cx, JS::Handle id) JS::Rooted idval(cx); double array_index; int32_t i; - if (!::JS_IdToValue(cx, id, &idval) || + if (JSID_IS_SYMBOL(id) || + !::JS_IdToValue(cx, id, &idval) || !JS::ToNumber(cx, idval, &array_index) || !::JS_DoubleIsInt32(array_index, &i)) { return -1; From bf48f46a72ee202a3b1526177001299eacdec81e Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Mon, 6 Oct 2014 16:42:33 -0500 Subject: [PATCH 35/81] Bug 1082672, part 3 - Add some more symbol support for DOM bindings. r=bz. --HG-- extra : rebase_source : 79b314cb464e9c77ac25bc19e91b6e13aa241aeb --- dom/bindings/DOMJSProxyHandler.cpp | 2 +- dom/bindings/DOMJSProxyHandler.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index 2c26eebc836d..e798c5a035e6 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -299,7 +299,7 @@ BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx, JS::Handle proxy, JS::AutoIdVector& props) const { - return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN, props); + return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); } bool diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index cc5324a7ca61..7aa264b9e41a 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -80,7 +80,8 @@ public: protected: // Hook for subclasses to implement shared ownPropertyKeys()/keys() // functionality. The "flags" argument is either JSITER_OWNONLY (for keys()) - // or JSITER_OWNONLY | JSITER_HIDDEN (for ownPropertyKeys()). + // or JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS (for + // ownPropertyKeys()). virtual bool ownPropNames(JSContext* cx, JS::Handle proxy, unsigned flags, JS::AutoIdVector& props) const = 0; From 8ec9f238d924544da458e4d1ffee8754aa636414 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 18 Sep 2014 12:30:38 -0500 Subject: [PATCH 36/81] Bug 1082672, part 4 - Change XrayWrapper code to be able to resolve symbol-keyed methods. r=bz, r=bholley. --HG-- extra : rebase_source : f78cbb83f63dfffd648c6d3c280273f4a61c9fe8 extra : amend_source : f006a096174eee166125430753e65e9a31bd930b --- dom/bindings/BindingUtils.cpp | 3 + js/src/jsapi.cpp | 39 +++++++- js/src/jsapi.h | 3 + js/src/vm/Runtime.h | 10 +- js/xpconnect/tests/chrome/test_xrayToJS.xul | 104 ++++++++++++++------ js/xpconnect/wrappers/XrayWrapper.cpp | 39 ++++---- 6 files changed, 144 insertions(+), 54 deletions(-) diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 8dfbf4acf9ec..09e8e30d1e2d 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1406,8 +1406,11 @@ XrayAttributeOrMethodKeys(JSContext* cx, JS::Handle wrapper, // looking at now. size_t i = list->specs - specList; for ( ; ids[i] != JSID_VOID; ++i) { + // Skip non-enumerable properties and symbol-keyed properties unless + // they are specially requested via flags. if (((flags & JSITER_HIDDEN) || (specList[i].flags & JSPROP_ENUMERATE)) && + ((flags & JSITER_SYMBOLS) || !JSID_IS_SYMBOL(ids[i])) && !props.append(ids[i])) { return false; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 30f8f13f4d65..39b6e4014277 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3244,13 +3244,21 @@ JS_DefineConstIntegers(JSContext *cx, HandleObject obj, const JSConstIntegerSpec return DefineConstScalar(cx, obj, cis); } +static JS::SymbolCode +PropertySpecNameToSymbolCode(const char *name) +{ + MOZ_ASSERT(JS::PropertySpecNameIsSymbol(name)); + uintptr_t u = reinterpret_cast(name); + return JS::SymbolCode(u - 1); +} + static bool PropertySpecNameToId(JSContext *cx, const char *name, MutableHandleId id, js::InternBehavior ib = js::DoNotInternAtom) { if (JS::PropertySpecNameIsSymbol(name)) { - uintptr_t u = reinterpret_cast(name); - id.set(SYMBOL_TO_JSID(cx->wellKnownSymbols().get(u - 1))); + JS::SymbolCode which = PropertySpecNameToSymbolCode(name); + id.set(SYMBOL_TO_JSID(cx->wellKnownSymbols().get(which))); } else { JSAtom *atom = Atomize(cx, name, strlen(name), ib); if (!atom) @@ -5516,6 +5524,33 @@ JS::GetWellKnownSymbol(JSContext *cx, JS::SymbolCode which) return cx->runtime()->wellKnownSymbols->get(uint32_t(which)); } +static bool +PropertySpecNameIsDigits(const char *s) { + if (JS::PropertySpecNameIsSymbol(s)) + return false; + if (!*s) + return false; + for (; *s; s++) { + if (*s < '0' || *s > '9') + return false; + } + return true; +} + +JS_PUBLIC_API(bool) +JS::PropertySpecNameEqualsId(const char *name, HandleId id) +{ + if (JS::PropertySpecNameIsSymbol(name)) { + if (!JSID_IS_SYMBOL(id)) + return false; + Symbol *sym = JSID_TO_SYMBOL(id); + return sym->isWellKnownSymbol() && sym->code() == PropertySpecNameToSymbolCode(name); + } + + MOZ_ASSERT(!PropertySpecNameIsDigits(name)); + return JSID_IS_ATOM(id) && JS_FlatStringEqualsAscii(JSID_TO_ATOM(id), name); +} + JS_PUBLIC_API(bool) JS_Stringify(JSContext *cx, MutableHandleValue vp, HandleObject replacer, HandleValue space, JSONWriteCallback callback, void *data) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index a49b10936f06..50676f8a6040 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4517,6 +4517,9 @@ PropertySpecNameIsSymbol(const char *name) return u != 0 && u - 1 < WellKnownSymbolLimit; } +JS_PUBLIC_API(bool) +PropertySpecNameEqualsId(const char *name, HandleId id); + /* * Create a jsid that does not need to be marked for GC. * diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 711bbd3ca1e2..e152394b8aee 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -423,10 +423,14 @@ struct WellKnownSymbols { js::ImmutableSymbolPtr iterator; - ImmutableSymbolPtr &get(size_t i) { - MOZ_ASSERT(i < JS::WellKnownSymbolLimit); + ImmutableSymbolPtr &get(size_t u) { + MOZ_ASSERT(u < JS::WellKnownSymbolLimit); ImmutableSymbolPtr *symbols = reinterpret_cast(this); - return symbols[i]; + return symbols[u]; + } + + ImmutableSymbolPtr &get(JS::SymbolCode code) { + return get(size_t(code)); } }; diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index 03ae0e5c8fae..31fe8c45b408 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -145,6 +145,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 var version = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).version; var isNightlyBuild = version.endsWith("a1"); var isReleaseBuild = !version.contains("a"); + const jsHasSymbols = typeof Symbol === "function"; var gPrototypeProperties = {}; gPrototypeProperties['Date'] = ["getTime", "getTimezoneOffset", "getYear", "getFullYear", "getUTCFullYear", @@ -197,19 +198,23 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 ["constructor", "toSource", "toString", "apply", "call", "bind", "isGenerator", "length", "name", "arguments", "caller"]; + // Sort an array that may contain symbols as well as strings. + function sortProperties(arr) { + function sortKey(prop) { + return typeof prop + ":" + prop.toString(); + } + arr.sort((a, b) => sortKey(a) < sortKey(b) ? -1 : +1); + } + // Sort all the lists so we don't need to mutate them later (or copy them // again to sort them). for (var c of Object.keys(gPrototypeProperties)) - gPrototypeProperties[c].sort(); + sortProperties(gPrototypeProperties[c]); function filterOut(array, props) { return array.filter(p => props.indexOf(p) == -1); } - function appendUnique(array, vals) { - filterOut(vals, array).forEach(v => array.push(v)); - } - function isTypedArrayClass(classname) { return typedArrayClasses.indexOf(classname) >= 0; } @@ -267,6 +272,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 gPrototypeProperties[classname].filter(id => typeof id === "string").toSource(), "A property on the " + classname + " prototype has changed! You need a security audit from an XPConnect peer"); + if (jsHasSymbols) { + is(Object.getOwnPropertySymbols(localProto).map(uneval).sort().toSource(), + gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), + "A symbol-keyed property on the " + classname + + " prototype has been changed! You need a security audit from an XPConnect peer"); + } let protoProps = filterOut(desiredProtoProps, propsToSkip); let protoCallables = protoProps.filter(name => propertyIsGetter(localProto, name, classname) || @@ -280,6 +291,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 testProtoCallables(protoCallables, xray, xrayProto, localProto); is(Object.getOwnPropertyNames(xrayProto).sort().toSource(), protoProps.toSource(), "getOwnPropertyNames works"); + if (jsHasSymbols) { + is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(), + gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(), + protoProps.toSource(), "getOwnPropertySymbols works"); + } is(xrayProto.constructor, iwin[classname], "constructor property works"); @@ -303,26 +319,35 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 is(d.toLocaleString('de-DE'), d.wrappedJSObject.toLocaleString('de-DE'), "Results match"); } + var uniqueSymbol; + function testObject() { testXray('Object', Cu.unwaiveXrays(Cu.waiveXrays(iwin).Object.create(new iwin.Object())), new iwin.Object(), []); // Construct an object full of tricky things. + let symbolProps = ''; + if (jsHasSymbols) { + uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol'); + symbolProps = `, [uniqueSymbol]: 43, + [Symbol.for("registrySymbolProp")]: 44`; + } var trickyObject = - iwin.eval('new Object({ \ - primitiveProp: 42, objectProp: { foo: 2 }, \ - xoProp: top.location, hasOwnProperty: 10, \ - get getterProp() { return 2; }, \ - set setterProp(x) { }, \ - get getterSetterProp() { return 3; }, \ - set getterSetterProp(x) { }, \ - callableProp: function() { }, \ - nonXrayableProp: new WeakMap() })'); + iwin.eval(`new Object({ + primitiveProp: 42, objectProp: { foo: 2 }, + xoProp: top.location, hasOwnProperty: 10, + get getterProp() { return 2; }, + set setterProp(x) { }, + get getterSetterProp() { return 3; }, + set getterSetterProp(x) { }, + callableProp: function() { }, + nonXrayableProp: new WeakMap() + ${symbolProps} + })`); testTrickyObject(trickyObject); - } -function testArray() { + function testArray() { // The |length| property is generally very weird, especially with respect // to its behavior on the prototype. Array.prototype is actually an Array // instance, and therefore has a vestigial .length. But we don't want to @@ -332,18 +357,25 @@ function testArray() { let propsToSkip = ['length']; testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip); + let symbolProps = ''; + if (jsHasSymbols) { + uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol'); + symbolProps = `trickyArray[uniqueSymbol] = 43; + trickyArray[Symbol.for("registrySymbolProp")] = 44;`; + } var trickyArray = - iwin.eval("var trickyArray = []; \ - trickyArray.primitiveProp = 42; \ - trickyArray.objectProp = { foo: 2 }; \ - trickyArray.xoProp = top.location; \ - trickyArray.hasOwnProperty = 10; \ - Object.defineProperty(trickyArray, 'getterProp', { get: function() { return 2; }}); \ - Object.defineProperty(trickyArray, 'setterProp', { set: function(x) {}}); \ - Object.defineProperty(trickyArray, 'getterSetterProp', { get: function() { return 3; }, set: function(x) {}}); \ - trickyArray.callableProp = function() {}; \ - trickyArray.nonXrayableProp = new WeakMap(); \ - trickyArray;"); + iwin.eval(`var trickyArray = []; + trickyArray.primitiveProp = 42; + trickyArray.objectProp = { foo: 2 }; + trickyArray.xoProp = top.location; + trickyArray.hasOwnProperty = 10; + Object.defineProperty(trickyArray, 'getterProp', { get: function() { return 2; }}); + Object.defineProperty(trickyArray, 'setterProp', { set: function(x) {}}); + Object.defineProperty(trickyArray, 'getterSetterProp', { get: function() { return 3; }, set: function(x) {}}); + trickyArray.callableProp = function() {}; + trickyArray.nonXrayableProp = new WeakMap(); + ${symbolProps} + trickyArray;`); // Test indexed access. trickyArray.wrappedJSObject[9] = "some indexed property"; @@ -366,11 +398,11 @@ function testArray() { is(trickyArray[1], undefined, "Frozen length forbids new properties"); testTrickyObject(trickyArray); -} + } -// Parts of this function are kind of specific to testing Object, but we factor -// it out so that we can re-use the trickyObject stuff on Arrays. -function testTrickyObject(trickyObject) { + // Parts of this function are kind of specific to testing Object, but we factor + // it out so that we can re-use the trickyObject stuff on Arrays. + function testTrickyObject(trickyObject) { // Make sure it looks right under the hood. is(trickyObject.wrappedJSObject.getterProp, 2, "Underlying object has getter"); @@ -382,11 +414,21 @@ function testTrickyObject(trickyObject) { expectedNames.push('length'); is(Object.getOwnPropertyNames(trickyObject).sort().toSource(), expectedNames.sort().toSource(), "getOwnPropertyNames should be filtered correctly"); + if (jsHasSymbols) { + var expectedSymbols = [Symbol.for("registrySymbolProp"), uniqueSymbol]; + is(Object.getOwnPropertySymbols(trickyObject).map(uneval).sort().toSource(), + expectedSymbols.map(uneval).sort().toSource(), + "getOwnPropertySymbols should be filtered correctly"); + } // Test that cloning uses the Xray view. var cloned = Cu.cloneInto(trickyObject, this); is(Object.getOwnPropertyNames(cloned).sort().toSource(), expectedNames.sort().toSource(), "structured clone should use the Xray view"); + if (jsHasSymbols) { + is(Object.getOwnPropertySymbols(cloned).map(uneval).sort().toSource(), + "[]", "structured cloning doesn't clone symbol-keyed properties yet"); + } // Test iteration and in-place modification. Beware of 'expando', which is the property // we placed on the xray proto. diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index fac44a384685..f5e67d3bd867 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -458,21 +458,14 @@ JSXrayTraits::resolveOwnProperty(JSContext *cx, const Wrapper &jsWrapper, return JS_IdToValue(cx, className, desc.value()); } - // Compute the property name we're looking for. Indexed array properties - // are handled above. We'll handle well-known symbols when we start - // supporting Symbol.iterator in bug 918828. - if (!JSID_IS_STRING(id)) - return true; - Rooted str(cx, JSID_TO_FLAT_STRING(id)); - // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. const js::Class *clasp = js::GetObjectClass(target); MOZ_ASSERT(clasp->spec.defined()); - // Scan through the functions. + // Scan through the functions. Indexed array properties are handled above. const JSFunctionSpec *fsMatch = nullptr; for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) { - if (JS_FlatStringEqualsAscii(str, fs->name)) { + if (PropertySpecNameEqualsId(fs->name, id)) { fsMatch = fs; break; } @@ -501,7 +494,7 @@ JSXrayTraits::resolveOwnProperty(JSContext *cx, const Wrapper &jsWrapper, // Scan through the properties. const JSPropertySpec *psMatch = nullptr; for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) { - if (JS_FlatStringEqualsAscii(str, ps->name)) { + if (PropertySpecNameEqualsId(ps->name, id)) { psMatch = ps; break; } @@ -632,6 +625,15 @@ JSXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, return true; } +static bool +MaybeAppend(jsid id, unsigned flags, AutoIdVector &props) +{ + MOZ_ASSERT(!(flags & JSITER_SYMBOLSONLY)); + if (!(flags & JSITER_SYMBOLS) && JSID_IS_SYMBOL(id)) + return true; + return props.append(id); +} + bool JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, AutoIdVector &props) @@ -711,12 +713,12 @@ JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags const js::Class *clasp = js::GetObjectClass(target); MOZ_ASSERT(clasp->spec.defined()); - // Intern all the strings, and pass theme to the caller. + // Convert the method and property names to jsids and pass them to the caller. for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) { - RootedString str(cx, JS_InternString(cx, fs->name)); - if (!str) + jsid id; + if (!PropertySpecNameToPermanentId(cx, fs->name, &id)) return false; - if (!props.append(INTERNED_STRING_TO_JSID(cx, str))) + if (!MaybeAppend(id, flags, props)) return false; } for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) { @@ -727,10 +729,11 @@ JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags MOZ_ASSERT(ps->flags & JSPROP_NATIVE_ACCESSORS, "Self-hosted accessor added to Xrayable class - ping the XPConnect " "module owner about adding test coverage"); - RootedString str(cx, JS_InternString(cx, ps->name)); - if (!str) + + jsid id; + if (!PropertySpecNameToPermanentId(cx, ps->name, &id)) return false; - if (!props.append(INTERNED_STRING_TO_JSID(cx, str))) + if (!MaybeAppend(id, flags, props)) return false; } @@ -1978,7 +1981,7 @@ XrayWrapper::ownPropertyKeys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const { assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); - return enumerate(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN, props); + return enumerate(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); } template From b35ef8b853b428c42a40cde365255a8c5d9817cc Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Thu, 9 Oct 2014 16:05:20 -0500 Subject: [PATCH 37/81] Bug 1075088 - Add telemetry probes for Flash content. r=gfritzsche,mconley --HG-- extra : rebase_source : 2afac68cc01c045c4d4c85d663e0e76bb4e828ae --- browser/modules/PluginContent.jsm | 44 ++++++++++++++++++++ toolkit/components/telemetry/Histograms.json | 36 ++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/browser/modules/PluginContent.jsm b/browser/modules/PluginContent.jsm index c5c560c71de1..1ded633a2c4f 100644 --- a/browser/modules/PluginContent.jsm +++ b/browser/modules/PluginContent.jsm @@ -94,6 +94,9 @@ PluginContent.prototype = { return; } + if (Services.telemetry.canSend) { + this._finishRecordingFlashPluginTelemetry(); + } this.clearPluginDataCache(); }, @@ -378,6 +381,11 @@ PluginContent.prototype = { break; } + if (Services.telemetry.canSend && this._getPluginInfo(plugin).mimetype === + "application/x-shockwave-flash") { + this._recordFlashPluginTelemetry(eventType, plugin); + } + // Show the in-content UI if it's not too big. The crashed plugin handler already did this. if (eventType != "PluginCrashed") { let overlay = this.getPluginUI(plugin, "main"); @@ -407,6 +415,42 @@ PluginContent.prototype = { } }, + _recordFlashPluginTelemetry: function (eventType, plugin) { + if (!this.flashPluginStats) { + this.flashPluginStats = { + instancesCount: 0, + plugins: new WeakSet() + }; + } + + if (!this.flashPluginStats.plugins.has(plugin)) { + // Reporting plugin instance and its dimensions only once. + this.flashPluginStats.plugins.add(plugin); + + this.flashPluginStats.instancesCount++; + + let pluginRect = plugin.getBoundingClientRect(); + Services.telemetry.getHistogramById('FLASH_PLUGIN_WIDTH') + .add(pluginRect.width); + Services.telemetry.getHistogramById('FLASH_PLUGIN_HEIGHT') + .add(pluginRect.height); + Services.telemetry.getHistogramById('FLASH_PLUGIN_AREA') + .add(pluginRect.width * pluginRect.height); + + let state = this._getPluginInfo(plugin).fallbackType; + Services.telemetry.getHistogramById('FLASH_PLUGIN_STATES') + .add(state); + } + }, + + _finishRecordingFlashPluginTelemetry: function () { + if (this.flashPluginStats) { + Services.telemetry.getHistogramById('FLASH_PLUGIN_INSTANCES_ON_PAGE') + .add(this.flashPluginStats.instancesCount); + delete this.flashPluginStats; + } + }, + isKnownPlugin: function (objLoadingContent) { return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) == Ci.nsIObjectLoadingContent.TYPE_PLUGIN); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 61aa3cd0bcce..6704b96c2c18 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -2609,6 +2609,42 @@ "kind": "flag", "description": "A plugin object was successfully invoked as a function" }, + "FLASH_PLUGIN_STATES": { + "expires_in_version": "50", + "kind": "enumerated", + "n_values": 20, + "description": "A flash object's initialization state" + }, + "FLASH_PLUGIN_AREA": { + "expires_in_version": "50", + "kind": "exponential", + "low": "256", + "high": "16777216", + "n_buckets": 50, + "description": "Flash object area (width * height)" + }, + "FLASH_PLUGIN_WIDTH": { + "expires_in_version": "50", + "kind": "linear", + "low": "1", + "high": "2000", + "n_buckets": 50, + "description": "Flash object width" + }, + "FLASH_PLUGIN_HEIGHT": { + "expires_in_version": "50", + "kind": "linear", + "low": "1", + "high": "2000", + "n_buckets": 50, + "description": "Flash object height" + }, + "FLASH_PLUGIN_INSTANCES_ON_PAGE": { + "expires_in_version": "50", + "kind": "enumerated", + "n_values": 30, + "description": "Flash object instances count on page" + }, "MOZ_SQLITE_OPEN_MS": { "expires_in_version": "default", "kind": "exponential", From 7ce9ecf3272331f71fa1a2eefa9efefaac0e45e8 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Fri, 17 Oct 2014 11:47:11 +1300 Subject: [PATCH 38/81] Bug 1057891 - Remove some comments from MediaKey*.webidl now that the issue the comments were about has been resolved. r=bz --- dom/webidl/MediaKeySession.webidl | 4 ---- dom/webidl/MediaKeys.webidl | 1 - 2 files changed, 5 deletions(-) diff --git a/dom/webidl/MediaKeySession.webidl b/dom/webidl/MediaKeySession.webidl index c9ea12402099..4a1274ad50d7 100644 --- a/dom/webidl/MediaKeySession.webidl +++ b/dom/webidl/MediaKeySession.webidl @@ -21,7 +21,6 @@ interface MediaKeySession : EventTarget { readonly attribute unrestricted double expiration; - // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457 readonly attribute Promise closed; [NewObject, Throws] @@ -31,15 +30,12 @@ interface MediaKeySession : EventTarget { Promise load(DOMString sessionId); // session operations - // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457 [NewObject, Throws] Promise update((ArrayBufferView or ArrayBuffer) response); - // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457 [NewObject, Throws] Promise close(); - // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457 [NewObject, Throws] Promise remove(); diff --git a/dom/webidl/MediaKeys.webidl b/dom/webidl/MediaKeys.webidl index 4187ca0f11d2..7467ed68b7f0 100644 --- a/dom/webidl/MediaKeys.webidl +++ b/dom/webidl/MediaKeys.webidl @@ -20,7 +20,6 @@ interface MediaKeys { [NewObject, Throws] MediaKeySession createSession(optional SessionType sessionType = "temporary"); - // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457 [NewObject, Throws] Promise setServerCertificate((ArrayBufferView or ArrayBuffer) serverCertificate); From 3d87c3e63b3af714c66fde2ac71ec197a4625bf0 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 17 Oct 2014 08:12:01 +0900 Subject: [PATCH 39/81] Bug 922912 - Fold gkmedias.dll back into xul.dll. r=ehsan --- configure.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.in b/configure.in index bd1217908254..12c5bf615182 100644 --- a/configure.in +++ b/configure.in @@ -7953,8 +7953,7 @@ dnl = dnl ======================================================== MOZ_ARG_HEADER(Static build options) -if test "$OS_ARCH" = "WINNT"; then - GKMEDIAS_SHARED_LIBRARY=1 +if test -n "$GKMEDIAS_SHARED_LIBRARY"; then AC_DEFINE(GKMEDIAS_SHARED_LIBRARY) fi AC_SUBST(GKMEDIAS_SHARED_LIBRARY) From 2597924fe114ba9fbff430fefdbec2e68a8ef1ee Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 17 Oct 2014 08:12:27 +0900 Subject: [PATCH 40/81] Bug 609976 - Fold mozjs.dll back into xul.dll. r=ehsan --- configure.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/configure.in b/configure.in index 12c5bf615182..de4de42a2f42 100644 --- a/configure.in +++ b/configure.in @@ -3796,11 +3796,6 @@ dnl system libffi Support dnl ======================================================== MOZ_CONFIG_FFI() -# split JS out by default to avoid VS2005 PGO crash (bug 591836). -if test "$OS_ARCH" = "WINNT"; then - JS_SHARED_LIBRARY=1 -fi - MOZ_ARG_ENABLE_BOOL(shared-js, [ --enable-shared-js Create a shared JavaScript library.], From 556a6221739b0059e8405e649c73f33d90f76e42 Mon Sep 17 00:00:00 2001 From: John Daggett Date: Fri, 17 Oct 2014 09:15:29 +0900 Subject: [PATCH 41/81] Bug 1083599 - fixup small problem with userfont logging. r=m_kato --- gfx/thebes/gfxUserFontSet.cpp | 8 +++--- gfx/thebes/gfxUserFontSet.h | 6 +++-- layout/style/FontFaceSet.cpp | 41 +++++++++++++++---------------- layout/style/nsFontFaceLoader.cpp | 4 +-- layout/style/nsFontFaceLoader.h | 4 +++ 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index 583821cc4b81..4e4aa5225686 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -413,7 +413,7 @@ gfxUserFontEntry::LoadNextSrc() mItalic); mFontSet->SetLocalRulesUsed(); if (fe) { - LOG(("fontset (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n", + LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n", mFontSet, mSrcIndex, NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(), NS_ConvertUTF16toUTF8(mFamilyName).get(), @@ -430,7 +430,7 @@ gfxUserFontEntry::LoadNextSrc() SetLoadState(STATUS_LOADED); return; } else { - LOG(("fontset (%p) [src %d] failed local: (%s) for (%s)\n", + LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n", mFontSet, mSrcIndex, NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(), NS_ConvertUTF16toUTF8(mFamilyName).get())); @@ -647,7 +647,7 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength) nsAutoCString fontURI; mSrcList[mSrcIndex].mURI->GetSpec(fontURI); LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n", - this, mSrcIndex, fontURI.get(), + mFontSet, mSrcIndex, fontURI.get(), NS_ConvertUTF16toUTF8(mFamilyName).get(), uint32_t(mFontSet->mGeneration))); } @@ -662,7 +662,7 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength) mSrcList[mSrcIndex].mURI->GetSpec(fontURI); LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)" " error making platform font\n", - this, mSrcIndex, fontURI.get(), + mFontSet, mSrcIndex, fontURI.get(), NS_ConvertUTF16toUTF8(mFamilyName).get())); } #endif diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h index b2a5bfa8da7a..07e0d2132702 100644 --- a/gfx/thebes/gfxUserFontSet.h +++ b/gfx/thebes/gfxUserFontSet.h @@ -467,6 +467,10 @@ public: mLocalRulesUsed = true; } +#ifdef PR_LOGGING + static PRLogModuleInfo* GetUserFontsLog(); +#endif + protected: // Protected destructor, to discourage deletion outside of Release(): virtual ~gfxUserFontSet(); @@ -512,8 +516,6 @@ protected: // true when local names have been looked up, false otherwise bool mLocalRulesUsed; - - static PRLogModuleInfo* GetUserFontsLog(); }; // acts a placeholder until the real font is downloaded diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp index 8b5493ae8007..7d01dc79dcab 100644 --- a/layout/style/FontFaceSet.cpp +++ b/layout/style/FontFaceSet.cpp @@ -35,19 +35,9 @@ using namespace mozilla; using namespace mozilla::dom; -#ifdef PR_LOGGING -static PRLogModuleInfo* -GetFontFaceSetLog() -{ - static PRLogModuleInfo* sLog; - if (!sLog) - sLog = PR_NewLogModule("fontfaceset"); - return sLog; -} -#endif /* PR_LOGGING */ - -#define LOG(args) PR_LOG(GetFontFaceSetLog(), PR_LOG_DEBUG, args) -#define LOG_ENABLED() PR_LOG_TEST(GetFontFaceSetLog(), PR_LOG_DEBUG) +#define LOG(args) PR_LOG(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG, args) +#define LOG_ENABLED() PR_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), \ + PR_LOG_DEBUG) NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet) @@ -415,14 +405,16 @@ FontFaceSet::StartLoad(gfxUserFontEntry* aUserFontEntry, return NS_ERROR_OUT_OF_MEMORY; #ifdef PR_LOGGING - if (LOG_ENABLED()) { + if (PR_LOG_TEST(nsFontFaceLoader::GetFontDownloaderLog(), + PR_LOG_DEBUG)) { nsAutoCString fontURI, referrerURI; aFontFaceSrc->mURI->GetSpec(fontURI); if (aFontFaceSrc->mReferrer) aFontFaceSrc->mReferrer->GetSpec(referrerURI); - LOG(("fontdownloader (%p) download start - font uri: (%s) " - "referrer uri: (%s)\n", - fontLoader.get(), fontURI.get(), referrerURI.get())); + PR_LOG(nsFontFaceLoader::GetFontDownloaderLog(), PR_LOG_DEBUG, + ("fontdownloader (%p) download start - font uri: (%s) " + "referrer uri: (%s)\n", + fontLoader.get(), fontURI.get(), referrerURI.get())); } #endif @@ -613,6 +605,13 @@ FontFaceSet::UpdateRules(const nsTArray& aRules) // local rules have been rebuilt, so clear the flag mUserFontSet->mLocalRulesUsed = false; +#if PR_LOGGING + LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", + mUserFontSet.get(), + (modified ? "modified" : "not modified"), + mRuleFaces.Length())); +#endif + return modified; } @@ -1046,13 +1045,13 @@ FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry, break; } } - message.AppendLiteral("\nsource: "); + message.AppendLiteral(" source: "); message.Append(fontURI); #ifdef PR_LOGGING - if (PR_LOG_TEST(GetFontFaceSetLog(), PR_LOG_DEBUG)) { - PR_LOG(GetFontFaceSetLog(), PR_LOG_DEBUG, - ("userfonts (%p) %s", this, message.get())); + if (PR_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG)) { + PR_LOG(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG, + ("userfonts (%p) %s", mUserFontSet.get(), message.get())); } #endif diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp index 77535b9ebf87..12bb2e1d8baf 100644 --- a/layout/style/nsFontFaceLoader.cpp +++ b/layout/style/nsFontFaceLoader.cpp @@ -26,8 +26,8 @@ using namespace mozilla; #ifdef PR_LOGGING -static PRLogModuleInfo* -GetFontDownloaderLog() +PRLogModuleInfo* +nsFontFaceLoader::GetFontDownloaderLog() { static PRLogModuleInfo* sLog; if (!sLog) diff --git a/layout/style/nsFontFaceLoader.h b/layout/style/nsFontFaceLoader.h index 6aaa33ea1640..e1e6d2d71cba 100644 --- a/layout/style/nsFontFaceLoader.h +++ b/layout/style/nsFontFaceLoader.h @@ -46,6 +46,10 @@ public: nsIURI* aTargetURI, nsISupports* aContext); +#ifdef PR_LOGGING + static PRLogModuleInfo* GetFontDownloaderLog(); +#endif + protected: virtual ~nsFontFaceLoader(); From 07b09c0948a20e178b4fd86032a7e5d16e47f631 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Thu, 16 Oct 2014 17:36:22 -0700 Subject: [PATCH 42/81] Bug 609976 needed a CLOBBER to fix the CLOSED TREE --- CLOBBER | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLOBBER b/CLOBBER index acdf4b6cff29..269ff19f9f45 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 1061335 - CLOBBER for Win32 compiler update. +Bug 609976 - CLOBBER From ab50d5106b0b41e7ff82fa8b12c04c7e00b380cc Mon Sep 17 00:00:00 2001 From: William Chen Date: Thu, 16 Oct 2014 11:53:36 -0700 Subject: [PATCH 43/81] Bug 1064211 - Keep CustomElementData alive while on processing stack. r=mrbkap --- content/base/public/FragmentOrElement.h | 2 +- content/base/src/nsDocument.cpp | 9 ++------- content/base/src/nsDocument.h | 7 ++++++- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/content/base/public/FragmentOrElement.h b/content/base/public/FragmentOrElement.h index c53bf7894672..95c7299a6977 100644 --- a/content/base/public/FragmentOrElement.h +++ b/content/base/public/FragmentOrElement.h @@ -412,7 +412,7 @@ public: /** * Web components custom element data. */ - nsAutoPtr mCustomElementData; + nsRefPtr mCustomElementData; }; protected: diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 9d06af8500b4..188337415703 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -5844,12 +5844,7 @@ nsDocument::ProcessTopElementQueue(bool aIsBaseQueue) { MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); - if (sProcessingStack.isNothing()) { - // If XPCOM shutdown has reset the processing stack, don't do anything. - return; - } - - nsTArray& stack = *sProcessingStack; + nsTArray>& stack = *sProcessingStack; uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr); if (aIsBaseQueue && firstQueue != 0) { @@ -5886,7 +5881,7 @@ nsDocument::RegisterEnabled() } // static -Maybe> +Maybe>> nsDocument::sProcessingStack; // static diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index dcb52223036b..3a9f8a3deabe 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -325,6 +325,8 @@ private: // being created flag. struct CustomElementData { + NS_INLINE_DECL_REFCOUNTING(CustomElementData) + explicit CustomElementData(nsIAtom* aType); // Objects in this array are transient and empty after each microtask // checkpoint. @@ -346,6 +348,9 @@ struct CustomElementData // Empties the callback queue. void RunCallbackQueue(); + +private: + virtual ~CustomElementData() {} }; // The required information for a custom element as defined in: @@ -1520,7 +1525,7 @@ private: // CustomElementData in this array, separated by nullptr that // represent the boundaries of the items in the stack. The first // queue in the stack is the base element queue. - static mozilla::Maybe> sProcessingStack; + static mozilla::Maybe>> sProcessingStack; // Flag to prevent re-entrance into base element queue as described in the // custom elements speicification. From 7d62f7b0cd5ee833ad203e87e769f51d1d9e968d Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Fri, 17 Oct 2014 10:09:32 +0900 Subject: [PATCH 44/81] Bug 1083629 Use nsHTMLEditor::IsAcceptableInputEvent() instead of nsEditor::IsDescendantOfEditorRoot() for checking if a mouse down event target is in focused HTML editor r=ehsan --- editor/libeditor/nsHTMLEditorEventListener.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/editor/libeditor/nsHTMLEditorEventListener.cpp b/editor/libeditor/nsHTMLEditorEventListener.cpp index e05caa14c331..68c777cab81d 100644 --- a/editor/libeditor/nsHTMLEditorEventListener.cpp +++ b/editor/libeditor/nsHTMLEditorEventListener.cpp @@ -72,6 +72,11 @@ nsresult nsHTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent) { nsHTMLEditor* htmlEditor = GetHTMLEditor(); + // Contenteditable should disregard mousedowns outside it. + // IsAcceptableInputEvent() checks it for a mouse event. + if (!htmlEditor->IsAcceptableInputEvent(aMouseEvent)) { + return NS_OK; + } // Detect only "context menu" click // XXX This should be easier to do! @@ -93,11 +98,6 @@ nsHTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent) NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER); nsCOMPtr element = do_QueryInterface(target); - // Contenteditable should disregard mousedowns outside it - if (element && !htmlEditor->IsDescendantOfEditorRoot(element)) { - return NS_OK; - } - if (isContextClick || (buttonNumber == 0 && clickCount == 2)) { nsCOMPtr selection; mEditor->GetSelection(getter_AddRefs(selection)); From 6e5f97300995c21e29f977a52a9bddf56d8ffc17 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 16 Oct 2014 21:40:00 -0400 Subject: [PATCH 45/81] Bug 1083887 - Package the clang-cl runtime library for ASAN on Windows ASAN builds; r=gps --- browser/installer/Makefile.in | 3 +++ browser/installer/package-manifest.in | 3 +++ build/Makefile.in | 7 +++++++ configure.in | 10 ++++++++++ 4 files changed, 23 insertions(+) diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in index b04f52cf1ef8..ffb5c647689f 100644 --- a/browser/installer/Makefile.in +++ b/browser/installer/Makefile.in @@ -137,6 +137,9 @@ DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX) ifdef CLANG_CXX DEFINES += -DCLANG_CXX endif +ifdef CLANG_CL +DEFINES += -DCLANG_CL +endif libs:: $(MAKE) -C $(DEPTH)/browser/locales langpack diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 037a7d941f7e..03290e42a8bf 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -921,4 +921,7 @@ bin/libfreebl_32int64_3.so #ifdef CLANG_CXX @BINPATH@/llvm-symbolizer #endif +#ifdef CLANG_CL +@BINPATH@/clang_rt.asan_dynamic-i386.dll +#endif #endif diff --git a/build/Makefile.in b/build/Makefile.in index 31ddf7cc2dab..b497dbf07980 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -66,6 +66,13 @@ LLDBINIT_FINAL_TARGET_FILES := $(DEPTH)/.lldbinit LLDBINIT_FINAL_TARGET_DEST = $(FINAL_TARGET) INSTALL_TARGETS += LLDBINIT_FINAL_TARGET +ifeq (1_1,$(MOZ_ASAN)_$(CLANG_CL)) +# Install the clang-cl runtime library for ASAN next to the binaries we produce. +CLANG_RT_ASAN_FILES := $(MOZ_CLANG_RT_ASAN_LIB_PATH) +CLANG_RT_ASAN_DEST = $(FINAL_TARGET) +INSTALL_TARGETS += CLANG_RT_ASAN +endif + include $(topsrcdir)/config/rules.mk TARGET_DEPTH = .. diff --git a/configure.in b/configure.in index de4de42a2f42..4cf7ae0676a3 100644 --- a/configure.in +++ b/configure.in @@ -1260,6 +1260,16 @@ MOZ_ARG_ENABLE_BOOL(address-sanitizer, MOZ_ASAN= ) if test -n "$MOZ_ASAN"; then MOZ_LLVM_HACKS=1 + if test -n "$CLANG_CL"; then + # Look for clang_rt.asan_dynamic-i386.dll + MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll + # We use MOZ_PATH_PROG in order to get a Windows style path. + MOZ_PATH_PROG(MOZ_CLANG_RT_ASAN_LIB_PATH, $MOZ_CLANG_RT_ASAN_LIB) + if test -z "$MOZ_CLANG_RT_ASAN_LIB_PATH"; then + AC_MSG_ERROR([Couldn't find $MOZ_CLANG_RT_ASAN_LIB. It should be available in the same location as clang-cl.]) + fi + AC_SUBST(MOZ_CLANG_RT_ASAN_LIB_PATH) + fi AC_DEFINE(MOZ_ASAN) MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) fi From 94a20717aa99d128661e4eadf3ce583e5d608c1b Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 16 Oct 2014 21:40:22 -0400 Subject: [PATCH 46/81] Bug 1083616 - Build on clang-cl with MSVC 2013 with fallback emulation; r=gps We officially test MSVC2013 builds now, so it makes sense to emulate the same compiler when building with clang-cl. Also, we need to build with fallback mode, since clang-cl doesn't still support SEH. We also need to pass these flags to NSS too for the same reason. --- configure.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.in b/configure.in index 4cf7ae0676a3..96b69269af31 100644 --- a/configure.in +++ b/configure.in @@ -506,6 +506,11 @@ case "$target" in if test -z "$CLANG_CL"; then AC_DEFINE(HAVE_SEH_EXCEPTIONS) else + # Build on clang-cl with MSVC 2013 with fallback emulation. + CFLAGS="$CFLAGS -fmsc-version=1800 -fallback" + CXXFLAGS="$CXXFLAGS -fmsc-version=1800 -fallback" + # Send our CFLAGS to NSS too + MOZ_CFLAGS_NSS=1 AC_DEFINE_UNQUOTED(GTEST_HAS_SEH, 0) fi From 08fbad97d7b00c8054530ce98f3acbc55e75d1ac Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Thu, 16 Oct 2014 22:03:34 -0400 Subject: [PATCH 47/81] Bug 1083936 - WebGL2: Add texImage3D - r=jgilbert,smaug --- dom/canvas/WebGL2Context.h | 6 +- dom/canvas/WebGL2ContextTextures.cpp | 113 +++++++++++++++++++++++ dom/canvas/WebGLContextUtils.cpp | 1 + dom/webidl/WebGL2RenderingContext.webidl | 5 + gfx/gl/GLContext.cpp | 2 +- gfx/gl/GLContext.h | 15 +++ gfx/gl/GLContextSymbols.h | 6 ++ 7 files changed, 146 insertions(+), 2 deletions(-) diff --git a/dom/canvas/WebGL2Context.h b/dom/canvas/WebGL2Context.h index 23b6eeaa0c3b..16fd6a018eb7 100644 --- a/dom/canvas/WebGL2Context.h +++ b/dom/canvas/WebGL2Context.h @@ -66,7 +66,11 @@ public: void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); void TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); - + void TexImage3D(GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLenum format, GLenum type, + const Nullable &pixels, + ErrorResult& rv); void TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, diff --git a/dom/canvas/WebGL2ContextTextures.cpp b/dom/canvas/WebGL2ContextTextures.cpp index 21028d09e5b6..106349b1cfec 100644 --- a/dom/canvas/WebGL2ContextTextures.cpp +++ b/dom/canvas/WebGL2ContextTextures.cpp @@ -204,6 +204,119 @@ WebGL2Context::TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat } } +void +WebGL2Context::TexImage3D(GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLenum format, GLenum type, + const Nullable &pixels, + ErrorResult& rv) +{ + if (IsContextLost()) + return; + + void* data; + size_t dataLength; + js::Scalar::Type jsArrayType; + if (pixels.IsNull()) { + data = nullptr; + dataLength = 0; + jsArrayType = js::Scalar::TypeMax; + } else { + const ArrayBufferView& view = pixels.Value(); + view.ComputeLengthAndData(); + + data = view.Data(); + dataLength = view.Length(); + jsArrayType = JS_GetArrayBufferViewType(view.Obj()); + } + + const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage; + const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D; + + if (!ValidateTexImageTarget(target, func, dims)) + return; + + TexImageTarget texImageTarget = target; + + if (!ValidateTexImage(texImageTarget, level, internalformat, + 0, 0, 0, + width, height, depth, + border, format, type, func, dims)) + { + return; + } + + if (!ValidateTexInputData(type, jsArrayType, func, dims)) + return; + + TexInternalFormat effectiveInternalFormat = + EffectiveInternalFormatFromInternalFormatAndType(internalformat, type); + + if (effectiveInternalFormat == LOCAL_GL_NONE) { + return ErrorInvalidOperation("texImage3D: bad combination of internalformat and type"); + } + + // we need to find the exact sized format of the source data. Slightly abusing + // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format + // is the same thing as an unsized internalformat. + TexInternalFormat effectiveSourceFormat = + EffectiveInternalFormatFromInternalFormatAndType(format, type); + MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated format/type combo earlier + const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat); + MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here. + size_t srcTexelSize = srcbitsPerTexel / 8; + + CheckedUint32 checked_neededByteLength = + GetImageSize(height, width, depth, srcTexelSize, mPixelStoreUnpackAlignment); + + if (!checked_neededByteLength.isValid()) + return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size"); + + uint32_t bytesNeeded = checked_neededByteLength.value(); + + if (dataLength && dataLength < bytesNeeded) + return ErrorInvalidOperation("texImage3D: not enough data for operation (need %d, have %d)", + bytesNeeded, dataLength); + + WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget); + + if (!tex) + return ErrorInvalidOperation("texImage3D: no texture is bound to this target"); + + if (tex->IsImmutable()) { + return ErrorInvalidOperation( + "texImage3D: disallowed because the texture " + "bound to this target has already been made immutable by texStorage3D"); + } + + GLenum driverType = LOCAL_GL_NONE; + GLenum driverInternalFormat = LOCAL_GL_NONE; + GLenum driverFormat = LOCAL_GL_NONE; + DriverFormatsFromEffectiveInternalFormat(gl, + effectiveInternalFormat, + &driverInternalFormat, + &driverFormat, + &driverType); + + MakeContextCurrent(); + GetAndFlushUnderlyingGLErrors(); + gl->fTexImage3D(texImageTarget.get(), level, + driverInternalFormat, + width, height, depth, + 0, driverFormat, driverType, + data); + GLenum error = GetAndFlushUnderlyingGLErrors(); + if (error) { + return GenerateWarning("texImage3D generated error %s", ErrorName(error)); + } + + tex->SetImageInfo(texImageTarget, level, + width, height, depth, + effectiveInternalFormat, + data ? WebGLImageDataStatus::InitializedImageData + : WebGLImageDataStatus::UninitializedImageData); +} + void WebGL2Context::TexSubImage3D(GLenum rawTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, diff --git a/dom/canvas/WebGLContextUtils.cpp b/dom/canvas/WebGLContextUtils.cpp index 55b93c1b2e7e..e32cfb2b03a9 100644 --- a/dom/canvas/WebGLContextUtils.cpp +++ b/dom/canvas/WebGLContextUtils.cpp @@ -867,6 +867,7 @@ InfoFrom(WebGLTexImageFunc func, WebGLTexDimensions dims) } case WebGLTexDimensions::Tex3D: switch (func) { + case WebGLTexImageFunc::TexImage: return "texImage3D"; case WebGLTexImageFunc::TexSubImage: return "texSubImage3D"; case WebGLTexImageFunc::CopyTexSubImage: return "copyTexSubImage3D"; case WebGLTexImageFunc::CompTexSubImage: return "compressedTexSubImage3D"; diff --git a/dom/webidl/WebGL2RenderingContext.webidl b/dom/webidl/WebGL2RenderingContext.webidl index 4784f83224f3..6b950dcfe462 100644 --- a/dom/webidl/WebGL2RenderingContext.webidl +++ b/dom/webidl/WebGL2RenderingContext.webidl @@ -342,6 +342,11 @@ interface WebGL2RenderingContext : WebGLRenderingContext void texStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); void texStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); + [Throws] + void texImage3D(GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLenum format, + GLenum type, ArrayBufferView? pixels); [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, ArrayBufferView? pixels); diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 7d517e522a6b..ca673de0af1e 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -1253,7 +1253,7 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl) if (IsSupported(GLFeature::texture_3D)) { SymLoadStruct coreSymbols[] = { - // TexImage3D is not required for WebGL2 so not queried here. + { (PRFuncPtr*) &mSymbols.fTexImage3D, { "TexImage3D", nullptr } }, { (PRFuncPtr*) &mSymbols.fTexSubImage3D, { "TexSubImage3D", nullptr } }, END_SYMBOLS }; diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index 3e386faa8faf..16827ee141e2 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -3132,6 +3132,21 @@ public: // ----------------------------------------------------------------------------- // 3D Textures + void fTexImage3D(GLenum target, GLint level, + GLint internalFormat, + GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLenum format, GLenum type, + const GLvoid * data) + { + BEFORE_GL_CALL; + ASSERT_SYMBOL_PRESENT(fTexImage3D); + mSymbols.fTexImage3D(target, level, internalFormat, + width, height, depth, + border, format, type, + data); + AFTER_GL_CALL; + } + void fTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels) diff --git a/gfx/gl/GLContextSymbols.h b/gfx/gl/GLContextSymbols.h index c4839a35440d..d02377f2e64f 100644 --- a/gfx/gl/GLContextSymbols.h +++ b/gfx/gl/GLContextSymbols.h @@ -642,6 +642,12 @@ struct GLContextSymbols PFNGLGETFRAGDATALOCATIONPROC fGetFragDataLocation; // 3D Textures + typedef void (GLAPIENTRY * PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, + GLenum internalFormat, + GLenum width, GLsizei height, GLsizei depth, + GLint border, GLenum format, GLenum type, + const GLvoid* pixels); + PFNGLTEXIMAGE3DPROC fTexImage3D; typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, From 75bb32cc855678f4da5aaf00d94de7bcf776773d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 16 Oct 2014 19:02:51 -0700 Subject: [PATCH 48/81] Bug 1083624 - Fix assertion failure in Factory::GetDirect3D10Device(). r=bas. --HG-- extra : rebase_source : e28b1575b8bd4706d7d203696a1280c76e103ef6 --- gfx/2d/Factory.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index f18245c85d1e..820484112f2b 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -548,11 +548,12 @@ Factory::SetDirect3D10Device(ID3D10Device1 *aDevice) ID3D10Device1* Factory::GetDirect3D10Device() - { #ifdef DEBUG - UINT mode = mD3D10Device->GetExceptionMode(); - MOZ_ASSERT(0 == mode); + if (mD3D10Device) { + UINT mode = mD3D10Device->GetExceptionMode(); + MOZ_ASSERT(0 == mode); + } #endif return mD3D10Device; } From d9516dbd03d1670922ca9332069dcec3433980b2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 16 Oct 2014 19:03:19 -0700 Subject: [PATCH 49/81] Bug 1084114 - Use a better buffer growth strategy during XDR encoding. r=luke. --HG-- extra : rebase_source : 800768a4534cedf857dbc8bff2fcc149d6adff00 --- js/src/vm/Xdr.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/src/vm/Xdr.cpp b/js/src/vm/Xdr.cpp index d20e22b9fbe3..cd7ca1dc69b7 100644 --- a/js/src/vm/Xdr.cpp +++ b/js/src/vm/Xdr.cpp @@ -29,9 +29,11 @@ XDRBuffer::grow(size_t n) { MOZ_ASSERT(n > size_t(limit - cursor)); - const size_t MEM_BLOCK = 8192; + const size_t MIN_CAPACITY = 8192; size_t offset = cursor - base; - size_t newCapacity = JS_ROUNDUP(offset + n, MEM_BLOCK); + size_t newCapacity = mozilla::RoundUpPow2(offset + n); + if (newCapacity < MIN_CAPACITY) + newCapacity = MIN_CAPACITY; if (isUint32Overflow(newCapacity)) { js::gc::AutoSuppressGC suppressGC(cx()); JS_ReportErrorNumber(cx(), js_GetErrorMessage, nullptr, JSMSG_TOO_BIG_TO_ENCODE); From 92956470535262f5e990227734e8c196ca656d4d Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 16 Oct 2014 23:16:39 -0400 Subject: [PATCH 50/81] Bug 1083229. Give FrameStateVector a nonzero minimal length, so we don't end up doing a bunch of realloc/copy as we walk our stack. r=fitzgen --- js/src/vm/SavedStacks.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/vm/SavedStacks.h b/js/src/vm/SavedStacks.h index d328265983f1..b7b6fc5f7296 100644 --- a/js/src/vm/SavedStacks.h +++ b/js/src/vm/SavedStacks.h @@ -245,7 +245,7 @@ class SavedStacks { frames(cx) { } - typedef Vector FrameStateVector; + typedef Vector FrameStateVector; inline FrameStateVector *operator->() { return &frames; } inline FrameState &operator[](size_t i) { return frames[i]; } From 6ca6944e5141cfac44568efcb44231291155c9ee Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 16 Oct 2014 23:18:42 -0400 Subject: [PATCH 51/81] Bug 1083232. Don't refcount the principals in FrameState, since it has stack lifetime and the principals will be kept alive by the JS stack while he FrameState is alive. r=fitzgen --- js/src/vm/SavedStacks.cpp | 9 ++------- js/src/vm/SavedStacks.h | 3 +++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index 96a4981ff9f6..6daedc7e59b2 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -748,8 +748,6 @@ SavedStacks::FrameState::FrameState(const FrameIter &iter) name(iter.isNonEvalFunctionFrame() ? iter.functionDisplayAtom() : nullptr), location() { - if (principals) - JS_HoldPrincipals(principals); } SavedStacks::FrameState::FrameState(const FrameState &fs) @@ -757,13 +755,10 @@ SavedStacks::FrameState::FrameState(const FrameState &fs) name(fs.name), location(fs.location) { - if (principals) - JS_HoldPrincipals(principals); } -SavedStacks::FrameState::~FrameState() { - if (principals) - JS_DropPrincipals(TlsPerThreadData.get()->runtimeFromMainThread(), principals); +SavedStacks::FrameState::~FrameState() +{ } void diff --git a/js/src/vm/SavedStacks.h b/js/src/vm/SavedStacks.h index b7b6fc5f7296..7ebde362a951 100644 --- a/js/src/vm/SavedStacks.h +++ b/js/src/vm/SavedStacks.h @@ -233,6 +233,9 @@ class SavedStacks { void trace(JSTracer *trc); + // Note: we don't have to hold/drop principals, because we're + // only alive while the stack is being walked and during this + // time the principals are kept alive by the stack itself. JSPrincipals *principals; JSAtom *name; LocationValue location; From d775ad4acfd2c0a3b36acec6caa73343ef14a78e Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Thu, 9 Oct 2014 23:46:16 -0700 Subject: [PATCH 52/81] Bug 1081010 - Part 1: Fix -Wswitch warning in ipc/chromium. r=tabraldes --- ipc/chromium/src/base/message_loop.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipc/chromium/src/base/message_loop.cc b/ipc/chromium/src/base/message_loop.cc index b8e7138ce11f..27c1e3625a87 100644 --- a/ipc/chromium/src/base/message_loop.cc +++ b/ipc/chromium/src/base/message_loop.cc @@ -125,6 +125,9 @@ MessageLoop::MessageLoop(Type type) pump_ = new mozilla::ipc::MessagePumpForNonMainUIThreads(); return; #endif + default: + // Create one of Chromium's standard MessageLoop types below. + break; } #if defined(OS_WIN) From 6d71c4447fff4ef502fbca5fb54b6d7f7ed26cce Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 8 Oct 2014 09:51:01 -0700 Subject: [PATCH 53/81] Bug 1081561 - Treat -Wignored-qualifiers warnings as errors in SpiderMonkey's C++ code. r=glandium --- js/src/configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/configure.in b/js/src/configure.in index 7c537c3df566..a9e758e11522 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1294,7 +1294,6 @@ if test "$GNU_CXX"; then # -Wwrite-strings - catches non-const char* pointers to string literals # _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall" - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wignored-qualifiers" _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wsign-compare" _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wtype-limits" @@ -1303,6 +1302,7 @@ if test "$GNU_CXX"; then _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=comment" _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=empty-body" _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=endif-labels" + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=ignored-qualifiers" _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=int-to-pointer-cast" _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=missing-braces" _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=overloaded-virtual" From 984856dd8ca2a70f42ff3a20700184bebc791cbd Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 17 Oct 2014 14:09:17 +0900 Subject: [PATCH 54/81] Fixup for bug 1081034 - Add missing #ifdef to avoid build failure on desktop linux. r=me,npotb,DONTBUILD --- mozglue/linker/ElfLoader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mozglue/linker/ElfLoader.cpp b/mozglue/linker/ElfLoader.cpp index 72f560dc523e..09373e9ad403 100644 --- a/mozglue/linker/ElfLoader.cpp +++ b/mozglue/linker/ElfLoader.cpp @@ -512,7 +512,9 @@ ElfLoader::~ElfLoader() /* Release self_elf and libc */ self_elf = nullptr; +#if defined(ANDROID) libc = nullptr; +#endif /* Build up a list of all library handles with direct (external) references. * We actually skip system library handles because we want to keep at least From d1eae13c7d34c8f4ca64754d264f57032848ccd0 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 16 Oct 2014 22:14:02 -0700 Subject: [PATCH 55/81] Bug 1084039: Clean up AnimationPlayerCollection::PseudoElement(). r=birtles --- layout/style/AnimationCommon.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h index 9288246411bd..3c483bb7eab0 100644 --- a/layout/style/AnimationCommon.h +++ b/layout/style/AnimationCommon.h @@ -18,6 +18,7 @@ #include "mozilla/dom/Nullable.h" #include "nsStyleStruct.h" #include "mozilla/Attributes.h" +#include "mozilla/Assertions.h" #include "mozilla/FloatingPoint.h" #include "nsCSSPseudoElements.h" #include "nsCycleCollectionParticipant.h" @@ -251,15 +252,17 @@ struct AnimationPlayerCollection : public PRCList mElementProperty == nsGkAtoms::animationsOfAfterProperty; } - nsString PseudoElement() + nsString PseudoElement() const { if (IsForElement()) { return EmptyString(); - } else if (IsForBeforePseudo()) { - return NS_LITERAL_STRING("::before"); - } else { - return NS_LITERAL_STRING("::after"); } + if (IsForBeforePseudo()) { + return NS_LITERAL_STRING("::before"); + } + MOZ_ASSERT(IsForAfterPseudo(), + "::before & ::after should be the only pseudo-elements here"); + return NS_LITERAL_STRING("::after"); } mozilla::dom::Element* GetElementToRestyle() const; From 15364358ea1fc655fcd5446bcfd5cc0105c14560 Mon Sep 17 00:00:00 2001 From: Jocelyn Liu Date: Fri, 17 Oct 2014 14:25:16 +0800 Subject: [PATCH 56/81] Bug 1083646: Overwrite BLE config in bluedroid to enable BLE for bluetooth2. r=shuang --- dom/bluetooth2/bluedroid/b2g_bdroid_buildcfg.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dom/bluetooth2/bluedroid/b2g_bdroid_buildcfg.h b/dom/bluetooth2/bluedroid/b2g_bdroid_buildcfg.h index f28123bef5be..f6982409efb6 100644 --- a/dom/bluetooth2/bluedroid/b2g_bdroid_buildcfg.h +++ b/dom/bluetooth2/bluedroid/b2g_bdroid_buildcfg.h @@ -39,4 +39,9 @@ /* CHLD values */ #define BTA_AG_CHLD_VAL "(0,1,2,3)" +/* BLE Feature */ +#define BTA_GATT_INCLUDED TRUE +#define BLE_INCLUDED TRUE +#define SMP_INCLUDED TRUE + #endif /* B2G_BDROID_BUILDCFG_H */ From c21d8d69b4017d7c0f4840cc84a363ac185eaf9c Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Fri, 17 Oct 2014 16:05:21 +0900 Subject: [PATCH 57/81] Bug 1071433 - Change order of audio/3gpp association for Gallery Apps. r=bz The AUDIO_3GPP has to come after the VIDEO_3GPP entry because the Gallery app on Firefox OS depends on the "3gp" extension mapping to the "video/3gpp" MIME type. --- uriloader/exthandler/nsExternalHelperAppService.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 4285bef727be..25e5d49b2bde 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -544,7 +544,6 @@ static nsExtraMimeTypeEntry extraMimeEntries [] = { AUDIO_OGG, "opus", "Opus Audio" }, #ifdef MOZ_WIDGET_GONK { AUDIO_AMR, "amr", "Adaptive Multi-Rate Audio" }, - { AUDIO_3GPP, "3gpp,3gp", "3GPP Audio" }, #endif { VIDEO_WEBM, "webm", "Web Media Video" }, { AUDIO_WEBM, "webm", "Web Media Audio" }, @@ -555,6 +554,12 @@ static nsExtraMimeTypeEntry extraMimeEntries [] = { AUDIO_WAV, "wav", "Waveform Audio" }, { VIDEO_3GPP, "3gpp,3gp", "3GPP Video" }, { VIDEO_3GPP2,"3g2", "3GPP2 Video" }, +#ifdef MOZ_WIDGET_GONK + // The AUDIO_3GPP has to come after the VIDEO_3GPP entry because the Gallery + // app on Firefox OS depends on the "3gp" extension mapping to the + // "video/3gpp" MIME type. + { AUDIO_3GPP, "3gpp,3gp", "3GPP Audio" }, +#endif { AUDIO_MIDI, "mid", "Standard MIDI Audio" } }; From 7228e8624f670918d1e980cb624a52221e4699dc Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Fri, 17 Oct 2014 08:12:26 +0100 Subject: [PATCH 58/81] Bug 1083425 - WebSocket should remove its nsIRequest from the docShell when disconnected, r=smaug --- content/base/src/WebSocket.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/content/base/src/WebSocket.cpp b/content/base/src/WebSocket.cpp index 1ba4fc4fa0f0..bfde6f97a4ae 100644 --- a/content/base/src/WebSocket.cpp +++ b/content/base/src/WebSocket.cpp @@ -193,6 +193,8 @@ public: WorkerPrivate* mWorkerPrivate; nsAutoPtr mWorkerFeature; + nsWeakPtr mWeakLoadGroup; + private: ~WebSocketImpl() { @@ -528,8 +530,7 @@ WebSocketImpl::DisconnectInternal() { AssertIsOnMainThread(); - nsCOMPtr loadGroup; - GetLoadGroup(getter_AddRefs(loadGroup)); + nsCOMPtr loadGroup = do_QueryReferent(mWeakLoadGroup); if (loadGroup) { loadGroup->RemoveRequest(this, nullptr, NS_OK); } @@ -1390,6 +1391,8 @@ WebSocketImpl::InitializeConnection() NS_ENSURE_SUCCESS(rv, rv); rv = loadGroup->AddRequest(this, nullptr); NS_ENSURE_SUCCESS(rv, rv); + + mWeakLoadGroup = do_GetWeakReference(loadGroup); } // manually adding loadinfo to the channel since it From 5dcb538c2869c7f15588013ec84e006e3721ed19 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Thu, 16 Oct 2014 14:11:19 -0500 Subject: [PATCH 59/81] Bug 1083325 - Gracefully deal with null ssl status when serializing/deserializing TransportSecurityInfo. r=dkeeler --- .../manager/ssl/src/TransportSecurityInfo.cpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/security/manager/ssl/src/TransportSecurityInfo.cpp b/security/manager/ssl/src/TransportSecurityInfo.cpp index cc9cfbfc9bb3..878435b0ef0a 100644 --- a/security/manager/ssl/src/TransportSecurityInfo.cpp +++ b/security/manager/ssl/src/TransportSecurityInfo.cpp @@ -289,8 +289,8 @@ TransportSecurityInfo::GetInterface(const nsIID & uuid, void * *result) // of the previous value. This is so when older versions attempt to // read a newer serialized TransportSecurityInfo, they will actually // fail and return NS_ERROR_FAILURE instead of silently failing. -#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0xf40a, 0x4060, \ - { 0xb2, 0xe1, 0x62, 0xab, 0x2b, 0x85, 0x26, 0xa9 } } +#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0xda1f, 0x4008, \ + { 0xac, 0x3c, 0x52, 0x86, 0x21, 0x54, 0x10, 0x70 } } static NS_DEFINE_CID(kTransportSecurityInfoMagic, TRANSPORTSECURITYINFOMAGIC); NS_IMETHODIMP @@ -325,9 +325,15 @@ TransportSecurityInfo::Write(nsIObjectOutputStream* stream) if (NS_FAILED(rv)) { return rv; } + + // For successful connections and for connections with overridable errors, + // mSSLStatus will be non-null. However, for connections with non-overridable + // errors, it will be null. nsCOMPtr serializable(mSSLStatus); - rv = stream->WriteCompoundObject(serializable, NS_GET_IID(nsISSLStatus), - true); + rv = NS_WriteOptionalCompoundObject(stream, + serializable, + NS_GET_IID(nsISSLStatus), + true); if (NS_FAILED(rv)) { return rv; } @@ -385,16 +391,18 @@ TransportSecurityInfo::Read(nsIObjectInputStream* stream) if (NS_FAILED(rv)) { return rv; } + mErrorCode = 0; + + // For successful connections and for connections with overridable errors, + // mSSLStatus will be non-null. For connections with non-overridable errors, + // it will be null. nsCOMPtr supports; - rv = stream->ReadObject(true, getter_AddRefs(supports)); + rv = NS_ReadOptionalObject(stream, true, getter_AddRefs(supports)); if (NS_FAILED(rv)) { return rv; } mSSLStatus = reinterpret_cast(supports.get()); - if (!mSSLStatus) { - return NS_ERROR_FAILURE; - } nsCOMPtr failedCertChainSupports; rv = NS_ReadOptionalObject(stream, true, getter_AddRefs(failedCertChainSupports)); From b7c481e472fa741f777ba918e24944c07893b971 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 16 Oct 2014 16:19:58 -0700 Subject: [PATCH 60/81] Bug 1081072: Remove scrollbar opacity transition from b2g content.css file, since it interacts poorly with ScrollbarActivity.cpp. r=fabrice --- b2g/chrome/content/content.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/b2g/chrome/content/content.css b/b2g/chrome/content/content.css index 645ab3d384ac..d354dc2713d2 100644 --- a/b2g/chrome/content/content.css +++ b/b2g/chrome/content/content.css @@ -28,7 +28,6 @@ html xul|scrollbar { background-image: none !important; border: 0px solid transparent !important; pointer-events: none; - opacity: 1; } xul|scrollbar[orient="vertical"] { @@ -56,7 +55,6 @@ xul|scrollbar[orient="horizontal"] xul|thumb { xul|scrollbar:not([active="true"]), xul|scrollbar[disabled] { opacity: 0; - transition: opacity 1s ease; } xul|scrollbarbutton { From 21f3b13913938970fc8cb6c9e09cab659d4daacf Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 16:45:55 -0700 Subject: [PATCH 61/81] Bumping gaia.json for 3 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/fa83194807e1 Author: Kevin Grandon Desc: Merge pull request #25237 from mikehenrty/KevinGrandon-bug_1083571_bholley_wip_patch Bug 1083571 - Remove __exposedProps__ from marionette shims ======== https://hg.mozilla.org/integration/gaia-central/rev/f4546793283f Author: Michael Henretty Desc: Bug 1083571 - Waive XRays for sms message event ======== https://hg.mozilla.org/integration/gaia-central/rev/40dd8297f0c3 Author: Kevin Grandon Desc: Bug 1083571 - Remove __exposedProps__ from marionette shims --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index b969aaa98df5..097c5aa1ec7a 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "63436aa17e7fa3ad521fdeffdc22b81c36e5d69b", + "revision": "fa83194807e102168ff248c55bae0e7ffa3e05bd", "repo_path": "/integration/gaia-central" } From 3e725b0e8999bc6115ed416d62a492821dc672e1 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 16:52:03 -0700 Subject: [PATCH 62/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index fc8928aa36da..345ebbb81cb3 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index ffce04229cc9..12e3b87afaac 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index f3edc6c260d6..5abc889a9473 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 6cce451a7299..df657a4a8cb0 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index ffce04229cc9..12e3b87afaac 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 131ce198a071..3552823eb268 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index 29f57f20b0f2..5172e99b80c3 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index ddf7a56c18b0..6845c4af4ad8 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index b95704c693f0..f88819fe8b2a 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 699092edd0d2..54e91a497275 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 6882bd535b45..bbcd2d793b6b 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + From 8b4f37b04596203bac4797a84e8e59cc45af6655 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 18:00:48 -0700 Subject: [PATCH 63/81] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/aaeecc098e23 Author: Kevin Grandon Desc: Merge pull request #25176 from sudheesh001/Tests Bug 1061513 - Fixes hint errors in tests/ directory r=kgrandon ======== https://hg.mozilla.org/integration/gaia-central/rev/1b7e06118099 Author: sudheesh001 Desc: Bug 1061513 Fixes hint errors in tests/ directory r=kgrandon --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 097c5aa1ec7a..de8f065fb5b5 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "fa83194807e102168ff248c55bae0e7ffa3e05bd", + "revision": "aaeecc098e230c26ff11fb5b66b6ed97e58d9822", "repo_path": "/integration/gaia-central" } From 335cfb0fb4f6bb41b408c90578a7f57049d0ce5b Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 18:06:56 -0700 Subject: [PATCH 64/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 345ebbb81cb3..0fed7ac5831d 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 12e3b87afaac..517c795399ee 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 5abc889a9473..3dd06f90e89a 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index df657a4a8cb0..bede7a020cd9 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 12e3b87afaac..517c795399ee 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 3552823eb268..09bf4d40c996 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index 5172e99b80c3..a4739c38654b 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 6845c4af4ad8..b01a06d64930 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index f88819fe8b2a..a830f8adaf85 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 54e91a497275..92ccc5f4b9e8 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index bbcd2d793b6b..4120dde2a223 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + From a174929b90eb118a08615db11f334b1a1f1d8c45 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 19:55:46 -0700 Subject: [PATCH 65/81] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/899748204da9 Author: Arthur Chen Desc: Merge pull request #25159 from crh0716/1071366 Bug 1071366 - Add the missed case of erasing call forwarding settings r=jaoo ======== https://hg.mozilla.org/integration/gaia-central/rev/4aa05dba0080 Author: Arthur Chen Desc: Bug 1071366 - Add the missed case of erasing call forwarding settings --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index de8f065fb5b5..ee3ff293441d 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "aaeecc098e230c26ff11fb5b66b6ed97e58d9822", + "revision": "899748204da92eae2ca95c20cc4d4aa2676d6a66", "repo_path": "/integration/gaia-central" } From a685228cc67237b75e7bd146c89da813a95a722f Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 20:02:19 -0700 Subject: [PATCH 66/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 0fed7ac5831d..1ba822bb1697 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 517c795399ee..9940783797bd 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 3dd06f90e89a..6b3ffd5e47b0 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index bede7a020cd9..4995ef4eb452 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 517c795399ee..9940783797bd 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 09bf4d40c996..feaa2a54a108 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index a4739c38654b..f5e7ea1f8e1f 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index b01a06d64930..d3721e475dbd 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index a830f8adaf85..88831fa031ff 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 92ccc5f4b9e8..926e55c48fe2 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 4120dde2a223..6f06c557a20d 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + From cb52f28eda8f9dbbed1486351dd6f42bd1338580 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 20:25:48 -0700 Subject: [PATCH 67/81] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/17b27134fb2a Author: Arthur Chen Desc: Merge pull request #25120 from crh0716/1082234 Bug 1082234 - Set a default value when the stored value is unavailable r=alive ======== https://hg.mozilla.org/integration/gaia-central/rev/2e9ce4bd3375 Author: Arthur Chen Desc: Bug 1082234 - Set a default value when the stored value is unavailable --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index ee3ff293441d..1ad543226a7a 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "899748204da92eae2ca95c20cc4d4aa2676d6a66", + "revision": "17b27134fb2a343703a2c2e0bf243932de4f1989", "repo_path": "/integration/gaia-central" } From 36cfd14e3ff7b21129efae9d4aba3d310df06ea2 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 20:37:15 -0700 Subject: [PATCH 68/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 1ba822bb1697..f3d3bd31db82 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 9940783797bd..8badc3492ffd 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 6b3ffd5e47b0..0872339d6899 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 4995ef4eb452..7ea86ff6035b 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 9940783797bd..8badc3492ffd 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index feaa2a54a108..800974b0afa7 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index f5e7ea1f8e1f..6b13f02e43f6 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index d3721e475dbd..f8a37db1e54a 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 88831fa031ff..eaef732383b3 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 926e55c48fe2..9410def1f256 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 6f06c557a20d..623e385a5cd5 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + From f624e22e24f5695ffe946ca19b53720d4e92463a Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 23:10:47 -0700 Subject: [PATCH 69/81] Bumping gaia.json for 4 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/db4d4031b50d Author: Marina Desc: Merge pull request #25131 from gitmai/bug-1080473-difference-between-byApp-and-global Bug 1080473 - [Usage] Noticeable difference between by application break... r=salva ======== https://hg.mozilla.org/integration/gaia-central/rev/facf167e6e37 Author: mai Desc: Bug 1080473 - [Usage] Noticeable difference between by application breakdown totals and the total displayed in chart and widget ======== https://hg.mozilla.org/integration/gaia-central/rev/81e4e4f83035 Author: George Desc: Merge pull request #24930 from cctuan/1067231 Bug 1067231 - [Keyboard][Text Selection] Tapping the touch caret should show Paste utility bubble if clipboard is not empty ======== https://hg.mozilla.org/integration/gaia-central/rev/a605b824f250 Author: cctuan Desc: Bug 1082399 - [Keyboard][Text Selection] Tapping the touch caret should show Paste utility bubble if clipboard is not empty --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 1ad543226a7a..0c39f3d64c15 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "17b27134fb2a343703a2c2e0bf243932de4f1989", + "revision": "db4d4031b50dff08d6a4f61f97ab2a598efb30bb", "repo_path": "/integration/gaia-central" } From 3f26b06406b562bc6dbd0dca73bdae36eee08fa8 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 23:17:01 -0700 Subject: [PATCH 70/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index f3d3bd31db82..ed8a66639d95 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 8badc3492ffd..bcf8a1a30189 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 0872339d6899..227107f0e971 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 7ea86ff6035b..225e9f46c893 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 8badc3492ffd..bcf8a1a30189 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 800974b0afa7..d4a4b20eb20d 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index 6b13f02e43f6..aa8dd715083d 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index f8a37db1e54a..b6a64838e9ab 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index eaef732383b3..ad84028cadd4 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 9410def1f256..f6db04fd1e04 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 623e385a5cd5..9cd7431b7dd7 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + From f242e680d12c2fd36e83210f64abc7f524184976 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 23:25:48 -0700 Subject: [PATCH 71/81] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/ba323456efdf Author: Marina Desc: Merge pull request #25134 from empoalp/bug_1059715_4 Bug 1059715 - VCard reader refactor to read contacts before importing th... ======== https://hg.mozilla.org/integration/gaia-central/rev/4a40de3598fb Author: David Garcia Desc: Bug 1059715 - VCard reader refactor to read contacts before importing them r=sergi --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 0c39f3d64c15..a238875a775a 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "db4d4031b50dff08d6a4f61f97ab2a598efb30bb", + "revision": "ba323456efdf2afb3388d82b109102de5b69b612", "repo_path": "/integration/gaia-central" } From 17b5450c07cc57b2aeb8cadf72f1cb59bec2789b Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 23:31:57 -0700 Subject: [PATCH 72/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index ed8a66639d95..7bbad1257345 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index bcf8a1a30189..955112d38eb8 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 227107f0e971..28802855d9a9 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 225e9f46c893..db6ef7e6e5cf 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index bcf8a1a30189..955112d38eb8 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index d4a4b20eb20d..eb357a9b4921 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index aa8dd715083d..e5cef0dfc704 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index b6a64838e9ab..aba07f03c533 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index ad84028cadd4..77acf627f6e1 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index f6db04fd1e04..3510846f9fd7 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 9cd7431b7dd7..d844f969e45e 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + From 84817115c61da9ac7caeb15c2dbd41268a0fc966 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 23:55:50 -0700 Subject: [PATCH 73/81] Bumping gaia.json for 6 gaia revision(s) a=gaia-bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ======== https://hg.mozilla.org/integration/gaia-central/rev/9730965fdf0e Author: Fernando Jiménez Moreno Desc: Merge pull request #25035 from ferjm/bug1053163.inappicon Bug 1053163 - B2G emulator debug mochitests and Mulet reftests attempt t... ======== https://hg.mozilla.org/integration/gaia-central/rev/d64d875d7297 Author: Fernando Jiménez Desc: Bug 1053163 - B2G emulator debug mochitests and Mulet reftests attempt to connect to inapp-pay-test.paas.allizom.org. r=fabrice ======== https://hg.mozilla.org/integration/gaia-central/rev/069fa8b3e76d Author: Gabriele Svelto Desc: Merge pull request #25210 from gabrielesvelto/bug-1081028-cdma-call-onhold Bug 1081028 - Do not try to hold a single call in CDMA mode since it's not supported r=drs ======== https://hg.mozilla.org/integration/gaia-central/rev/9cca4956c161 Author: Gabriele Svelto Desc: Bug 1081028 - Do not try to hold a single call in CDMA mode since it's not supported r=drs ======== https://hg.mozilla.org/integration/gaia-central/rev/341ec79b7657 Author: Jose M. Cantera Desc: Merge pull request #25180 from jmcanterafonseca/fix_scroll_2 Bug 1083100 - [Contacts] In the contacts selection screen horizontal scr... ======== https://hg.mozilla.org/integration/gaia-central/rev/1b9fabf074b0 Author: Jose M. Cantera Desc: Bug 1083100 - [Contacts] In the contacts selection screen horizontal scrolling is enabled --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index a238875a775a..55c939ff36f9 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "ba323456efdf2afb3388d82b109102de5b69b612", + "revision": "9730965fdf0e6f68641bc9b835ab268d3b435873", "repo_path": "/integration/gaia-central" } From a67d200d00757983d3f5a6a2d6ce3f52fd03c39e Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Thu, 16 Oct 2014 23:57:35 -0700 Subject: [PATCH 74/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 7bbad1257345..aad844587368 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 955112d38eb8..a28a33637783 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 28802855d9a9..476b5245a127 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index db6ef7e6e5cf..5576108bc38e 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 955112d38eb8..a28a33637783 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index eb357a9b4921..71ea6ef521d4 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index e5cef0dfc704..0e23f9552b2c 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index aba07f03c533..4627f3e8bbde 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 77acf627f6e1..85161619baeb 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 3510846f9fd7..de869cc0e9fc 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index d844f969e45e..0bb308e2ec6b 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + From 2ce1258e62360b38e47af2ec049cc97986f29a85 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 17 Oct 2014 00:10:50 -0700 Subject: [PATCH 75/81] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/48560b945308 Author: Jose M. Cantera Desc: Merge pull request #25136 from jmcanterafonseca/fix_search_position_2 Bug 1065731 - When adding a number to an existing contact, search icon d... ======== https://hg.mozilla.org/integration/gaia-central/rev/dec760b9aa98 Author: Jose M. Cantera Desc: Bug 1065731 - When adding a number to an existing contact, search icon does nothing --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 55c939ff36f9..4ea26a3be9c2 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "9730965fdf0e6f68641bc9b835ab268d3b435873", + "revision": "48560b9453082ec39a354afc0ca29f3060c42c52", "repo_path": "/integration/gaia-central" } From d69f951c333913129f47b4058e2235613180f0a1 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 17 Oct 2014 00:17:03 -0700 Subject: [PATCH 76/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index aad844587368..6d4948568e09 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index a28a33637783..f8764fa679e7 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 476b5245a127..5f27e56ad43f 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 5576108bc38e..16093b43e2e1 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index a28a33637783..f8764fa679e7 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 71ea6ef521d4..1cc3a80d1d39 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index 0e23f9552b2c..d26a2d2e58b3 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 4627f3e8bbde..bab73f365cdd 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 85161619baeb..f85b205ffc2b 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index de869cc0e9fc..7b738579a186 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 0bb308e2ec6b..1aaa1f9fbfff 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + From cfababa43a964bc665ad1296dd9ef50d1d753ae9 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 17 Oct 2014 00:25:48 -0700 Subject: [PATCH 77/81] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/73ba4234231f Author: Jose M. Cantera Desc: Merge pull request #25251 from empoalp/bug_1084225 Bug 1084225 - Missing image thumbnail dependency in VCardReader r=jmcf ======== https://hg.mozilla.org/integration/gaia-central/rev/eb75f3492da8 Author: David Garcia Desc: Bug 1084225 - Missing image thumbnail dependency in VCardReader r=jmcf --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 4ea26a3be9c2..f008d5d09639 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "48560b9453082ec39a354afc0ca29f3060c42c52", + "revision": "73ba4234231f12d55219bd2e932b6478a7ee5888", "repo_path": "/integration/gaia-central" } From 0879dce3f133367433474bb3ecae692d7f827c99 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 17 Oct 2014 00:31:56 -0700 Subject: [PATCH 78/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 6d4948568e09..6ae032e55929 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index f8764fa679e7..a73170f6c6f1 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 5f27e56ad43f..51ada69b5ca5 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 16093b43e2e1..4e29e87253ad 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index f8764fa679e7..a73170f6c6f1 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 1cc3a80d1d39..8c0c154b89f4 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index d26a2d2e58b3..c25256467244 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index bab73f365cdd..6f2bb41cc5c3 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index f85b205ffc2b..fc359b18b7d5 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 7b738579a186..2e67fc939535 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 1aaa1f9fbfff..fdc6d496755a 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + From d0fa9bef04a2c5a2bddce3aacbc474b9d1cdb979 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Fri, 17 Oct 2014 10:19:40 +0200 Subject: [PATCH 79/81] Bug 987560 - Greatly refactor generator implementation. Patch mostly written by Andy Wingo. r=wingo --- js/src/Makefile.in | 1 + js/src/builtin/Generator.js | 104 ++++ js/src/frontend/BytecodeEmitter.cpp | 300 ++++++---- js/src/frontend/FullParseHandler.h | 36 ++ js/src/frontend/ParseNode.h | 9 +- js/src/frontend/Parser.cpp | 131 +++-- js/src/frontend/Parser.h | 1 + js/src/frontend/SyntaxParseHandler.h | 3 + js/src/gc/Barrier.h | 2 +- .../tests/debug/Frame-onPop-generators-01.js | 5 +- .../debug/Frame-onPop-star-generators-01.js | 4 +- js/src/jit-test/tests/debug/resumption-04.js | 9 +- js/src/jit-test/tests/debug/resumption-06.js | 4 +- .../jit-test/tests/generators/throw-closes.js | 49 ++ js/src/jit-test/tests/generators/wrappers.js | 34 ++ .../jit-test/tests/saved-stacks/generators.js | 7 +- js/src/jit/BaselineJIT.cpp | 7 +- js/src/jit/Ion.cpp | 4 - js/src/jsatom.cpp | 1 - js/src/jscntxt.cpp | 22 +- js/src/jscntxt.h | 7 - js/src/jsinfer.cpp | 2 + js/src/jsiter.cpp | 551 +----------------- js/src/jsiter.h | 33 -- js/src/jsobj.cpp | 2 - js/src/jsopcode.h | 16 +- js/src/jsreflect.cpp | 18 +- js/src/moz.build | 1 + js/src/vm/CommonPropertyNames.h | 4 + js/src/vm/GeneratorObject.cpp | 329 +++++++++++ js/src/vm/GeneratorObject.h | 185 +++++- js/src/vm/GlobalObject.h | 3 + js/src/vm/Interpreter.cpp | 103 ++-- js/src/vm/Interpreter.h | 27 +- js/src/vm/Opcodes.h | 62 +- js/src/vm/ScopeObject.cpp | 4 +- js/src/vm/SelfHosting.cpp | 171 ++++++ js/src/vm/Stack-inl.h | 76 ++- js/src/vm/Stack.cpp | 50 +- js/src/vm/Stack.h | 74 +-- js/src/vm/Xdr.h | 2 +- 41 files changed, 1465 insertions(+), 988 deletions(-) create mode 100644 js/src/builtin/Generator.js create mode 100644 js/src/jit-test/tests/generators/throw-closes.js create mode 100644 js/src/jit-test/tests/generators/wrappers.js create mode 100644 js/src/vm/GeneratorObject.cpp diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 372bcbd88f06..ada06a5ab413 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -315,6 +315,7 @@ selfhosting_srcs := \ $(srcdir)/builtin/Array.js \ $(srcdir)/builtin/Date.js \ $(srcdir)/builtin/Error.js \ + $(srcdir)/builtin/Generator.js \ $(srcdir)/builtin/Intl.js \ $(srcdir)/builtin/IntlData.js \ $(srcdir)/builtin/Iterator.js \ diff --git a/js/src/builtin/Generator.js b/js/src/builtin/Generator.js new file mode 100644 index 000000000000..6bea65305fa8 --- /dev/null +++ b/js/src/builtin/Generator.js @@ -0,0 +1,104 @@ +/* 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/. */ + +function StarGeneratorNext(val) { + if (!IsObject(this) || !IsStarGeneratorObject(this)) + return callFunction(CallStarGeneratorMethodIfWrapped, this, val, "StarGeneratorNext"); + + if (StarGeneratorObjectIsClosed(this)) + return { value: undefined, done: true }; + + if (GeneratorIsRunning(this)) + ThrowError(JSMSG_NESTING_GENERATOR); + + try { + return resumeGenerator(this, val, 'next'); + } catch (e) { + if (!StarGeneratorObjectIsClosed(this)) + GeneratorSetClosed(this); + throw e; + } +} + +function StarGeneratorThrow(val) { + if (!IsObject(this) || !IsStarGeneratorObject(this)) + return callFunction(CallStarGeneratorMethodIfWrapped, this, val, "StarGeneratorThrow"); + + if (StarGeneratorObjectIsClosed(this)) + throw val; + + if (GeneratorIsRunning(this)) + ThrowError(JSMSG_NESTING_GENERATOR); + + try { + return resumeGenerator(this, val, 'throw'); + } catch (e) { + if (!StarGeneratorObjectIsClosed(this)) + GeneratorSetClosed(this); + throw e; + } +} + +function LegacyGeneratorNext(val) { + if (!IsObject(this) || !IsLegacyGeneratorObject(this)) + return callFunction(CallLegacyGeneratorMethodIfWrapped, this, val, "LegacyGeneratorNext"); + + if (LegacyGeneratorObjectIsClosed(this)) + ThrowStopIteration(); + + if (GeneratorIsRunning(this)) + ThrowError(JSMSG_NESTING_GENERATOR); + + try { + return resumeGenerator(this, val, 'next'); + } catch(e) { + if (!LegacyGeneratorObjectIsClosed(this)) + GeneratorSetClosed(this); + throw e; + } +} + +function LegacyGeneratorThrow(val) { + if (!IsObject(this) || !IsLegacyGeneratorObject(this)) + return callFunction(CallLegacyGeneratorMethodIfWrapped, this, val, "LegacyGeneratorThrow"); + + if (LegacyGeneratorObjectIsClosed(this)) + throw val; + + if (GeneratorIsRunning(this)) + ThrowError(JSMSG_NESTING_GENERATOR); + + try { + return resumeGenerator(this, val, 'throw'); + } catch(e) { + if (!LegacyGeneratorObjectIsClosed(this)) + GeneratorSetClosed(this); + throw e; + } +} + +// Called by js::CloseIterator. +function LegacyGeneratorCloseInternal() { + assert(IsObject(this), "Not an object: " + ToString(this)); + assert(IsLegacyGeneratorObject(this), "Not a legacy generator object: " + ToString(this)); + assert(!LegacyGeneratorObjectIsClosed(this), "Already closed: " + ToString(this)); + assert(!CloseNewbornLegacyGeneratorObject(this), "Newborn: " + ToString(this)); + + if (GeneratorIsRunning(this)) + ThrowError(JSMSG_NESTING_GENERATOR); + + resumeGenerator(this, undefined, 'close'); + if (!LegacyGeneratorObjectIsClosed(this)) + CloseClosingLegacyGeneratorObject(this); +} + +function LegacyGeneratorClose() { + if (!IsObject(this) || !IsLegacyGeneratorObject(this)) + return callFunction(CallLegacyGeneratorMethodIfWrapped, this, "LegacyGeneratorClose"); + + if (LegacyGeneratorObjectIsClosed(this) || CloseNewbornLegacyGeneratorObject(this)) + return; + + callFunction(LegacyGeneratorCloseInternal, this); +} diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 065c193e62cb..70e9b45515cb 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -31,6 +31,7 @@ #include "frontend/Parser.h" #include "frontend/TokenStream.h" #include "vm/Debugger.h" +#include "vm/GeneratorObject.h" #include "vm/Stack.h" #include "jsatominlines.h" @@ -1989,10 +1990,10 @@ CheckSideEffects(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool default: /* - * All of PNK_INC, PNK_DEC, PNK_THROW, PNK_YIELD, and PNK_YIELD_STAR - * have direct effects. Of the remaining unary-arity node types, we - * can't easily prove that the operand never denotes an object with - * a toString or valueOf method. + * All of PNK_INC, PNK_DEC and PNK_THROW have direct effects. Of + * the remaining unary-arity node types, we can't easily prove that + * the operand never denotes an object with a toString or valueOf + * method. */ *answer = true; return true; @@ -2973,13 +2974,6 @@ frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNo bce->switchToMain(); } - if (funbox->isGenerator()) { - bce->switchToProlog(); - if (Emit1(cx, bce, JSOP_GENERATOR) < 0) - return false; - bce->switchToMain(); - } - /* * Emit a prologue for run-once scripts which will deoptimize JIT code if * the script ends up running multiple times via foo.caller related @@ -2996,18 +2990,27 @@ frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNo if (!EmitTree(cx, bce, body)) return false; - // If we fall off the end of an ES6 generator, return a boxed iterator - // result object of the form { value: undefined, done: true }. - if (bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isStarGenerator()) { - if (!EmitPrepareIteratorResult(cx, bce)) + // If we fall off the end of a generator, do a final yield. + if (bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isGenerator()) { + if (bce->sc->asFunctionBox()->isStarGenerator() && !EmitPrepareIteratorResult(cx, bce)) return false; + if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) return false; - if (!EmitFinishIteratorResult(cx, bce, true)) + + if (bce->sc->asFunctionBox()->isStarGenerator() && !EmitFinishIteratorResult(cx, bce, true)) + return false; + + ScopeCoordinate sc; + // We know that .generator is on the top scope chain node, as we are + // at the function end. + sc.setHops(0); + MOZ_ALWAYS_TRUE(LookupAliasedNameSlot(bce, bce->script, cx->names().dotGenerator, &sc)); + if (!EmitAliasedVarOp(cx, JSOP_GETALIASEDVAR, sc, DontCheckLexical, bce)) return false; // No need to check for finally blocks, etc as in EmitReturn. - if (Emit1(cx, bce, JSOP_RETURN) < 0) + if (Emit1(cx, bce, JSOP_FINALYIELD) < 0) return false; } @@ -5463,15 +5466,26 @@ EmitReturn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) */ ptrdiff_t top = bce->offset(); - if (Emit1(cx, bce, JSOP_RETURN) < 0) - return false; + bool isGenerator = bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isGenerator(); + if (Emit1(cx, bce, isGenerator ? JSOP_SETRVAL : JSOP_RETURN) < 0) + return false; NonLocalExitScope nle(cx, bce); if (!nle.prepareForNonLocalJump(nullptr)) return false; - if (top + static_cast(JSOP_RETURN_LENGTH) != bce->offset()) { + if (isGenerator) { + ScopeCoordinate sc; + // We know that .generator is on the top scope chain node, as we just + // exited nested scopes. + sc.setHops(0); + MOZ_ALWAYS_TRUE(LookupAliasedNameSlot(bce, bce->script, cx->names().dotGenerator, &sc)); + if (!EmitAliasedVarOp(cx, JSOP_GETALIASEDVAR, sc, DontCheckLexical, bce)) + return false; + if (Emit1(cx, bce, JSOP_FINALYIELDRVAL) < 0) + return false; + } else if (top + static_cast(JSOP_RETURN_LENGTH) != bce->offset()) { bce->code()[top] = JSOP_SETRVAL; if (Emit1(cx, bce, JSOP_RETRVAL) < 0) return false; @@ -5481,7 +5495,41 @@ EmitReturn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) } static bool -EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter) +EmitYield(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) +{ + MOZ_ASSERT(bce->sc->isFunctionBox()); + + if (pn->getOp() == JSOP_YIELD) { + if (bce->sc->asFunctionBox()->isStarGenerator()) { + if (!EmitPrepareIteratorResult(cx, bce)) + return false; + } + if (pn->pn_left) { + if (!EmitTree(cx, bce, pn->pn_left)) + return false; + } else { + if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) + return false; + } + if (bce->sc->asFunctionBox()->isStarGenerator()) { + if (!EmitFinishIteratorResult(cx, bce, false)) + return false; + } + } else { + MOZ_ASSERT(pn->getOp() == JSOP_INITIALYIELD); + } + + if (!EmitTree(cx, bce, pn->pn_right)) + return false; + + if (Emit1(cx, bce, pn->getOp()) < 0) + return false; + + return true; +} + +static bool +EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter, ParseNode *gen) { MOZ_ASSERT(bce->sc->isFunctionBox()); MOZ_ASSERT(bce->sc->asFunctionBox()->isStarGenerator()); @@ -5500,12 +5548,13 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter) return false; CheckTypeSet(cx, bce, JSOP_CALL); - int depth = bce->stackDepth; - MOZ_ASSERT(depth >= 1); - // Initial send value is undefined. if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) // ITER RECEIVED return false; + + int depth = bce->stackDepth; + MOZ_ASSERT(depth >= 2); + ptrdiff_t initialSend = -1; if (EmitBackPatchOp(cx, bce, &initialSend) < 0) // goto initialSend return false; @@ -5514,17 +5563,21 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter) StmtInfoBCE stmtInfo(cx); PushStatementBCE(bce, &stmtInfo, STMT_TRY, bce->offset()); ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_TRY); + ptrdiff_t tryStart = bce->offset(); // tryStart: if (noteIndex < 0 || Emit1(cx, bce, JSOP_TRY) < 0) return false; - ptrdiff_t tryStart = bce->offset(); // tryStart: - MOZ_ASSERT(bce->stackDepth == depth + 1); + MOZ_ASSERT(bce->stackDepth == depth); + + // Load the generator object. + if (!EmitTree(cx, bce, gen)) // ITER RESULT GENOBJ + return false; // Yield RESULT as-is, without re-boxing. if (Emit1(cx, bce, JSOP_YIELD) < 0) // ITER RECEIVED return false; // Try epilogue. - if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, bce->offset() - tryStart + JSOP_TRY_LENGTH)) + if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, bce->offset() - tryStart)) return false; ptrdiff_t subsequentSend = -1; if (EmitBackPatchOp(cx, bce, &subsequentSend) < 0) // goto subsequentSend @@ -5532,8 +5585,10 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter) ptrdiff_t tryEnd = bce->offset(); // tryEnd: // Catch location. - // THROW? = 'throw' in ITER // ITER - bce->stackDepth = (uint32_t) depth; + bce->stackDepth = uint32_t(depth); // ITER RESULT + if (Emit1(cx, bce, JSOP_POP) < 0) // ITER + return false; + // THROW? = 'throw' in ITER if (Emit1(cx, bce, JSOP_EXCEPTION) < 0) // ITER EXCEPTION return false; if (Emit1(cx, bce, JSOP_SWAP) < 0) // EXCEPTION ITER @@ -5557,7 +5612,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter) SetJumpOffsetAt(bce, checkThrow); // delegate: // RESULT = ITER.throw(EXCEPTION) // EXCEPTION ITER - bce->stackDepth = (uint32_t) depth + 1; + bce->stackDepth = uint32_t(depth); if (Emit1(cx, bce, JSOP_DUP) < 0) // EXCEPTION ITER ITER return false; if (Emit1(cx, bce, JSOP_DUP) < 0) // EXCEPTION ITER ITER ITER @@ -5571,7 +5626,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter) if (EmitCall(cx, bce, JSOP_CALL, 1, iter) < 0) // ITER RESULT return false; CheckTypeSet(cx, bce, JSOP_CALL); - MOZ_ASSERT(bce->stackDepth == depth + 1); + MOZ_ASSERT(bce->stackDepth == depth); ptrdiff_t checkResult = -1; if (EmitBackPatchOp(cx, bce, &checkResult) < 0) // goto checkResult return false; @@ -5582,7 +5637,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter) // This is a peace offering to ReconstructPCStack. See the note in EmitTry. if (Emit1(cx, bce, JSOP_NOP) < 0) return false; - if (!bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd)) + if (!bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart + JSOP_TRY_LENGTH, tryEnd)) return false; // After the try/catch block: send the received value to the iterator. @@ -5608,7 +5663,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter) if (EmitCall(cx, bce, JSOP_CALL, 1, iter) < 0) // ITER RESULT return false; CheckTypeSet(cx, bce, JSOP_CALL); - MOZ_ASSERT(bce->stackDepth == depth + 1); + MOZ_ASSERT(bce->stackDepth == depth); if (!BackPatch(cx, bce, checkResult, bce->code().end(), JSOP_GOTO)) // checkResult: return false; @@ -5629,7 +5684,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter) if (!EmitAtomOp(cx, cx->names().value, JSOP_GETPROP, bce)) // VALUE return false; - MOZ_ASSERT(bce->stackDepth == depth); + MOZ_ASSERT(bce->stackDepth == depth - 1); return true; } @@ -5807,6 +5862,80 @@ EmitDelete(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) static bool EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, uint32_t count); +static bool +EmitSelfHostedCallFunction(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) +{ + // Special-casing of callFunction to emit bytecode that directly + // invokes the callee with the correct |this| object and arguments. + // callFunction(fun, thisArg, arg0, arg1) thus becomes: + // - emit lookup for fun + // - emit lookup for thisArg + // - emit lookups for arg0, arg1 + // + // argc is set to the amount of actually emitted args and the + // emitting of args below is disabled by setting emitArgs to false. + if (pn->pn_count < 3) { + bce->reportError(pn, JSMSG_MORE_ARGS_NEEDED, "callFunction", "1", "s"); + return false; + } + + ParseNode *pn2 = pn->pn_head; + ParseNode *funNode = pn2->pn_next; + if (!EmitTree(cx, bce, funNode)) + return false; + + ParseNode *thisArg = funNode->pn_next; + if (!EmitTree(cx, bce, thisArg)) + return false; + + bool oldEmittingForInit = bce->emittingForInit; + bce->emittingForInit = false; + + for (ParseNode *argpn = thisArg->pn_next; argpn; argpn = argpn->pn_next) { + if (!EmitTree(cx, bce, argpn)) + return false; + } + + bce->emittingForInit = oldEmittingForInit; + + uint32_t argc = pn->pn_count - 3; + if (EmitCall(cx, bce, pn->getOp(), argc) < 0) + return false; + + CheckTypeSet(cx, bce, pn->getOp()); + return true; +} + +static bool +EmitSelfHostedResumeGenerator(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) +{ + // Syntax: resumeGenerator(gen, value, 'next'|'throw'|'close') + if (pn->pn_count != 4) { + bce->reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s"); + return false; + } + + ParseNode *funNode = pn->pn_head; // The resumeGenerator node. + + ParseNode *genNode = funNode->pn_next; + if (!EmitTree(cx, bce, genNode)) + return false; + + ParseNode *valNode = genNode->pn_next; + if (!EmitTree(cx, bce, valNode)) + return false; + + ParseNode *kindNode = valNode->pn_next; + MOZ_ASSERT(kindNode->isKind(PNK_STRING)); + uint16_t operand = GeneratorObject::getResumeKind(cx, kindNode->pn_atom); + MOZ_ASSERT(!kindNode->pn_next); + + if (EmitCall(cx, bce, JSOP_RESUME, operand) < 0) + return false; + + return true; +} + static bool EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) { @@ -5835,46 +5964,21 @@ EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) return false; } - bool emitArgs = true; ParseNode *pn2 = pn->pn_head; bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE; switch (pn2->getKind()) { case PNK_NAME: - if (bce->emitterMode == BytecodeEmitter::SelfHosting && - pn2->name() == cx->names().callFunction && - !spread) - { - /* - * Special-casing of callFunction to emit bytecode that directly - * invokes the callee with the correct |this| object and arguments. - * callFunction(fun, thisArg, arg0, arg1) thus becomes: - * - emit lookup for fun - * - emit lookup for thisArg - * - emit lookups for arg0, arg1 - * - * argc is set to the amount of actually emitted args and the - * emitting of args below is disabled by setting emitArgs to false. - */ - if (pn->pn_count < 3) { - bce->reportError(pn, JSMSG_MORE_ARGS_NEEDED, "callFunction", "1", "s"); - return false; - } - ParseNode *funNode = pn2->pn_next; - if (!EmitTree(cx, bce, funNode)) - return false; - ParseNode *thisArg = funNode->pn_next; - if (!EmitTree(cx, bce, thisArg)) - return false; - bool oldEmittingForInit = bce->emittingForInit; - bce->emittingForInit = false; - for (ParseNode *argpn = thisArg->pn_next; argpn; argpn = argpn->pn_next) { - if (!EmitTree(cx, bce, argpn)) - return false; - } - bce->emittingForInit = oldEmittingForInit; - argc -= 2; - emitArgs = false; - break; + if (bce->emitterMode == BytecodeEmitter::SelfHosting && !spread) { + // We shouldn't see foo(bar) = x in self-hosted code. + MOZ_ASSERT(!(pn->pn_xflags & PNX_SETCALL)); + + // Calls to "callFunction" or "resumeGenerator" in self-hosted code + // generate inline bytecode. + if (pn2->name() == cx->names().callFunction) + return EmitSelfHostedCallFunction(cx, bce, pn); + if (pn2->name() == cx->names().resumeGenerator) + return EmitSelfHostedResumeGenerator(cx, bce, pn); + // Fall through. } if (!EmitNameOp(cx, bce, pn2, callop)) return false; @@ -5922,25 +6026,23 @@ EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) return false; } - if (emitArgs) { - /* - * Emit code for each argument in order, then emit the JSOP_*CALL or - * JSOP_NEW bytecode with a two-byte immediate telling how many args - * were pushed on the operand stack. - */ - bool oldEmittingForInit = bce->emittingForInit; - bce->emittingForInit = false; - if (!spread) { - for (ParseNode *pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) { - if (!EmitTree(cx, bce, pn3)) - return false; - } - } else { - if (!EmitArray(cx, bce, pn2->pn_next, argc)) + /* + * Emit code for each argument in order, then emit the JSOP_*CALL or + * JSOP_NEW bytecode with a two-byte immediate telling how many args + * were pushed on the operand stack. + */ + bool oldEmittingForInit = bce->emittingForInit; + bce->emittingForInit = false; + if (!spread) { + for (ParseNode *pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) { + if (!EmitTree(cx, bce, pn3)) return false; } - bce->emittingForInit = oldEmittingForInit; + } else { + if (!EmitArray(cx, bce, pn2->pn_next, argc)) + return false; } + bce->emittingForInit = oldEmittingForInit; if (!spread) { if (EmitCall(cx, bce, pn->getOp(), argc, pn) < 0) @@ -6687,28 +6789,16 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) break; case PNK_YIELD_STAR: - ok = EmitYieldStar(cx, bce, pn->pn_kid); + ok = EmitYieldStar(cx, bce, pn->pn_left, pn->pn_right); + break; + + case PNK_GENERATOR: + if (Emit1(cx, bce, JSOP_GENERATOR) < 0) + return false; break; case PNK_YIELD: - MOZ_ASSERT(bce->sc->isFunctionBox()); - if (bce->sc->asFunctionBox()->isStarGenerator()) { - if (!EmitPrepareIteratorResult(cx, bce)) - return false; - } - if (pn->pn_kid) { - if (!EmitTree(cx, bce, pn->pn_kid)) - return false; - } else { - if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) - return false; - } - if (bce->sc->asFunctionBox()->isStarGenerator()) { - if (!EmitFinishIteratorResult(cx, bce, false)) - return false; - } - if (Emit1(cx, bce, JSOP_YIELD) < 0) - return false; + ok = EmitYield(cx, bce, pn); break; case PNK_STATEMENTLIST: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index a7c16bf246b3..a86dcff80a98 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -323,6 +323,17 @@ class FullParseHandler return true; } + ParseNode *newYieldExpression(uint32_t begin, ParseNode *value, ParseNode *gen, + JSOp op = JSOP_YIELD) { + TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); + return new_(PNK_YIELD, op, pos, value, gen); + } + + ParseNode *newYieldStarExpression(uint32_t begin, ParseNode *value, ParseNode *gen) { + TokenPos pos(begin, value->pn_pos.end); + return new_(PNK_YIELD_STAR, JSOP_NOP, pos, value, gen); + } + // Statements ParseNode *newStatementList(unsigned blockid, const TokenPos &pos) { @@ -352,6 +363,31 @@ class FullParseHandler list->append(stmt); } + bool prependInitialYield(ParseNode *stmtList, ParseNode *genName) { + MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST)); + + TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1); + ParseNode *makeGen = new_(PNK_GENERATOR, yieldPos); + if (!makeGen) + return false; + + MOZ_ASSERT(genName->getOp() == JSOP_NAME); + genName->setOp(JSOP_SETNAME); + genName->markAsAssigned(); + ParseNode *genInit = newBinary(PNK_ASSIGN, genName, makeGen); + + ParseNode *initialYield = newYieldExpression(yieldPos.begin, nullptr, genInit, + JSOP_INITIALYIELD); + if (!initialYield) + return false; + + initialYield->pn_next = stmtList->pn_head; + stmtList->pn_head = initialYield; + stmtList->pn_count++; + + return true; + } + ParseNode *newEmptyStatement(const TokenPos &pos) { return new_(PNK_SEMI, JSOP_NOP, pos, (ParseNode *) nullptr); } diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index c3bc7c147eb8..dfefe5a4248a 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -124,6 +124,7 @@ class UpvarCookie F(FINALLY) \ F(THROW) \ F(DEBUGGER) \ + F(GENERATOR) \ F(YIELD) \ F(YIELD_STAR) \ F(GENEXP) \ @@ -422,6 +423,9 @@ enum ParseNodeKind * * PNK_LEXICALSCOPE name pn_objbox: block object in ObjectBox holder * pn_expr: block body + * PNK_GENERATOR nullary + * PNK_YIELD, binary pn_left: expr or null; pn_right: generator object + * PNK_YIELD_STAR * PNK_ARRAYCOMP list pn_count: 1 * pn_head: list of 1 element, which is block * enclosing for loop(s) and optionally @@ -773,8 +777,9 @@ class ParseNode MOZ_ASSERT(isKind(PNK_GENEXP)); ParseNode *callee = this->pn_head; ParseNode *body = callee->pn_body; - MOZ_ASSERT(body->isKind(PNK_LEXICALSCOPE) || body->isKind(PNK_FOR)); - return body; + MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST)); + MOZ_ASSERT(body->last()->isKind(PNK_LEXICALSCOPE) || body->last()->isKind(PNK_FOR)); + return body->last(); } #endif diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 0bdc72b83eb0..c77ed18c8a80 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1035,6 +1035,23 @@ Parser::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ break; } + if (pc->isGenerator()) { + MOZ_ASSERT(type == StatementListBody); + Node generator = newName(context->names().dotGenerator); + if (!generator) + return null(); + if (!pc->define(tokenStream, context->names().dotGenerator, generator, Definition::VAR)) + return null(); + + generator = newName(context->names().dotGenerator); + if (!generator) + return null(); + if (!noteNameUse(context->names().dotGenerator, generator)) + return null(); + if (!handler.prependInitialYield(pn, generator)) + return null(); + } + /* Define the 'arguments' binding if necessary. */ if (!checkFunctionArguments()) return null(); @@ -5007,6 +5024,21 @@ Parser::returnStatement() return pn; } +template +typename ParseHandler::Node +Parser::newYieldExpression(uint32_t begin, typename ParseHandler::Node expr, + bool isYieldStar) +{ + Node generator = newName(context->names().dotGenerator); + if (!generator) + return null(); + if (!noteNameUse(context->names().dotGenerator, generator)) + return null(); + if (isYieldStar) + return handler.newYieldStarExpression(begin, expr, generator); + return handler.newYieldExpression(begin, expr, generator); +} + template typename ParseHandler::Node Parser::yieldExpression() @@ -5052,7 +5084,7 @@ Parser::yieldExpression() if (!exprNode) return null(); } - return handler.newUnary(kind, JSOP_NOP, begin, exprNode); + return newYieldExpression(begin, exprNode, kind == PNK_YIELD_STAR); } case NotGenerator: @@ -5110,7 +5142,7 @@ Parser::yieldExpression() return null(); } - return handler.newUnary(PNK_YIELD, JSOP_NOP, begin, exprNode); + return newYieldExpression(begin, exprNode); } } @@ -6189,9 +6221,17 @@ LegacyCompExprTransplanter::transplant(ParseNode *pn) MOZ_ASSERT(!stmt || stmt != pc->topStmt); #endif if (isGenexp && !dn->isOp(JSOP_CALLEE)) { - MOZ_ASSERT(!pc->decls().lookupFirst(atom)); + MOZ_ASSERT_IF(atom != parser->context->names().dotGenerator, + !pc->decls().lookupFirst(atom)); - if (dn->pn_pos < root->pn_pos) { + if (atom == parser->context->names().dotGenerator) { + if (dn->dn_uses == pn) { + if (!BumpStaticLevel(parser->tokenStream, dn, pc)) + return false; + if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc)) + return false; + } + } else if (dn->pn_pos < root->pn_pos) { /* * The variable originally appeared to be a use of a * definition or placeholder outside the generator, but now @@ -6304,7 +6344,7 @@ LegacyComprehensionHeadBlockScopeDepth(ParseContext *pc) */ template <> ParseNode * -Parser::legacyComprehensionTail(ParseNode *bodyStmt, unsigned blockid, +Parser::legacyComprehensionTail(ParseNode *bodyExpr, unsigned blockid, GeneratorKind comprehensionKind, ParseContext *outerpc, unsigned innerBlockScopeDepth) @@ -6368,15 +6408,15 @@ Parser::legacyComprehensionTail(ParseNode *bodyStmt, unsigned adjust = blockid - adjust; } - handler.setBeginPosition(pn, bodyStmt); + handler.setBeginPosition(pn, bodyExpr); pnp = &pn->pn_expr; - LegacyCompExprTransplanter transplanter(bodyStmt, this, outerpc, comprehensionKind, adjust); + LegacyCompExprTransplanter transplanter(bodyExpr, this, outerpc, comprehensionKind, adjust); if (!transplanter.init()) return null(); - if (!transplanter.transplant(bodyStmt)) + if (!transplanter.transplant(bodyExpr)) return null(); MOZ_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object); @@ -6532,6 +6572,23 @@ Parser::legacyComprehensionTail(ParseNode *bodyStmt, unsigned pnp = &pn2->pn_kid2; } + ParseNode *bodyStmt; + if (isGenexp) { + ParseNode *yieldExpr = newYieldExpression(bodyExpr->pn_pos.begin, bodyExpr); + if (!yieldExpr) + return null(); + yieldExpr->setInParens(true); + + bodyStmt = handler.newExprStatement(yieldExpr, bodyExpr->pn_pos.end); + if (!bodyStmt) + return null(); + } else { + bodyStmt = handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, + bodyExpr->pn_pos.begin, bodyExpr); + if (!bodyStmt) + return null(); + } + *pnp = bodyStmt; pc->topStmt->innerBlockScopeDepth += innerBlockScopeDepth; @@ -6568,12 +6625,7 @@ Parser::legacyArrayComprehension(ParseNode *array) array->pn_tail = &array->pn_head; *array->pn_tail = nullptr; - ParseNode *arrayPush = handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, - bodyExpr->pn_pos.begin, bodyExpr); - if (!arrayPush) - return null(); - - ParseNode *comp = legacyComprehensionTail(arrayPush, array->pn_blockid, NotGenerator, + ParseNode *comp = legacyComprehensionTail(bodyExpr, array->pn_blockid, NotGenerator, nullptr, LegacyComprehensionHeadBlockScopeDepth(pc)); if (!comp) return null(); @@ -6596,10 +6648,10 @@ Parser::legacyArrayComprehension(Node array) template typename ParseHandler::Node Parser::generatorComprehensionLambda(GeneratorKind comprehensionKind, - unsigned begin, Node innerStmt) + unsigned begin, Node innerExpr) { MOZ_ASSERT(comprehensionKind == LegacyGenerator || comprehensionKind == StarGenerator); - MOZ_ASSERT(!!innerStmt == (comprehensionKind == LegacyGenerator)); + MOZ_ASSERT(!!innerExpr == (comprehensionKind == LegacyGenerator)); Node genfn = handler.newFunctionDefinition(); if (!genfn) @@ -6650,29 +6702,47 @@ Parser::generatorComprehensionLambda(GeneratorKind comprehensionKi genFunbox->inGenexpLambda = true; handler.setBlockId(genfn, genpc.bodyid); - Node body; + Node generator = newName(context->names().dotGenerator); + if (!generator) + return null(); + if (!pc->define(tokenStream, context->names().dotGenerator, generator, Definition::VAR)) + return null(); + Node body = handler.newStatementList(pc->blockid(), TokenPos(begin, pos().end)); + if (!body) + return null(); + + Node comp; if (comprehensionKind == StarGenerator) { - body = comprehension(StarGenerator); - if (!body) + comp = comprehension(StarGenerator); + if (!comp) return null(); } else { MOZ_ASSERT(comprehensionKind == LegacyGenerator); - body = legacyComprehensionTail(innerStmt, outerpc->blockid(), LegacyGenerator, + comp = legacyComprehensionTail(innerExpr, outerpc->blockid(), LegacyGenerator, outerpc, LegacyComprehensionHeadBlockScopeDepth(outerpc)); - if (!body) + if (!comp) return null(); } if (comprehensionKind == StarGenerator) MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); - handler.setBeginPosition(body, begin); + handler.setBeginPosition(comp, begin); + handler.setEndPosition(comp, pos().end); + handler.addStatementToList(body, comp, pc); handler.setEndPosition(body, pos().end); - handler.setBeginPosition(genfn, begin); handler.setEndPosition(genfn, pos().end); + generator = newName(context->names().dotGenerator); + if (!generator) + return null(); + if (!noteNameUse(context->names().dotGenerator, generator)) + return null(); + if (!handler.prependInitialYield(body, generator)) + return null(); + // Note that if we ever start syntax-parsing generators, we will also // need to propagate the closed-over variable set to the inner // lazyscript, as in finishFunctionDefinition. @@ -6709,19 +6779,8 @@ Parser::legacyGeneratorExpr(ParseNode *expr) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); - /* Create a |yield| node for |kid|. */ - ParseNode *yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, expr->pn_pos.begin, expr); - if (!yieldExpr) - return null(); - yieldExpr->setInParens(true); - - // A statement to wrap the yield expression. - ParseNode *yieldStmt = handler.newExprStatement(yieldExpr, expr->pn_pos.end); - if (!yieldStmt) - return null(); - /* Make a new node for the desugared generator function. */ - ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, yieldStmt); + ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, expr); if (!genfn) return null(); @@ -6871,7 +6930,7 @@ Parser::comprehensionTail(GeneratorKind comprehensionKind) return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr); MOZ_ASSERT(comprehensionKind == StarGenerator); - Node yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, begin, bodyExpr); + Node yieldExpr = newYieldExpression(begin, bodyExpr); if (!yieldExpr) return null(); handler.setInParens(yieldExpr); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index d4e6ad9c1afb..b1826c7f7aa6 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -464,6 +464,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool addExprAndGetNextTemplStrToken(Node nodeList, TokenKind &tt); inline Node newName(PropertyName *name); + inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false); inline bool abortIfSyntaxParser(); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 7073a71bc07a..7154ff56d282 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -135,11 +135,14 @@ class SyntaxParseHandler bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; } bool addPropertyDefinition(Node literal, Node name, Node expr, bool isShorthand = false) { return true; } bool addMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } + Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } + Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } // Statements Node newStatementList(unsigned blockid, const TokenPos &pos) { return NodeGeneric; } void addStatementToList(Node list, Node stmt, ParseContext *pc) {} + bool prependInitialYield(Node stmtList, Node gen) { return true; } Node newEmptyStatement(const TokenPos &pos) { return NodeGeneric; } Node newExprStatement(Node expr, uint32_t end) { diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index cb5cfdc0b78f..972674c0689c 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -25,7 +25,7 @@ * - writes to object properties * - writes to array slots * - writes to fields like JSObject::shape_ that we trace through - * - writes to fields in private data, like JSGenerator::obj + * - writes to fields in private data * - writes to non-markable fields like JSObject::private that point to * markable data * The last category is the trickiest. Even though the private pointers does not diff --git a/js/src/jit-test/tests/debug/Frame-onPop-generators-01.js b/js/src/jit-test/tests/debug/Frame-onPop-generators-01.js index 3b8b8810b8b4..35b4d29621ca 100644 --- a/js/src/jit-test/tests/debug/Frame-onPop-generators-01.js +++ b/js/src/jit-test/tests/debug/Frame-onPop-generators-01.js @@ -1,5 +1,6 @@ +// |jit-test| error: StopIteration // Returning {throw:} from an onPop handler when yielding works and -// does not close the generator-iterator. +// does closes the generator-iterator. load(libdir + "asserts.js"); @@ -17,4 +18,4 @@ var rv = gw.evalInGlobal("it.next();"); assertEq(rv.throw, "fit"); dbg.enabled = false; -assertEq(g.it.next(), 1); +g.it.next(); diff --git a/js/src/jit-test/tests/debug/Frame-onPop-star-generators-01.js b/js/src/jit-test/tests/debug/Frame-onPop-star-generators-01.js index c8edd53744d3..b26f0ef7394e 100644 --- a/js/src/jit-test/tests/debug/Frame-onPop-star-generators-01.js +++ b/js/src/jit-test/tests/debug/Frame-onPop-star-generators-01.js @@ -1,5 +1,5 @@ // Returning {throw:} from an onPop handler when yielding works and -// does not close the generator-iterator. +// closes the generator-iterator. load(libdir + "iteration.js"); @@ -17,4 +17,4 @@ var rv = gw.evalInGlobal("it.next();"); assertEq(rv.throw, "fit"); dbg.enabled = false; -assertIteratorNext(g.it, 1); +assertIteratorDone(g.it); diff --git a/js/src/jit-test/tests/debug/resumption-04.js b/js/src/jit-test/tests/debug/resumption-04.js index d564a7a75ace..1309871eab81 100644 --- a/js/src/jit-test/tests/debug/resumption-04.js +++ b/js/src/jit-test/tests/debug/resumption-04.js @@ -1,3 +1,4 @@ +// |jit-test| error: already executing generator // Forced return from a generator frame. var g = newGlobal(); @@ -10,5 +11,9 @@ function gen() { debugger; // Force return here. The value is ignored. yield '2'; } -var x = [v for (v in gen())]; -assertEq(x.join(","), "1"); + +var iter = gen(); +assertEq(iter.next(), "1"); +assertEq(iter.next(), "!"); +iter.next(); +assertEq(0, 1); diff --git a/js/src/jit-test/tests/debug/resumption-06.js b/js/src/jit-test/tests/debug/resumption-06.js index 43010c28d832..4b58e3e464b9 100644 --- a/js/src/jit-test/tests/debug/resumption-06.js +++ b/js/src/jit-test/tests/debug/resumption-06.js @@ -1,3 +1,4 @@ +// |jit-test| error: already executing generator // Forced return from a star generator frame. load(libdir + 'asserts.js') @@ -16,4 +17,5 @@ function* gen() { var iter = gen(); assertIteratorNext(iter, '1'); assertEq(iter.next(), '!'); -assertIteratorDone(iter); +iter.next(); +assertEq(0, 1); diff --git a/js/src/jit-test/tests/generators/throw-closes.js b/js/src/jit-test/tests/generators/throw-closes.js new file mode 100644 index 000000000000..ffadbb4f1ea1 --- /dev/null +++ b/js/src/jit-test/tests/generators/throw-closes.js @@ -0,0 +1,49 @@ +// When a generator function throws, the generator is closed. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +// Star generator, next() throws. +function *g() { + yield 1; + yield 2; + throw 3; + yield 4; +} +var i = g(); +assertIteratorNext(i, 1); +assertIteratorNext(i, 2); +assertThrowsValue(() => i.next(), 3); +assertIteratorDone(i); +assertIteratorDone(i); + +// Star generator, throw() throws. +function *h() { + yield 1; + yield 2; +} +var i = h(); +assertIteratorNext(i, 1); +assertThrowsValue(() => i.throw(4), 4); +assertIteratorDone(i); + +// Legacy generator, throw() throws. +function l1() { + yield 1; + yield 2; +} +var i = l1(); +assertEq(i.next(), 1); +assertThrowsValue(() => i.throw(5), 5); +assertThrowsInstanceOf(() => i.next(), StopIteration); + +// Legacy generator, next() throws. +function l2() { + yield 1; + throw 6; + yield 2; +} +var i = l2(); +assertEq(i.next(), 1); +assertThrowsValue(() => i.next(), 6); +assertThrowsInstanceOf(() => i.next(), StopIteration); diff --git a/js/src/jit-test/tests/generators/wrappers.js b/js/src/jit-test/tests/generators/wrappers.js new file mode 100644 index 000000000000..1e7c03a7d0b9 --- /dev/null +++ b/js/src/jit-test/tests/generators/wrappers.js @@ -0,0 +1,34 @@ +// Generator methods work transparently on CrossCompartmentWrappers. + +load(libdir + "asserts.js"); +load(libdir + "iteration.js"); + +function gen() { yield 1; yield 2; } +var it = gen(); + +var g = newGlobal(); +g.eval("function gen2() { yield 3; yield 4; }; var it2 = gen2();"); + +// LegacyGenerator.next +assertEq(it.next.call(g.it2), 3); + +// LegacyGenerator.throw +assertThrowsValue(() => it.throw.call(g.it2, 7), 7); + +function *gen3() { yield 1; yield 2; } +it = gen3(); +g.eval("function *gen4() { yield 5; yield 6; }; var it4 = gen4();"); + +// StarGenerator.next +assertIteratorResult(it.next.call(g.it4), 5, false) + +// StarGenerator.throw +assertThrowsValue(() => it.throw.call(g.it4, 8), 8); + +// Other objects should throw. +try { + it.next.call([]); + assertEq(0, 1); +} catch (e) { + assertEq(e.toString().contains("called on incompatible Array"), true); +} diff --git a/js/src/jit-test/tests/saved-stacks/generators.js b/js/src/jit-test/tests/saved-stacks/generators.js index 69cd1e4adf38..7d75c5685ccc 100644 --- a/js/src/jit-test/tests/saved-stacks/generators.js +++ b/js/src/jit-test/tests/saved-stacks/generators.js @@ -10,6 +10,7 @@ const { value: frame } = (function iife1() { assertEq(frame.functionDisplayName, "iife2"); assertEq(frame.parent.functionDisplayName, "generator"); -assertEq(frame.parent.parent.functionDisplayName, "iife1"); -assertEq(frame.parent.parent.parent.functionDisplayName, null); -assertEq(frame.parent.parent.parent.parent, null); +assertEq(frame.parent.parent.functionDisplayName, "next"); +assertEq(frame.parent.parent.parent.functionDisplayName, "iife1"); +assertEq(frame.parent.parent.parent.parent.functionDisplayName, null); +assertEq(frame.parent.parent.parent.parent.parent, null); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index a34ca4f83721..3a2f32b13ec0 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -323,16 +323,13 @@ jit::CanEnterBaselineMethod(JSContext *cx, RunState &state) if (!state.maybeCreateThisForConstructor(cx)) return Method_Skipped; - } else if (state.isExecute()) { + } else { + MOZ_ASSERT(state.isExecute()); ExecuteType type = state.asExecute()->type(); if (type == EXECUTE_DEBUG || type == EXECUTE_DEBUG_GLOBAL) { JitSpew(JitSpew_BaselineAbort, "debugger frame"); return Method_CantCompile; } - } else { - MOZ_ASSERT(state.isGenerator()); - JitSpew(JitSpew_BaselineAbort, "generator frame"); - return Method_CantCompile; } RootedScript script(cx, state.script()); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 13b005764d21..acac206820f5 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2401,10 +2401,6 @@ jit::CanEnter(JSContext *cx, RunState &state) if (!state.maybeCreateThisForConstructor(cx)) return Method_Skipped; - } else if (state.isGenerator()) { - JitSpew(JitSpew_IonAbort, "generator frame"); - ForbidCompilation(cx, script); - return Method_CantCompile; } // If --ion-eager is used, compile with Baseline first, so that we diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index a744bc3126ff..62a0c66bb7ce 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -58,7 +58,6 @@ const char js_break_str[] = "break"; const char js_case_str[] = "case"; const char js_catch_str[] = "catch"; const char js_class_str[] = "class"; -const char js_close_str[] = "close"; const char js_const_str[] = "const"; const char js_continue_str[] = "continue"; const char js_debugger_str[] = "debugger"; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 117eee762bef..a945b84dd022 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1109,11 +1109,10 @@ JSContext::JSContext(JSRuntime *rt) data(nullptr), data2(nullptr), outstandingRequests(0), - jitIsBroken(false), + jitIsBroken(false) #ifdef MOZ_TRACE_JSCALLS - functionCallback(nullptr), + , functionCallback(nullptr) #endif - innermostGenerator_(nullptr) { MOZ_ASSERT(static_cast(this) == ContextFriendFields::get(this)); @@ -1146,23 +1145,6 @@ JSContext::isThrowingOutOfMemory() return throwing && unwrappedException_ == StringValue(names().outOfMemory); } -void -JSContext::enterGenerator(JSGenerator *gen) -{ - MOZ_ASSERT(!gen->prevGenerator); - gen->prevGenerator = innermostGenerator_; - innermostGenerator_ = gen; -} - -void -JSContext::leaveGenerator(JSGenerator *gen) -{ - MOZ_ASSERT(innermostGenerator_ == gen); - innermostGenerator_ = innermostGenerator_->prevGenerator; - gen->prevGenerator = nullptr; -} - - bool JSContext::saveFrameChain() { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 55fc9056b927..91e1dd45f4b8 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -552,14 +552,7 @@ struct JSContext : public js::ExclusiveContext, runtime_->gc.gcIfNeeded(this); } - private: - /* Innermost-executing generator or null if no generator are executing. */ - JSGenerator *innermostGenerator_; public: - JSGenerator *innermostGenerator() const { return innermostGenerator_; } - void enterGenerator(JSGenerator *gen); - void leaveGenerator(JSGenerator *gen); - bool isExceptionPending() { return throwing; } diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index a03038b65d15..f64dc2f5a872 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2264,6 +2264,8 @@ types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc) * Sub2 lets us continue to distinguish the two subclasses and any extra * properties added to those prototype objects. */ + if (script->isGenerator()) + return false; if (JSOp(*pc) != JSOP_NEW) return false; pc += JSOP_NEW_LENGTH; diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 75dfe35ab326..38b145918c77 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1009,9 +1009,6 @@ static const JSFunctionSpec string_iterator_methods[] = { JS_FS_END }; -static bool -CloseLegacyGenerator(JSContext *cx, HandleObject genobj); - bool js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp) { @@ -1058,7 +1055,14 @@ js::CloseIterator(JSContext *cx, HandleObject obj) ni->props_cursor = ni->props_array; } } else if (obj->is()) { - return CloseLegacyGenerator(cx, obj); + Rooted genObj(cx, &obj->as()); + if (genObj->isClosed()) + return true; + if (genObj->isRunning() || genObj->isClosing()) { + // Nothing sensible to do. + return true; + } + return LegacyGeneratorObject::close(cx, obj); } return true; } @@ -1511,506 +1515,6 @@ ForOfIterator::materializeArrayIterator() return true; } -/*** Generators **********************************************************************************/ - -template -static void -FinalizeGenerator(FreeOp *fop, JSObject *obj) -{ - MOZ_ASSERT(obj->is()); - JSGenerator *gen = obj->as().getGenerator(); - MOZ_ASSERT(gen); - // gen is open when a script has not called its close method while - // explicitly manipulating it. - MOZ_ASSERT(gen->state == JSGEN_NEWBORN || - gen->state == JSGEN_CLOSED || - gen->state == JSGEN_OPEN); - // If gen->state is JSGEN_CLOSED, gen->fp may be nullptr. - if (gen->fp) - JS_POISON(gen->fp, JS_SWEPT_FRAME_PATTERN, sizeof(InterpreterFrame)); - JS_POISON(gen, JS_SWEPT_FRAME_PATTERN, sizeof(JSGenerator)); - fop->free_(gen); -} - -static void -MarkGeneratorFrame(JSTracer *trc, JSGenerator *gen) -{ - gen->obj = MaybeForwarded(gen->obj.get()); - MarkObject(trc, &gen->obj, "Generator Object"); - MarkValueRange(trc, - HeapValueify(gen->fp->generatorArgsSnapshotBegin()), - HeapValueify(gen->fp->generatorArgsSnapshotEnd()), - "Generator Floating Args"); - gen->fp->mark(trc); - MarkValueRange(trc, - HeapValueify(gen->fp->generatorSlotsSnapshotBegin()), - HeapValueify(gen->regs.sp), - "Generator Floating Stack"); -} - -static void -GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen) -{ - JS::Zone *zone = cx->zone(); - if (zone->needsIncrementalBarrier()) - MarkGeneratorFrame(zone->barrierTracer(), gen); -} - -static void -GeneratorWriteBarrierPost(JSContext *cx, JSGenerator *gen) -{ -#ifdef JSGC_GENERATIONAL - cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(gen->obj); -#endif -} - -/* - * Only mark generator frames/slots when the generator is not active on the - * stack or closed. Barriers when copying onto the stack or closing preserve - * gc invariants. - */ -static bool -GeneratorHasMarkableFrame(JSGenerator *gen) -{ - return gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN; -} - -/* - * When a generator is closed, the GC things reachable from the contained frame - * and slots become unreachable and thus require a write barrier. - */ -static void -SetGeneratorClosed(JSContext *cx, JSGenerator *gen) -{ - MOZ_ASSERT(gen->state != JSGEN_CLOSED); - if (GeneratorHasMarkableFrame(gen)) - GeneratorWriteBarrierPre(cx, gen); - gen->state = JSGEN_CLOSED; - -#ifdef DEBUG - MakeRangeGCSafe(gen->fp->generatorArgsSnapshotBegin(), - gen->fp->generatorArgsSnapshotEnd()); - MakeRangeGCSafe(gen->fp->generatorSlotsSnapshotBegin(), - gen->regs.sp); - PodZero(&gen->regs, 1); - gen->fp = nullptr; -#endif -} - -template -static void -TraceGenerator(JSTracer *trc, JSObject *obj) -{ - MOZ_ASSERT(obj->is()); - JSGenerator *gen = obj->as().getGenerator(); - MOZ_ASSERT(gen); - if (GeneratorHasMarkableFrame(gen)) - MarkGeneratorFrame(trc, gen); -} - -GeneratorState::GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState) - : RunState(cx, Generator, gen->fp->script()), - cx_(cx), - gen_(gen), - futureState_(futureState), - entered_(false) -{ } - -GeneratorState::~GeneratorState() -{ - gen_->fp->setSuspended(); - - if (entered_) - cx_->leaveGenerator(gen_); -} - -InterpreterFrame * -GeneratorState::pushInterpreterFrame(JSContext *cx) -{ - /* - * Write barrier is needed since the generator stack can be updated, - * and it's not barriered in any other way. We need to do it before - * gen->state changes, which can cause us to trace the generator - * differently. - * - * We could optimize this by setting a bit on the generator to signify - * that it has been marked. If this bit has already been set, there is no - * need to mark again. The bit would have to be reset before the next GC, - * or else some kind of epoch scheme would have to be used. - */ - GeneratorWriteBarrierPre(cx, gen_); - gen_->state = futureState_; - - gen_->fp->clearSuspended(); - - cx->enterGenerator(gen_); /* OOM check above. */ - entered_ = true; - return gen_->fp; -} - -const Class LegacyGeneratorObject::class_ = { - "Generator", - JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, - JS_PropertyStub, /* addProperty */ - JS_DeletePropertyStub, /* delProperty */ - JS_PropertyStub, /* getProperty */ - JS_StrictPropertyStub, /* setProperty */ - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub, - FinalizeGenerator, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - TraceGenerator, - JS_NULL_CLASS_SPEC, - { - nullptr, /* outerObject */ - nullptr, /* innerObject */ - iterator_iteratorObject, - } -}; - -const Class StarGeneratorObject::class_ = { - "Generator", - JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, - JS_PropertyStub, /* addProperty */ - JS_DeletePropertyStub, /* delProperty */ - JS_PropertyStub, /* getProperty */ - JS_StrictPropertyStub, /* setProperty */ - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub, - FinalizeGenerator, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - TraceGenerator, -}; - -/* - * Called from the JSOP_GENERATOR case in the interpreter, with fp referring - * to the frame by which the generator function was activated. Create a new - * JSGenerator object, which contains its own InterpreterFrame that we populate - * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return - * from the activation in fp, so we can steal away fp->callobj and fp->argsobj - * if they are non-null. - */ -JSObject * -js_NewGenerator(JSContext *cx, const InterpreterRegs &stackRegs) -{ - MOZ_ASSERT(stackRegs.stackDepth() == 0); - InterpreterFrame *stackfp = stackRegs.fp(); - - MOZ_ASSERT(stackfp->script()->isGenerator()); - - Rooted global(cx, &stackfp->global()); - RootedNativeObject obj(cx); - if (stackfp->script()->isStarGenerator()) { - RootedValue pval(cx); - RootedObject fun(cx, stackfp->fun()); - // FIXME: This would be faster if we could avoid doing a lookup to get - // the prototype for the instance. Bug 906600. - if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval)) - return nullptr; - JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr; - if (!proto) { - proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global); - if (!proto) - return nullptr; - } - obj = NewNativeObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global); - } else { - MOZ_ASSERT(stackfp->script()->isLegacyGenerator()); - JSObject *proto = GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global); - if (!proto) - return nullptr; - obj = NewNativeObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto, global); - } - if (!obj) - return nullptr; - - /* Load and compute stack slot counts. */ - Value *stackvp = stackfp->generatorArgsSnapshotBegin(); - unsigned vplen = stackfp->generatorArgsSnapshotEnd() - stackvp; - - static_assert(sizeof(InterpreterFrame) % sizeof(HeapValue) == 0, - "The Values stored after InterpreterFrame must be aligned."); - unsigned nvals = vplen + VALUES_PER_STACK_FRAME + stackfp->script()->nslots(); - JSGenerator *gen = obj->zone()->pod_calloc_with_extra(nvals); - if (!gen) - return nullptr; - - /* Cut up floatingStack space. */ - HeapValue *genvp = gen->stackSnapshot(); - SetValueRangeToUndefined((Value *)genvp, vplen); - - InterpreterFrame *genfp = reinterpret_cast(genvp + vplen); - - /* Initialize JSGenerator. */ - gen->obj.init(obj); - gen->state = JSGEN_NEWBORN; - gen->fp = genfp; - gen->prevGenerator = nullptr; - - /* Copy from the stack to the generator's floating frame. */ - gen->regs.rebaseFromTo(stackRegs, *genfp); - genfp->copyFrameAndValues(cx, (Value *)genvp, stackfp, - stackvp, stackRegs.sp); - genfp->setSuspended(); - obj->setPrivate(gen); - return obj; -} - -static void -SetGeneratorClosed(JSContext *cx, JSGenerator *gen); - -typedef enum JSGeneratorOp { - JSGENOP_NEXT, - JSGENOP_SEND, - JSGENOP_THROW, - JSGENOP_CLOSE -} JSGeneratorOp; - -/* - * Start newborn or restart yielding generator and perform the requested - * operation inside its frame. - */ -static bool -SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj, - JSGenerator *gen, HandleValue arg, GeneratorKind generatorKind, - MutableHandleValue rval) -{ - MOZ_ASSERT(generatorKind == LegacyGenerator || generatorKind == StarGenerator); - - if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NESTING_GENERATOR); - return false; - } - - JSGeneratorState futureState; - MOZ_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_SEND: - if (gen->state == JSGEN_OPEN) { - /* - * Store the argument to send as the result of the yield - * expression. The generator stack is not barriered, so we need - * write barriers here. - */ - HeapValue::writeBarrierPre(gen->regs.sp[-1]); - gen->regs.sp[-1] = arg; - HeapValue::writeBarrierPost(gen->regs.sp[-1], &gen->regs.sp[-1]); - } - futureState = JSGEN_RUNNING; - break; - - case JSGENOP_THROW: - cx->setPendingException(arg); - futureState = JSGEN_RUNNING; - break; - - default: - MOZ_ASSERT(op == JSGENOP_CLOSE); - MOZ_ASSERT(generatorKind == LegacyGenerator); - cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING)); - futureState = JSGEN_CLOSING; - break; - } - - bool ok; - { - GeneratorState state(cx, gen, futureState); - ok = RunScript(cx, state); - if (!ok && gen->state == JSGEN_CLOSED) - return false; - } - - if (gen->fp->isYielding()) { - /* - * Yield is ordinarily infallible, but ok can be false here if a - * Debugger.Frame.onPop hook fails. - */ - MOZ_ASSERT(gen->state == JSGEN_RUNNING); - MOZ_ASSERT(op != JSGENOP_CLOSE); - gen->fp->clearYielding(); - gen->state = JSGEN_OPEN; - GeneratorWriteBarrierPost(cx, gen); - rval.set(gen->fp->returnValue()); - return ok; - } - - if (ok) { - if (generatorKind == StarGenerator) { - // Star generators return a {value:FOO, done:true} object. - rval.set(gen->fp->returnValue()); - } else { - MOZ_ASSERT(generatorKind == LegacyGenerator); - - // Otherwise we discard the return value and throw a StopIteration - // if needed. - rval.setUndefined(); - if (op != JSGENOP_CLOSE) - ok = ThrowStopIteration(cx); - } - } - - SetGeneratorClosed(cx, gen); - return ok; -} - -MOZ_ALWAYS_INLINE bool -star_generator_next(JSContext *cx, CallArgs args) -{ - RootedObject thisObj(cx, &args.thisv().toObject()); - JSGenerator *gen = thisObj->as().getGenerator(); - - if (gen->state == JSGEN_CLOSED) { - RootedObject obj(cx, CreateItrResultObject(cx, JS::UndefinedHandleValue, true)); - if (!obj) - return false; - args.rval().setObject(*obj); - return true; - } - - return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), StarGenerator, - args.rval()); -} - -MOZ_ALWAYS_INLINE bool -star_generator_throw(JSContext *cx, CallArgs args) -{ - RootedObject thisObj(cx, &args.thisv().toObject()); - - JSGenerator *gen = thisObj->as().getGenerator(); - if (gen->state == JSGEN_CLOSED) { - cx->setPendingException(args.get(0)); - return false; - } - - return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), StarGenerator, - args.rval()); -} - -MOZ_ALWAYS_INLINE bool -legacy_generator_next(JSContext *cx, CallArgs args) -{ - RootedObject thisObj(cx, &args.thisv().toObject()); - - JSGenerator *gen = thisObj->as().getGenerator(); - if (gen->state == JSGEN_CLOSED) - return ThrowStopIteration(cx); - - return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), LegacyGenerator, - args.rval()); -} - -MOZ_ALWAYS_INLINE bool -legacy_generator_throw(JSContext *cx, CallArgs args) -{ - RootedObject thisObj(cx, &args.thisv().toObject()); - - JSGenerator *gen = thisObj->as().getGenerator(); - if (gen->state == JSGEN_CLOSED) { - cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue()); - return false; - } - - return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), LegacyGenerator, - args.rval()); -} - -static bool -CloseLegacyGenerator(JSContext *cx, HandleObject obj, MutableHandleValue rval) -{ - MOZ_ASSERT(obj->is()); - - JSGenerator *gen = obj->as().getGenerator(); - - if (gen->state == JSGEN_CLOSED) { - rval.setUndefined(); - return true; - } - - if (gen->state == JSGEN_NEWBORN) { - SetGeneratorClosed(cx, gen); - rval.setUndefined(); - return true; - } - - return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JS::UndefinedHandleValue, LegacyGenerator, - rval); -} - -static bool -CloseLegacyGenerator(JSContext *cx, HandleObject obj) -{ - RootedValue rval(cx); - return CloseLegacyGenerator(cx, obj, &rval); -} - -MOZ_ALWAYS_INLINE bool -legacy_generator_close(JSContext *cx, CallArgs args) -{ - RootedObject thisObj(cx, &args.thisv().toObject()); - - return CloseLegacyGenerator(cx, thisObj, args.rval()); -} - -template -MOZ_ALWAYS_INLINE bool -IsObjectOfType(HandleValue v) -{ - return v.isObject() && v.toObject().is(); -} - -template -static bool -NativeMethod(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod, Impl>(cx, args); -} - -#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) -#define JS_METHOD(name, T, impl, len, attrs) JS_FN(name, (NativeMethod), len, attrs) - -static const JSFunctionSpec star_generator_methods[] = { - JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0), - JS_METHOD("next", StarGeneratorObject, star_generator_next, 1, 0), - JS_METHOD("throw", StarGeneratorObject, star_generator_throw, 1, 0), - JS_FS_END -}; - -static const JSFunctionSpec legacy_generator_methods[] = { - JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0), - // "send" is an alias for "next". - JS_METHOD("next", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM), - JS_METHOD("send", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM), - JS_METHOD("throw", LegacyGeneratorObject, legacy_generator_throw, 1, JSPROP_ROPERM), - JS_METHOD("close", LegacyGeneratorObject, legacy_generator_close, 0, JSPROP_ROPERM), - JS_FS_END -}; - -static JSObject* -NewSingletonObjectWithObjectPrototype(JSContext *cx, Handle global) -{ - JSObject *proto = global->getOrCreateObjectPrototype(cx); - if (!proto) - return nullptr; - return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject); -} - -static JSObject* -NewSingletonObjectWithFunctionPrototype(JSContext *cx, Handle global) -{ - JSObject *proto = global->getOrCreateFunctionPrototype(cx); - if (!proto) - return nullptr; - return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject); -} - /* static */ bool GlobalObject::initIteratorClasses(JSContext *cx, Handle global) { @@ -2063,43 +1567,6 @@ GlobalObject::initIteratorClasses(JSContext *cx, Handle global) global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto)); } - if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) { - proto = NewSingletonObjectWithObjectPrototype(cx, global); - if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods)) - return false; - global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto)); - } - - if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) { - RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global)); - if (!genObjectProto) - return false; - if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods)) - return false; - - RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); - if (!genFunctionProto) - return false; - if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto)) - return false; - - RootedValue function(cx, global->getConstructor(JSProto_Function)); - if (!function.toObjectOrNull()) - return false; - RootedAtom name(cx, cx->names().GeneratorFunction); - RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1, - JSFunction::NATIVE_CTOR, global, name, - &function.toObject())); - if (!genFunction) - return false; - if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto)) - return false; - - global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto)); - global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction)); - global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto)); - } - return GlobalObject::initStopIterationClass(cx, global); } @@ -2128,5 +1595,7 @@ js_InitIteratorClasses(JSContext *cx, HandleObject obj) Rooted global(cx, &obj->as()); if (!GlobalObject::initIteratorClasses(cx, global)) return nullptr; + if (!GlobalObject::initGeneratorClasses(cx, global)) + return nullptr; return global->getIteratorPrototype(); } diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 402cef8a5a92..e9babee2ee80 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -219,39 +219,6 @@ CreateItrResultObject(JSContext *cx, HandleValue value, bool done); } /* namespace js */ -/* - * Generator state codes. - */ -enum JSGeneratorState -{ - JSGEN_NEWBORN, /* not yet started */ - JSGEN_OPEN, /* started by a .next() or .send(undefined) call */ - JSGEN_RUNNING, /* currently executing via .next(), etc., call */ - JSGEN_CLOSING, /* close method is doing asynchronous return */ - JSGEN_CLOSED /* closed, cannot be started or closed again */ -}; - -struct JSGenerator -{ - js::HeapPtrObject obj; - JSGeneratorState state; - js::InterpreterRegs regs; - JSGenerator *prevGenerator; - js::InterpreterFrame *fp; -#if JS_BITS_PER_WORD == 32 - uint32_t padding; -#endif - - js::HeapValue *stackSnapshot() { - static_assert(sizeof(JSGenerator) % sizeof(js::HeapValue) == 0, - "The generator must have Value alignment for JIT access."); - return reinterpret_cast(this + 1); - } -}; - -extern JSObject * -js_NewGenerator(JSContext *cx, const js::InterpreterRegs ®s); - extern JSObject * js_InitIteratorClasses(JSContext *cx, js::HandleObject obj); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 9bc08a689d23..0c04f656e158 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4250,8 +4250,6 @@ js_DumpInterpreterFrame(JSContext *cx, InterpreterFrame *start) fprintf(stderr, " debugger"); if (i.isEvalFrame()) fprintf(stderr, " eval"); - if (!i.isJit() && i.interpFrame()->isYielding()) - fprintf(stderr, " yielding"); if (!i.isJit() && i.interpFrame()->isGeneratorFrame()) fprintf(stderr, " generator"); fputc('\n', stderr); diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 75904b4d502b..f65d48810bdd 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -298,6 +298,8 @@ BytecodeFallsThrough(JSOp op) case JSOP_DEFAULT: case JSOP_RETURN: case JSOP_RETRVAL: + case JSOP_FINALYIELD: + case JSOP_FINALYIELDRVAL: case JSOP_THROW: case JSOP_TABLESWITCH: return false; @@ -599,8 +601,18 @@ inline bool FlowsIntoNext(JSOp op) { /* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */ - return op != JSOP_RETRVAL && op != JSOP_RETURN && op != JSOP_THROW && - op != JSOP_GOTO && op != JSOP_RETSUB; + switch (op) { + case JSOP_RETRVAL: + case JSOP_RETURN: + case JSOP_THROW: + case JSOP_GOTO: + case JSOP_RETSUB: + case JSOP_FINALYIELD: + case JSOP_FINALYIELDRVAL: + return false; + default: + return true; + } } inline bool diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 8bd71076d712..a697f56842b3 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -2665,11 +2665,11 @@ ASTSerializer::generatorExpression(ParseNode *pn, MutableHandleValue dst) LOCAL_ASSERT(next->isKind(PNK_SEMI) && next->pn_kid->isKind(PNK_YIELD) && - next->pn_kid->pn_kid); + next->pn_kid->pn_left); RootedValue body(cx); - return expression(next->pn_kid->pn_kid, &body) && + return expression(next->pn_kid->pn_left, &body) && builder.generatorExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst); } @@ -2996,19 +2996,19 @@ ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst) case PNK_YIELD_STAR: { - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); + MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); RootedValue arg(cx); - return expression(pn->pn_kid, &arg) && + return expression(pn->pn_left, &arg) && builder.yieldExpression(arg, Delegating, &pn->pn_pos, dst); } case PNK_YIELD: { - MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos)); + MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos)); RootedValue arg(cx); - return optExpression(pn->pn_kid, &arg) && + return optExpression(pn->pn_left, &arg) && builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst); } @@ -3313,6 +3313,12 @@ ASTSerializer::functionArgsAndBody(ParseNode *pn, NodeVector &args, NodeVector & ? pnbody->pn_head->pn_next : pnbody->pn_head; + // Skip over initial yield in generator. + if (pnstart && pnstart->isKind(PNK_YIELD)) { + MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD); + pnstart = pnstart->pn_next; + } + return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) && functionBody(pnstart, &pnbody->pn_pos, body); } diff --git a/js/src/moz.build b/js/src/moz.build index c325108b42bc..d67936488d42 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -243,6 +243,7 @@ UNIFIED_SOURCES += [ 'vm/DebuggerMemory.cpp', 'vm/ErrorObject.cpp', 'vm/ForkJoin.cpp', + 'vm/GeneratorObject.cpp', 'vm/GlobalObject.cpp', 'vm/HelperThreads.cpp', 'vm/Id.cpp', diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 75db592e55ae..bc33dce05cf9 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -33,6 +33,7 @@ macro(callFunction, callFunction, "callFunction") \ macro(caseFirst, caseFirst, "caseFirst") \ macro(class_, class_, "class") \ + macro(close, close, "close") \ macro(Collator, Collator, "Collator") \ macro(CollatorCompareGet, CollatorCompareGet, "Intl_Collator_compare_get") \ macro(columnNumber, columnNumber, "columnNumber") \ @@ -58,6 +59,7 @@ macro(deleteProperty, deleteProperty, "deleteProperty") \ macro(displayURL, displayURL, "displayURL") \ macro(done, done, "done") \ + macro(dotGenerator, dotGenerator, ".generator") \ macro(each, each, "each") \ macro(elementType, elementType, "elementType") \ macro(empty, empty, "") \ @@ -110,6 +112,7 @@ macro(join, join, "join") \ macro(keys, keys, "keys") \ macro(lastIndex, lastIndex, "lastIndex") \ + macro(LegacyGeneratorCloseInternal, LegacyGeneratorCloseInternal, "LegacyGeneratorCloseInternal") \ macro(length, length, "length") \ macro(let, let, "let") \ macro(line, line, "line") \ @@ -162,6 +165,7 @@ macro(prototype, prototype, "prototype") \ macro(proxy, proxy, "proxy") \ macro(Reify, Reify, "Reify") \ + macro(resumeGenerator, resumeGenerator, "resumeGenerator") \ macro(return, return_, "return") \ macro(revoke, revoke, "revoke") \ macro(scripts, scripts, "scripts") \ diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp new file mode 100644 index 000000000000..4389feed474f --- /dev/null +++ b/js/src/vm/GeneratorObject.cpp @@ -0,0 +1,329 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/GeneratorObject.h" + +#include "jsatominlines.h" +#include "jsscriptinlines.h" + +#include "vm/NativeObject-inl.h" +#include "vm/Stack-inl.h" + +using namespace js; +using namespace js::types; + +JSObject * +GeneratorObject::create(JSContext *cx, const InterpreterRegs ®s) +{ + MOZ_ASSERT(regs.stackDepth() == 0); + InterpreterFrame *fp = regs.fp(); + + MOZ_ASSERT(fp->script()->isGenerator()); + + Rooted global(cx, &fp->global()); + RootedNativeObject obj(cx); + if (fp->script()->isStarGenerator()) { + RootedValue pval(cx); + RootedObject fun(cx, fp->fun()); + // FIXME: This would be faster if we could avoid doing a lookup to get + // the prototype for the instance. Bug 906600. + if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval)) + return nullptr; + JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr; + if (!proto) { + proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global); + if (!proto) + return nullptr; + } + obj = NewNativeObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global); + } else { + MOZ_ASSERT(fp->script()->isLegacyGenerator()); + JSObject *proto = GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global); + if (!proto) + return nullptr; + obj = NewNativeObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto, global); + } + if (!obj) + return nullptr; + + Rooted genObj(cx, &obj->as()); + + genObj->setCallee(fp->callee()); + genObj->setThisValue(fp->thisValue()); + genObj->setScopeChain(*fp->scopeChain()); + if (fp->script()->needsArgsObj()) + genObj->setArgsObj(fp->argsObj()); + genObj->clearExpressionStack(); + + return obj; +} + +bool +GeneratorObject::suspend(JSContext *cx, HandleObject obj, InterpreterFrame *fp, jsbytecode *pc, + Value *vp, unsigned nvalues, GeneratorObject::SuspendKind suspendKind) +{ + Rooted genObj(cx, &obj->as()); + MOZ_ASSERT(!genObj->hasExpressionStack()); + + if (suspendKind == NORMAL && genObj->isClosing()) { + MOZ_ASSERT(genObj->is()); + RootedValue val(cx, ObjectValue(fp->callee())); + js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_SEARCH_STACK, val, js::NullPtr()); + return false; + } + + genObj->setSuspendedBytecodeOffset(pc - fp->script()->code(), suspendKind == INITIAL); + genObj->setScopeChain(*fp->scopeChain()); + + if (nvalues) { + ArrayObject *stack = NewDenseCopiedArray(cx, nvalues, vp); + if (!stack) + return false; + genObj->setExpressionStack(*stack); + } + + return true; +} + +bool +GeneratorObject::finalSuspend(JSContext *cx, HandleObject obj) +{ + Rooted genObj(cx, &obj->as()); + MOZ_ASSERT(genObj->isRunning() || genObj->isClosing()); + + bool closing = genObj->isClosing(); + MOZ_ASSERT_IF(closing, genObj->is()); + genObj->setClosed(); + + if (genObj->is() && !closing) + return ThrowStopIteration(cx); + + return true; +} + +bool +GeneratorObject::resume(JSContext *cx, InterpreterActivation &activation, + HandleObject obj, HandleValue arg, GeneratorObject::ResumeKind resumeKind) +{ + Rooted genObj(cx, &obj->as()); + MOZ_ASSERT(genObj->isSuspended()); + + RootedFunction callee(cx, &genObj->callee()); + RootedValue thisv(cx, genObj->thisValue()); + RootedObject scopeChain(cx, &genObj->scopeChain()); + if (!activation.resumeGeneratorFrame(callee, thisv, scopeChain)) + return false; + + if (genObj->hasArgsObj()) + activation.regs().fp()->initArgsObj(genObj->argsObj()); + + if (genObj->hasExpressionStack()) { + uint32_t len = genObj->expressionStack().length(); + MOZ_ASSERT(activation.regs().spForStackDepth(len)); + RootedObject array(cx, &genObj->expressionStack()); + GetElements(cx, array, len, activation.regs().sp); + activation.regs().sp += len; + genObj->clearExpressionStack(); + } + + activation.regs().pc = callee->nonLazyScript()->code() + genObj->suspendedBytecodeOffset(); + + // If we are resuming a JSOP_YIELD, always push on a value, even if we are + // raising an exception. In the exception case, the stack needs to have + // something on it so that exception handling doesn't skip the catch + // blocks. See TryNoteIter::settle. + if (!genObj->isNewborn()) { + activation.regs().sp++; + MOZ_ASSERT(activation.regs().spForStackDepth(activation.regs().stackDepth())); + activation.regs().sp[-1] = arg; + } + + switch (resumeKind) { + case NEXT: + genObj->setRunning(); + return true; + + case THROW: + cx->setPendingException(arg); + if (genObj->isNewborn()) + genObj->setClosed(); + else + genObj->setRunning(); + return false; + + case CLOSE: + MOZ_ASSERT(genObj->is()); + cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING)); + genObj->setClosing(); + return false; + + default: + MOZ_CRASH("bad resumeKind"); + } +} + +bool +LegacyGeneratorObject::close(JSContext *cx, HandleObject obj) +{ + Rooted genObj(cx, &obj->as()); + + // Avoid calling back into JS unless it is necessary. + if (genObj->isClosed()) + return true; + + if (genObj->isNewborn()) { + genObj->setClosed(); + return true; + } + + RootedValue rval(cx); + + RootedValue closeValue(cx); + if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().LegacyGeneratorCloseInternal, + &closeValue)) + { + return false; + } + MOZ_ASSERT(closeValue.isObject()); + MOZ_ASSERT(closeValue.toObject().is()); + + InvokeArgs args(cx); + if (!args.init(0)) + return false; + + args.setCallee(closeValue); + args.setThis(ObjectValue(*genObj)); + + return Invoke(cx, args); +} + +static JSObject * +iterator_iteratorObject(JSContext *cx, HandleObject obj, bool keysonly) +{ + return obj; +} + +const Class LegacyGeneratorObject::class_ = { + "Generator", + JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS), + JS_PropertyStub, /* addProperty */ + JS_DeletePropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + nullptr, /* finalize */ + nullptr, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + nullptr, /* trace */ + JS_NULL_CLASS_SPEC, + { + nullptr, /* outerObject */ + nullptr, /* innerObject */ + iterator_iteratorObject, + } +}; + +const Class StarGeneratorObject::class_ = { + "Generator", + JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS), + JS_PropertyStub, /* addProperty */ + JS_DeletePropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + nullptr, /* finalize */ + nullptr, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + nullptr, /* trace */ +}; + +static const JSFunctionSpec star_generator_methods[] = { + JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0), + JS_SELF_HOSTED_FN("next", "StarGeneratorNext", 1, 0), + JS_SELF_HOSTED_FN("throw", "StarGeneratorThrow", 1, 0), + JS_FS_END +}; + +#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) + +static const JSFunctionSpec legacy_generator_methods[] = { + JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0), + // "send" is an alias for "next". + JS_SELF_HOSTED_FN("next", "LegacyGeneratorNext", 1, JSPROP_ROPERM), + JS_SELF_HOSTED_FN("send", "LegacyGeneratorNext", 1, JSPROP_ROPERM), + JS_SELF_HOSTED_FN("throw", "LegacyGeneratorThrow", 1, JSPROP_ROPERM), + JS_SELF_HOSTED_FN("close", "LegacyGeneratorClose", 0, JSPROP_ROPERM), + JS_FS_END +}; + +#undef JSPROP_ROPERM + +static JSObject* +NewSingletonObjectWithObjectPrototype(JSContext *cx, Handle global) +{ + JSObject *proto = global->getOrCreateObjectPrototype(cx); + if (!proto) + return nullptr; + return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject); +} + +static JSObject* +NewSingletonObjectWithFunctionPrototype(JSContext *cx, Handle global) +{ + JSObject *proto = global->getOrCreateFunctionPrototype(cx); + if (!proto) + return nullptr; + return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject); +} + +/* static */ bool +GlobalObject::initGeneratorClasses(JSContext *cx, Handle global) +{ + if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) { + RootedObject proto(cx, NewSingletonObjectWithObjectPrototype(cx, global)); + if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods)) + return false; + global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto)); + } + + if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) { + RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global)); + if (!genObjectProto) + return false; + if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods)) + return false; + + RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); + if (!genFunctionProto) + return false; + if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto)) + return false; + + RootedValue function(cx, global->getConstructor(JSProto_Function)); + if (!function.toObjectOrNull()) + return false; + RootedAtom name(cx, cx->names().GeneratorFunction); + RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1, + JSFunction::NATIVE_CTOR, global, name, + &function.toObject())); + if (!genFunction) + return false; + if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto)) + return false; + + global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto)); + global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction)); + global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto)); + } + + return true; +} diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h index 889933e3cce3..c69d0729bcb7 100644 --- a/js/src/vm/GeneratorObject.h +++ b/js/src/vm/GeneratorObject.h @@ -7,26 +7,199 @@ #ifndef vm_GeneratorObject_h #define vm_GeneratorObject_h +#include "jscntxt.h" #include "jsobj.h" +#include "vm/ArgumentsObject.h" +#include "vm/ArrayObject.h" +#include "vm/Stack.h" + namespace js { -class LegacyGeneratorObject : public NativeObject +class GeneratorObject : public NativeObject { - public: - static const Class class_; + static const int32_t MAX_BYTECODE_OFFSET = INT32_MAX >> 1; - JSGenerator *getGenerator() { return static_cast(getPrivate()); } + public: + enum { + CALLEE_SLOT = 0, + THIS_SLOT, + SCOPE_CHAIN_SLOT, + ARGS_OBJ_SLOT, + EXPRESSION_STACK_SLOT, + BYTECODE_OFFSET_SLOT, + RESERVED_SLOTS + }; + + enum SuspendKind { INITIAL, NORMAL, FINAL }; + enum ResumeKind { NEXT, THROW, CLOSE }; + + static inline ResumeKind getResumeKind(jsbytecode *pc) { + MOZ_ASSERT(*pc == JSOP_RESUME); + unsigned arg = GET_UINT16(pc); + MOZ_ASSERT(arg <= CLOSE); + return static_cast(arg); + } + + static inline ResumeKind getResumeKind(ExclusiveContext *cx, JSAtom *atom) { + if (atom == cx->names().next) + return NEXT; + if (atom == cx->names().throw_) + return THROW; + MOZ_ASSERT(atom == cx->names().close); + return CLOSE; + } + + static JSObject *create(JSContext *cx, const InterpreterRegs ®s); + + static bool suspend(JSContext *cx, HandleObject obj, InterpreterFrame *fp, jsbytecode *pc, + Value *vp, unsigned nvalues, SuspendKind kind); + + static bool resume(JSContext *cx, InterpreterActivation &activation, + HandleObject obj, HandleValue arg, ResumeKind resumeKind); + + static bool initialSuspend(JSContext *cx, HandleObject obj, InterpreterFrame *fp, jsbytecode *pc) { + return suspend(cx, obj, fp, pc, nullptr, 0, INITIAL); + } + + static bool normalSuspend(JSContext *cx, HandleObject obj, InterpreterFrame *fp, jsbytecode *pc, + Value *vp, unsigned nvalues) { + return suspend(cx, obj, fp, pc, vp, nvalues, NORMAL); + } + + static bool finalSuspend(JSContext *cx, HandleObject obj); + + JSFunction &callee() const { + return getFixedSlot(CALLEE_SLOT).toObject().as(); + } + void setCallee(JSFunction &callee) { + setFixedSlot(CALLEE_SLOT, ObjectValue(callee)); + } + + const Value &thisValue() const { + return getFixedSlot(THIS_SLOT); + } + void setThisValue(Value &thisv) { + setFixedSlot(THIS_SLOT, thisv); + } + + JSObject &scopeChain() const { + return getFixedSlot(SCOPE_CHAIN_SLOT).toObject(); + } + void setScopeChain(JSObject &scopeChain) { + setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(scopeChain)); + } + + bool hasArgsObj() const { + return getFixedSlot(ARGS_OBJ_SLOT).isObject(); + } + ArgumentsObject &argsObj() const { + return getFixedSlot(ARGS_OBJ_SLOT).toObject().as(); + } + void setArgsObj(ArgumentsObject &argsObj) { + setFixedSlot(ARGS_OBJ_SLOT, ObjectValue(argsObj)); + } + + bool hasExpressionStack() const { + return getFixedSlot(EXPRESSION_STACK_SLOT).isObject(); + } + ArrayObject &expressionStack() const { + return getFixedSlot(EXPRESSION_STACK_SLOT).toObject().as(); + } + void setExpressionStack(ArrayObject &expressionStack) { + setFixedSlot(EXPRESSION_STACK_SLOT, ObjectValue(expressionStack)); + } + void clearExpressionStack() { + setFixedSlot(EXPRESSION_STACK_SLOT, NullValue()); + } + + // The bytecode offset slot is abused for a few purposes. It's undefined if + // it hasn't been set yet (before the initial yield), and null if the + // generator is closed. Otherwise, the lower bit is set if the generator + // is in the "suspendedStart" set, and cleared otherwise. The upper bits + // are used for the PC offset of the suspended continuation, and are zero if + // the generator is running. If the generator is in that bizarre "closing" + // state, all upper bits are set. + + bool isRunning() const { + MOZ_ASSERT(!isClosed()); + return getFixedSlot(BYTECODE_OFFSET_SLOT).toInt32() == 0; + } + bool isClosing() const { + return getFixedSlot(BYTECODE_OFFSET_SLOT).toInt32() == MAX_BYTECODE_OFFSET << 1; + } + bool isSuspended() const { + MOZ_ASSERT(!isClosed()); + // Note that newborn objects also count as suspended. + return !isRunning() && !isClosing(); + } + bool isNewborn() const { + MOZ_ASSERT(!isClosed()); + return getFixedSlot(BYTECODE_OFFSET_SLOT).toInt32() & 0x1; + } + void setRunning() { + MOZ_ASSERT(isSuspended()); + setFixedSlot(BYTECODE_OFFSET_SLOT, Int32Value(0)); + } + void setClosing() { + MOZ_ASSERT(isSuspended()); + setFixedSlot(BYTECODE_OFFSET_SLOT, Int32Value(MAX_BYTECODE_OFFSET << 1)); + } + ptrdiff_t suspendedBytecodeOffset() const { + MOZ_ASSERT(isSuspended()); + return getFixedSlot(BYTECODE_OFFSET_SLOT).toInt32() >> 1; + } + void setSuspendedBytecodeOffset(ptrdiff_t offset, bool newborn) { + MOZ_ASSERT(newborn ? getFixedSlot(BYTECODE_OFFSET_SLOT).isUndefined() : isRunning()); + MOZ_ASSERT(offset > 0 && offset < MAX_BYTECODE_OFFSET); + setFixedSlot(BYTECODE_OFFSET_SLOT, Int32Value((offset << 1) | (newborn ? 0x1 : 0))); + MOZ_ASSERT(isSuspended()); + } + bool isClosed() const { + return getFixedSlot(CALLEE_SLOT).isNull(); + } + void setClosed() { + setFixedSlot(CALLEE_SLOT, NullValue()); + setFixedSlot(THIS_SLOT, NullValue()); + setFixedSlot(SCOPE_CHAIN_SLOT, NullValue()); + setFixedSlot(ARGS_OBJ_SLOT, NullValue()); + setFixedSlot(EXPRESSION_STACK_SLOT, NullValue()); + setFixedSlot(BYTECODE_OFFSET_SLOT, NullValue()); + } }; -class StarGeneratorObject : public NativeObject +class LegacyGeneratorObject : public GeneratorObject { public: static const Class class_; - JSGenerator *getGenerator() { return static_cast(getPrivate()); } + static bool close(JSContext *cx, HandleObject obj); + + // Unlike most other methods returning boolean, if this returns false, it + // doesn't mean that an error was raised -- it just means that the object + // wasn't newborn. + static bool maybeCloseNewborn(LegacyGeneratorObject *genObj) { + if (genObj->isNewborn()) { + genObj->setClosed(); + return true; + } + return false; + } +}; + +class StarGeneratorObject : public GeneratorObject +{ + public: + static const Class class_; }; } // namespace js +template<> +inline bool +JSObject::is() const +{ + return is() || is(); +} + #endif /* vm_GeneratorObject_h */ diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 771fe315d63a..80a16064b661 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -615,6 +615,9 @@ class GlobalObject : public NativeObject static bool initIteratorClasses(JSContext *cx, Handle global); static bool initStopIterationClass(JSContext *cx, Handle global); + // Implemented in vm/GeneratorObject.cpp. + static bool initGeneratorClasses(JSContext *cx, Handle global); + // Implemented in builtin/MapObject.cpp. static bool initMapIteratorProto(JSContext *cx, Handle global); static bool initSetIteratorProto(JSContext *cx, Handle global); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 7414b15a9c08..b93281feef13 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -35,6 +35,7 @@ #include "jit/Ion.h" #include "jit/IonAnalysis.h" #include "vm/Debugger.h" +#include "vm/GeneratorObject.h" #include "vm/Opcodes.h" #include "vm/Shape.h" #include "vm/TraceLogging.h" @@ -1657,10 +1658,6 @@ CASE(JSOP_UNUSED190) CASE(JSOP_UNUSED191) CASE(JSOP_UNUSED192) CASE(JSOP_UNUSED196) -CASE(JSOP_UNUSED201) -CASE(JSOP_UNUSED205) -CASE(JSOP_UNUSED206) -CASE(JSOP_UNUSED207) CASE(JSOP_UNUSED208) CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) @@ -1774,9 +1771,7 @@ CASE(JSOP_RETRVAL) successful_return_continuation: interpReturnOK = true; return_continuation: - if (activation.entryFrame() != REGS.fp()) - inline_return: - { + if (activation.entryFrame() != REGS.fp()) { // Stop the engine. (No details about which engine exactly, could be // interpreter, Baseline or IonMonkey.) TraceLogStopEvent(logger); @@ -1785,11 +1780,7 @@ CASE(JSOP_RETRVAL) interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK); - if (!REGS.fp()->isYielding()) - REGS.fp()->epilogue(cx); - else - probes::ExitScript(cx, script, script->functionNonDelazifying(), - REGS.fp()->hasPushedSPSFrame()); + REGS.fp()->epilogue(cx); jit_return_pop_frame: @@ -3380,32 +3371,80 @@ CASE(JSOP_GENERATOR) { MOZ_ASSERT(!cx->isExceptionPending()); REGS.fp()->initGeneratorFrame(); - REGS.pc += JSOP_GENERATOR_LENGTH; - JSObject *obj = js_NewGenerator(cx, REGS); + JSObject *obj = GeneratorObject::create(cx, REGS); if (!obj) goto error; - REGS.fp()->setReturnValue(ObjectValue(*obj)); - REGS.fp()->setYielding(); - interpReturnOK = true; - if (activation.entryFrame() != REGS.fp()) - goto inline_return; - goto exit; + PUSH_OBJECT(*obj); +} +END_CASE(JSOP_GENERATOR) + +CASE(JSOP_INITIALYIELD) +{ + MOZ_ASSERT(!cx->isExceptionPending()); + MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); + RootedObject &obj = rootObject0; + obj = ®S.sp[-1].toObject(); + POP_RETURN_VALUE(); + MOZ_ASSERT(REGS.stackDepth() == 0); + if (!GeneratorObject::initialSuspend(cx, obj, REGS.fp(), REGS.pc + JSOP_INITIALYIELD_LENGTH)) + goto error; + goto successful_return_continuation; } CASE(JSOP_YIELD) +{ MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); - if (cx->innermostGenerator()->state == JSGEN_CLOSING) { - RootedValue &val = rootValue0; - val.setObject(REGS.fp()->callee()); - js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_SEARCH_STACK, val, js::NullPtr()); + RootedObject &obj = rootObject0; + obj = ®S.sp[-1].toObject(); + if (!GeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc + JSOP_YIELD_LENGTH, + REGS.spForStackDepth(0), REGS.stackDepth() - 2)) + { goto error; } - REGS.fp()->setReturnValue(REGS.sp[-1]); - REGS.fp()->setYielding(); - REGS.pc += JSOP_YIELD_LENGTH; - interpReturnOK = true; - goto exit; + + REGS.sp--; + POP_RETURN_VALUE(); + + goto successful_return_continuation; +} + +CASE(JSOP_RESUME) +{ + RootedObject &gen = rootObject0; + RootedValue &val = rootValue0; + val = REGS.sp[-1]; + gen = ®S.sp[-2].toObject(); + // popInlineFrame expects there to be an additional value on the stack to + // pop off, so leave "gen" on the stack. + + GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(REGS.pc); + bool ok = GeneratorObject::resume(cx, activation, gen, val, resumeKind); + SET_SCRIPT(REGS.fp()->script()); + if (!ok) + goto error; + + ADVANCE_AND_DISPATCH(0); +} + +CASE(JSOP_FINALYIELD) + REGS.fp()->setReturnValue(REGS.sp[-2]); + REGS.sp[-2] = REGS.sp[-1]; + REGS.sp--; + /* FALL THROUGH */ +CASE(JSOP_FINALYIELDRVAL) +{ + RootedObject &gen = rootObject0; + gen = ®S.sp[-1].toObject(); + REGS.sp--; + + if (!GeneratorObject::finalSuspend(cx, gen)) { + interpReturnOK = false; + goto return_continuation; + } + + goto successful_return_continuation; +} CASE(JSOP_ARRAYPUSH) { @@ -3463,11 +3502,7 @@ DEFAULT() exit: interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK); - if (!REGS.fp()->isYielding()) - REGS.fp()->epilogue(cx); - else - probes::ExitScript(cx, script, script->functionNonDelazifying(), - REGS.fp()->hasPushedSPSFrame()); + REGS.fp()->epilogue(cx); gc::MaybeVerifyBarriers(cx, true); diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 91f4206e3e51..0f92435e68cd 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -111,7 +111,6 @@ Execute(JSContext *cx, HandleScript script, JSObject &scopeChain, Value *rval); class ExecuteState; class InvokeState; -class GeneratorState; // RunState is passed to RunScript and RunScript then eiter passes it to the // interpreter or to the JITs. RunState contains all information we need to @@ -119,7 +118,7 @@ class GeneratorState; class RunState { protected: - enum Kind { Execute, Invoke, Generator }; + enum Kind { Execute, Invoke }; Kind kind_; RootedScript script_; @@ -132,7 +131,6 @@ class RunState public: bool isExecute() const { return kind_ == Execute; } bool isInvoke() const { return kind_ == Invoke; } - bool isGenerator() const { return kind_ == Generator; } ExecuteState *asExecute() const { MOZ_ASSERT(isExecute()); @@ -142,10 +140,6 @@ class RunState MOZ_ASSERT(isInvoke()); return (InvokeState *)this; } - GeneratorState *asGenerator() const { - MOZ_ASSERT(isGenerator()); - return (GeneratorState *)this; - } JS::HandleScript script() const { return script_; } @@ -158,7 +152,6 @@ class RunState RunState(const RunState &other) MOZ_DELETE; RunState(const ExecuteState &other) MOZ_DELETE; RunState(const InvokeState &other) MOZ_DELETE; - RunState(const GeneratorState &other) MOZ_DELETE; void operator=(const RunState &other) MOZ_DELETE; }; @@ -224,24 +217,6 @@ class InvokeState : public RunState } }; -// Generator script. -class GeneratorState : public RunState -{ - JSContext *cx_; - JSGenerator *gen_; - JSGeneratorState futureState_; - bool entered_; - - public: - GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState); - ~GeneratorState(); - - virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx); - virtual void setReturnValue(Value) { } - - JSGenerator *gen() const { return gen_; } -}; - extern bool RunScript(JSContext *cx, RunState &state); diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 3f8a5284b20d..99f0c39c9aae 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1551,27 +1551,61 @@ */ \ macro(JSOP_DEBUGLEAVEBLOCK, 200,"debugleaveblock", NULL, 1, 0, 0, JOF_BYTE) \ \ - macro(JSOP_UNUSED201, 201,"unused201", NULL, 1, 0, 0, JOF_BYTE) \ - \ /* - * Initializes generator frame, creates a generator, sets 'YIELDING' flag, - * stops interpretation and returns the generator. + * Initializes generator frame, creates a generator and pushes it on the + * stack. * Category: Statements * Type: Generator * Operands: - * Stack: => + * Stack: => generator */ \ - macro(JSOP_GENERATOR, 202,"generator", NULL, 1, 0, 0, JOF_BYTE) \ + macro(JSOP_GENERATOR, 201,"generator", NULL, 1, 0, 1, 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. + * Pops the generator from the top of the stack, suspends it and stops + * interpretation. * Category: Statements * Type: Generator * Operands: - * Stack: rval1 => rval2 + * Stack: generator => */ \ - macro(JSOP_YIELD, 203,"yield", NULL, 1, 1, 1, JOF_BYTE) \ + macro(JSOP_INITIALYIELD, 202,"initialyield", NULL, 1, 1, 0, JOF_BYTE) \ + /* + * Pops the generator and the return value 'rval1', stops interpretation and + * returns 'rval1'. Pushes sent value from 'send()' onto the stack. + * Category: Statements + * Type: Generator + * Operands: + * Stack: rval1, gen => rval2 + */ \ + macro(JSOP_YIELD, 203,"yield", NULL, 1, 2, 1, JOF_BYTE) \ + /* + * Pops the generator and the value to yield from the stack. Then suspends + * and closes the generator. + * Category: Statements + * Type: Generator + * Operands: + * Stack: gen, val => + */ \ + macro(JSOP_FINALYIELD, 204,"finalyield", NULL, 1, 2, 0, JOF_BYTE) \ + /* + * Pops the generator and suspends and closes it. Yields the value in the + * frame's return value slot. + * Category: Statements + * Type: Generator + * Operands: + * Stack: gen => + */ \ + macro(JSOP_FINALYIELDRVAL,205,"finalyieldrval",NULL, 1, 1, 0, JOF_BYTE) \ + /* + * Pops the generator and argument from the stack, pushes a new generator + * frame and resumes execution of it. Pushes the return value after the + * generator yields. + * Category: Statements + * Type: Generator + * Operands: resume kind (GeneratorObject::ResumeKind) + * Stack: gen, val => rval + */ \ + macro(JSOP_RESUME, 206,"resume", NULL, 3, 2, 1, JOF_UINT8|JOF_INVOKE) \ /* * Pops the top two values on the stack as 'obj' and 'v', pushes 'v' to * 'obj'. @@ -1582,12 +1616,8 @@ * Operands: * Stack: v, obj => */ \ - macro(JSOP_ARRAYPUSH, 204,"arraypush", NULL, 1, 2, 0, JOF_BYTE) \ + macro(JSOP_ARRAYPUSH, 207,"arraypush", NULL, 1, 2, 0, JOF_BYTE) \ \ - macro(JSOP_UNUSED205, 205, "unused205", NULL, 1, 0, 0, JOF_BYTE) \ - macro(JSOP_UNUSED206, 206, "unused206", NULL, 1, 0, 0, JOF_BYTE) \ - \ - macro(JSOP_UNUSED207, 207, "unused207", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED208, 208, "unused208", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index fd125e7782c2..49ef141960b9 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -2149,7 +2149,6 @@ DebugScopes::addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject void DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx) { - MOZ_ASSERT(!frame.isYielding()); assertSameCompartment(cx, frame); DebugScopes *scopes = cx->compartment()->debugScopes; @@ -2166,6 +2165,9 @@ DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx) if (!frame.hasCallObj()) return; + if (frame.fun()->isGenerator()) + return; + CallObject &callobj = frame.scopeChain()->as(); scopes->liveScopes.remove(&callobj); if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&callobj)) diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index a6bf2e86074f..16d532367560 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -23,6 +23,7 @@ #include "gc/Marking.h" #include "vm/Compression.h" #include "vm/ForkJoin.h" +#include "vm/GeneratorObject.h" #include "vm/Interpreter.h" #include "vm/String.h" #include "vm/TypedArrayCommon.h" @@ -655,6 +656,159 @@ intrinsic_IsStringIterator(JSContext *cx, unsigned argc, Value *vp) return true; } +static bool +intrinsic_IsStarGeneratorObject(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + args.rval().setBoolean(args[0].toObject().is()); + return true; +} + +static bool +intrinsic_StarGeneratorObjectIsClosed(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + StarGeneratorObject *genObj = &args[0].toObject().as(); + args.rval().setBoolean(genObj->isClosed()); + return true; +} + +static bool +intrinsic_IsLegacyGeneratorObject(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + args.rval().setBoolean(args[0].toObject().is()); + return true; +} + +static bool +intrinsic_LegacyGeneratorObjectIsClosed(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + LegacyGeneratorObject *genObj = &args[0].toObject().as(); + args.rval().setBoolean(genObj->isClosed()); + return true; +} + +static bool +intrinsic_CloseNewbornLegacyGeneratorObject(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + LegacyGeneratorObject *genObj = &args[0].toObject().as(); + args.rval().setBoolean(LegacyGeneratorObject::maybeCloseNewborn(genObj)); + return true; +} + +static bool +intrinsic_CloseClosingLegacyGeneratorObject(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + LegacyGeneratorObject *genObj = &args[0].toObject().as(); + MOZ_ASSERT(genObj->isClosing()); + genObj->setClosed(); + return true; +} + +static bool +intrinsic_ThrowStopIteration(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + + return ThrowStopIteration(cx); +} + +static bool +intrinsic_GeneratorIsRunning(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + GeneratorObject *genObj = &args[0].toObject().as(); + args.rval().setBoolean(genObj->isRunning() || genObj->isClosing()); + return true; +} + +static bool +intrinsic_GeneratorSetClosed(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + GeneratorObject *genObj = &args[0].toObject().as(); + genObj->setClosed(); + return true; +} + +bool +CallSelfHostedNonGenericMethod(JSContext *cx, CallArgs args) +{ + // This function is called when a self-hosted method is invoked on a + // wrapper object, like a CrossCompartmentWrapper. The last argument is + // the name of the self-hosted function. The other arguments are the + // arguments to pass to this function. + + MOZ_ASSERT(args.length() > 0); + RootedPropertyName name(cx, args[args.length() - 1].toString()->asAtom().asPropertyName()); + + RootedValue selfHostedFun(cx); + if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &selfHostedFun)) + return false; + + MOZ_ASSERT(selfHostedFun.toObject().is()); + + InvokeArgs args2(cx); + if (!args2.init(args.length() - 1)) + return false; + + args2.setCallee(selfHostedFun); + args2.setThis(args.thisv()); + + for (size_t i = 0; i < args.length() - 1; i++) + args2[i].set(args[i]); + + if (!Invoke(cx, args2)) + return false; + + args.rval().set(args2.rval()); + return true; +} + +template +MOZ_ALWAYS_INLINE bool +IsObjectOfType(HandleValue v) +{ + return v.isObject() && v.toObject().is(); +} + +template +static bool +NativeMethod(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod, Impl>(cx, args); +} + static bool intrinsic_IsWeakSet(JSContext *cx, unsigned argc, Value *vp) { @@ -902,6 +1056,23 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0), JS_FN("IsStringIterator", intrinsic_IsStringIterator, 1,0), + JS_FN("IsStarGeneratorObject", intrinsic_IsStarGeneratorObject, 1,0), + JS_FN("StarGeneratorObjectIsClosed", intrinsic_StarGeneratorObjectIsClosed, 1,0), + + JS_FN("IsLegacyGeneratorObject", intrinsic_IsLegacyGeneratorObject, 1,0), + JS_FN("LegacyGeneratorObjectIsClosed", intrinsic_LegacyGeneratorObjectIsClosed, 1,0), + JS_FN("CloseNewbornLegacyGeneratorObject", intrinsic_CloseNewbornLegacyGeneratorObject, 1,0), + JS_FN("CloseClosingLegacyGeneratorObject", intrinsic_CloseClosingLegacyGeneratorObject, 1,0), + JS_FN("ThrowStopIteration", intrinsic_ThrowStopIteration, 0,0), + + JS_FN("GeneratorIsRunning", intrinsic_GeneratorIsRunning, 1,0), + JS_FN("GeneratorSetClosed", intrinsic_GeneratorSetClosed, 1,0), + + JS_FN("CallLegacyGeneratorMethodIfWrapped", + (NativeMethod), 2, 0), + JS_FN("CallStarGeneratorMethodIfWrapped", + (NativeMethod), 2, 0), + JS_FN("IsWeakSet", intrinsic_IsWeakSet, 1,0), JS_FN("ForkJoin", intrinsic_ForkJoin, 5,0), diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index d13b46ac7716..78e51ef71953 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -15,6 +15,7 @@ #include "jit/BaselineFrame.h" #include "jit/RematerializedFrame.h" +#include "vm/GeneratorObject.h" #include "vm/ScopeObject.h" #include "jsobjinlines.h" @@ -326,6 +327,45 @@ InterpreterStack::pushInlineFrame(JSContext *cx, InterpreterRegs ®s, const Ca return true; } +MOZ_ALWAYS_INLINE bool +InterpreterStack::resumeGeneratorCallFrame(JSContext *cx, InterpreterRegs ®s, + HandleFunction callee, HandleValue thisv, + HandleObject scopeChain) +{ + MOZ_ASSERT(callee->isGenerator()); + RootedScript script(cx, callee->getOrCreateScript(cx)); + InterpreterFrame *prev = regs.fp(); + jsbytecode *prevpc = regs.pc; + Value *prevsp = regs.sp; + MOZ_ASSERT(prev); + + script->ensureNonLazyCanonicalFunction(cx); + + LifoAlloc::Mark mark = allocator_.mark(); + + // Include callee, |this|. + unsigned nformal = callee->nargs(); + unsigned nvals = 2 + nformal + script->nslots(); + + uint8_t *buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvals * sizeof(Value)); + if (!buffer) + return false; + + Value *argv = reinterpret_cast(buffer) + 2; + argv[-2] = ObjectValue(*callee); + argv[-1] = thisv; + SetValueRangeToUndefined(argv, nformal); + + InterpreterFrame *fp = reinterpret_cast(argv + nformal); + InterpreterFrame::Flags flags = ToFrameFlags(INITIAL_NONE); + fp->mark_ = mark; + fp->initCallFrame(cx, prev, prevpc, prevsp, *callee, script, argv, 0, flags); + fp->resumeGeneratorFrame(scopeChain); + + regs.prepareToRun(*fp, script); + return true; +} + MOZ_ALWAYS_INLINE void InterpreterStack::popInlineFrame(InterpreterRegs ®s) { @@ -517,14 +557,6 @@ AbstractFramePtr::isGeneratorFrame() const return false; } -inline bool -AbstractFramePtr::isYielding() const -{ - if (isInterpreterFrame()) - return asInterpreterFrame()->isYielding(); - return false; -} - inline bool AbstractFramePtr::isFunctionFrame() const { @@ -809,13 +841,8 @@ InterpreterActivation::InterpreterActivation(RunState &state, JSContext *cx, , oldFrameCount_(cx->runtime()->interpreterStack().frameCount_) #endif { - if (!state.isGenerator()) { - regs_.prepareToRun(*entryFrame, state.script()); - MOZ_ASSERT(regs_.pc == state.script()->code()); - } else { - regs_ = state.asGenerator()->gen()->regs; - } - + regs_.prepareToRun(*entryFrame, state.script()); + MOZ_ASSERT(regs_.pc == state.script()->code()); MOZ_ASSERT_IF(entryFrame_->isEvalFrame(), state_.script()->isActiveEval()); } @@ -829,13 +856,6 @@ InterpreterActivation::~InterpreterActivation() MOZ_ASSERT(oldFrameCount_ == cx->runtime()->interpreterStack().frameCount_); MOZ_ASSERT_IF(oldFrameCount_ == 0, cx->runtime()->interpreterStack().allocator_.used() == 0); - if (state_.isGenerator()) { - JSGenerator *gen = state_.asGenerator()->gen(); - gen->fp->unsetPushedSPSFrame(); - gen->regs = regs_; - return; - } - if (entryFrame_) cx->runtime()->interpreterStack().releaseFrame(entryFrame_); } @@ -861,6 +881,18 @@ InterpreterActivation::popInlineFrame(InterpreterFrame *frame) cx_->asJSContext()->runtime()->interpreterStack().popInlineFrame(regs_); } +inline bool +InterpreterActivation::resumeGeneratorFrame(HandleFunction callee, HandleValue thisv, + HandleObject scopeChain) +{ + InterpreterStack &stack = cx_->asJSContext()->runtime()->interpreterStack(); + if (!stack.resumeGeneratorCallFrame(cx_->asJSContext(), regs_, callee, thisv, scopeChain)) + return false; + + MOZ_ASSERT(regs_.fp()->script()->compartment() == compartment_); + return true; +} + inline JSContext * AsmJSActivation::cx() { diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 05d232b613db..f4dc2733a027 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -92,47 +92,6 @@ InterpreterFrame::initExecuteFrame(JSContext *cx, JSScript *script, AbstractFram #endif } -template -void -InterpreterFrame::copyFrameAndValues(JSContext *cx, Value *vp, InterpreterFrame *otherfp, - const Value *othervp, Value *othersp) -{ - MOZ_ASSERT(othervp == otherfp->generatorArgsSnapshotBegin()); - MOZ_ASSERT(othersp >= otherfp->slots()); - MOZ_ASSERT(othersp <= otherfp->generatorSlotsSnapshotBegin() + otherfp->script()->nslots()); - - /* Copy args, InterpreterFrame, and slots. */ - const Value *srcend = otherfp->generatorArgsSnapshotEnd(); - Value *dst = vp; - for (const Value *src = othervp; src < srcend; src++, dst++) { - *dst = *src; - if (doPostBarrier) - HeapValue::writeBarrierPost(*dst, dst); - } - - *this = *otherfp; - argv_ = vp + 2; - unsetPushedSPSFrame(); - if (doPostBarrier) - writeBarrierPost(); - - srcend = othersp; - dst = slots(); - for (const Value *src = otherfp->slots(); src < srcend; src++, dst++) { - *dst = *src; - if (doPostBarrier) - HeapValue::writeBarrierPost(*dst, dst); - } -} - -/* Note: explicit instantiation for js_NewGenerator located in jsiter.cpp. */ -template -void InterpreterFrame::copyFrameAndValues( - JSContext *, Value *, InterpreterFrame *, const Value *, Value *); -template -void InterpreterFrame::copyFrameAndValues( - JSContext *, Value *, InterpreterFrame *, const Value *, Value *); - void InterpreterFrame::writeBarrierPost() { @@ -265,8 +224,6 @@ InterpreterFrame::prologue(JSContext *cx) void InterpreterFrame::epilogue(JSContext *cx) { - MOZ_ASSERT(!isYielding()); - RootedScript script(cx, this->script()); probes::ExitScript(cx, script, script->functionNonDelazifying(), hasPushedSPSFrame()); @@ -303,11 +260,12 @@ InterpreterFrame::epilogue(JSContext *cx) MOZ_ASSERT(isNonEvalFunctionFrame()); - if (fun()->isHeavyweight()) - MOZ_ASSERT_IF(hasCallObj(), + if (fun()->isHeavyweight()) { + MOZ_ASSERT_IF(hasCallObj() && !fun()->isGenerator(), scopeChain()->as().callee().nonLazyScript() == script); - else + } else { AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain()); + } if (MOZ_UNLIKELY(cx->compartment()->debugMode())) DebugScopes::onPopCall(this, cx); diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index e09ba468f75c..2dcc08621784 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -19,7 +19,6 @@ #endif struct JSCompartment; -struct JSGenerator; namespace js { @@ -188,7 +187,6 @@ class AbstractFramePtr inline bool hasCallObj() const; inline bool isGeneratorFrame() const; - inline bool isYielding() const; inline bool isFunctionFrame() const; inline bool isGlobalFrame() const; inline bool isEvalFrame() const; @@ -293,17 +291,7 @@ class InterpreterFrame GENERATOR = 0x10, /* frame is associated with a generator */ CONSTRUCTING = 0x20, /* frame is for a constructor invocation */ - /* - * Generator frame state - * - * YIELDING and SUSPENDED are similar, but there are differences. After - * a generator yields, SendToGenerator immediately clears the YIELDING - * flag, but the frame will still have the SUSPENDED flag. Also, when the - * generator returns but before it's GC'ed, YIELDING is not set but - * SUSPENDED is. - */ - YIELDING = 0x40, /* Interpret dispatched JSOP_YIELD */ - SUSPENDED = 0x80, /* Generator is not running. */ + /* (0x40 and 0x80 are unused) */ /* Function prologue state */ HAS_CALL_OBJ = 0x100, /* CallObject created for heavyweight fun */ @@ -801,29 +789,13 @@ class InterpreterFrame flags_ |= GENERATOR; } - Value *generatorArgsSnapshotBegin() const { - MOZ_ASSERT(isGeneratorFrame()); - return argv() - 2; + void resumeGeneratorFrame(HandleObject scopeChain) { + MOZ_ASSERT(!isGeneratorFrame()); + MOZ_ASSERT(isNonEvalFunctionFrame()); + flags_ |= GENERATOR | HAS_CALL_OBJ | HAS_SCOPECHAIN; + scopeChain_ = scopeChain; } - Value *generatorArgsSnapshotEnd() const { - MOZ_ASSERT(isGeneratorFrame()); - return argv() + js::Max(numActualArgs(), numFormalArgs()); - } - - Value *generatorSlotsSnapshotBegin() const { - MOZ_ASSERT(isGeneratorFrame()); - return (Value *)(this + 1); - } - - enum TriggerPostBarriers { - DoPostBarrier = true, - NoPostBarrier = false - }; - template - void copyFrameAndValues(JSContext *cx, Value *vp, InterpreterFrame *otherfp, - const Value *othervp, Value *othersp); - /* * Other flags */ @@ -884,33 +856,6 @@ class InterpreterFrame flags_ |= PREV_UP_TO_DATE; } - bool isYielding() { - return !!(flags_ & YIELDING); - } - - void setYielding() { - flags_ |= YIELDING; - } - - void clearYielding() { - flags_ &= ~YIELDING; - } - - bool isSuspended() const { - MOZ_ASSERT(isGeneratorFrame()); - return flags_ & SUSPENDED; - } - - void setSuspended() { - MOZ_ASSERT(isGeneratorFrame()); - flags_ |= SUSPENDED; - } - - void clearSuspended() { - MOZ_ASSERT(isGeneratorFrame()); - flags_ &= ~SUSPENDED; - } - public: void mark(JSTracer *trc); void markValues(JSTracer *trc, unsigned start, unsigned end); @@ -1052,6 +997,10 @@ class InterpreterStack void popInlineFrame(InterpreterRegs ®s); + bool resumeGeneratorCallFrame(JSContext *cx, InterpreterRegs ®s, + HandleFunction callee, HandleValue thisv, + HandleObject scopeChain); + inline void purge(JSRuntime *rt); size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { @@ -1236,6 +1185,9 @@ class InterpreterActivation : public Activation InitialFrameFlags initial); inline void popInlineFrame(InterpreterFrame *frame); + inline bool resumeGeneratorFrame(HandleFunction callee, HandleValue thisv, + HandleObject scopeChain); + InterpreterFrame *current() const { return regs_.fp(); } diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 0fe0e150ee64..a1671f760e99 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -28,7 +28,7 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 185); +static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 186); class XDRBuffer { public: From 29b2e5957fd9c8860b19d02081f3c5730d567660 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 17 Oct 2014 01:30:49 -0700 Subject: [PATCH 80/81] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/934b8c3014a3 Author: Timothy Guan-tin Chien Desc: Merge pull request #25058 from timdream/screen-wake-lock-manager Bug 1081630 - Implement WakeLockManagers, r=alive ======== https://hg.mozilla.org/integration/gaia-central/rev/3de3252d9d42 Author: Timothy Guan-tin Chien Desc: Bug 1081630 - Implement WakeLockManagers --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index f008d5d09639..9664093845cd 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "73ba4234231f12d55219bd2e932b6478a7ee5888", + "revision": "934b8c3014a3e20dd5d90ecf95f4b6b704dddb1e", "repo_path": "/integration/gaia-central" } From f4375ef57527a11b3e95f17d9aa41e2f40f09ef3 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 17 Oct 2014 01:32:35 -0700 Subject: [PATCH 81/81] Bumping manifests a=b2g-bump --- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/flame/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 6ae032e55929..b9d2bf969efb 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index a73170f6c6f1..d9197012c578 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 51ada69b5ca5..9a6c7d319bf9 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 4e29e87253ad..84684c1215e0 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index a73170f6c6f1..d9197012c578 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 8c0c154b89f4..d55ce8f7482e 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index c25256467244..52ca484c87ae 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 6f2bb41cc5c3..423b426b54a5 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index fc359b18b7d5..6eb79edcafc1 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 2e67fc939535..ae02f80fd648 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index fdc6d496755a..70ac3ec41131 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - +